mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-11-07 04:36:52 -10:00
tpm: Harvest entropy from TPM 1.2 devices
The TPM 2.0 driver periodically contributes TPM-generated random bytes to the kernel entropy pool, but the TPM 1.2 driver does not. Issue TPM_GetRandom every ten seconds and submit successful responses as RANDOM_PURE_TPM. Keep each exchange under the TPM transaction lock. Defer harvesting while the character device is open or a command is pending so the task cannot consume a user response. Drain the task for suspend and detach, rearming it after resume or a failed suspend. Document the expanded scope of RANDOM_ENABLE_TPM and update the kernel configuration comments that described it as TPM 2.0-only. Validated on a STMicroelectronics TPM 1.2 TIS device. DTrace observed periodic entropy submissions; a raw GetRandom response remained intact while the device was held open across a harvest interval; and module unload/reload deregistered and restored the entropy source cleanly. Tested by: zarychtam_plan-b.pwste.edu.pl MFC after: 2 weeks Sponsored by: BBOX.io Differential Revision: https://reviews.freebsd.org/D59239
This commit is contained in:
1 parent
f0ac0f73ae
commit
4bcd10f7c0
6 files changed
+216
-5
No files matched your search
@@ -13,7 +13,7 @@
|
||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd October 31, 2018
|
||||
.Dd August 27, 2026
|
||||
.Dt TPM 4
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -21,6 +21,7 @@
|
||||
.Nd Trusted Platform Module
|
||||
.Sh SYNOPSIS
|
||||
.Cd device tpm
|
||||
.Cd options RANDOM_ENABLE_TPM
|
||||
.Pp
|
||||
In
|
||||
.Xr device.hints 5 :
|
||||
@@ -43,6 +44,12 @@ store cryptographic keys.
|
||||
The driver can be configured to use an IRQ by providing a free ISA
|
||||
interrupt vector in
|
||||
.Pa /boot/device.hints .
|
||||
.Pp
|
||||
When the kernel is configured with
|
||||
.Cd "options RANDOM_ENABLE_TPM" ,
|
||||
the driver periodically obtains random bytes from the TPM and supplies them
|
||||
to
|
||||
.Xr random 4 .
|
||||
.Sh HARDWARE
|
||||
The
|
||||
.Nm
|
||||
@@ -66,6 +73,7 @@ Winbond WEC WPCT200
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr intro 4 ,
|
||||
.Xr random 4 ,
|
||||
.Xr device.hints 5 ,
|
||||
.Xr config 8
|
||||
.Pp
|
||||
|
||||
@@ -218,7 +218,7 @@ device smsc # SMSC LAN91C111
|
||||
|
||||
# random(4)
|
||||
device tpm # Trusted Platform Module
|
||||
options RANDOM_ENABLE_TPM # enable entropy from TPM 2.0
|
||||
options RANDOM_ENABLE_TPM # enable entropy from TPM devices
|
||||
options RANDOM_ENABLE_KBD
|
||||
options RANDOM_ENABLE_MOUSE
|
||||
|
||||
|
||||
@@ -111,6 +111,6 @@ device smbios
|
||||
|
||||
# random(4)
|
||||
device tpm # Trusted Platform Module
|
||||
options RANDOM_ENABLE_TPM # enable entropy from TPM 2.0
|
||||
options RANDOM_ENABLE_TPM # enable entropy from TPM devices
|
||||
options RANDOM_ENABLE_KBD
|
||||
options RANDOM_ENABLE_MOUSE
|
||||
+198
-1
@@ -24,6 +24,7 @@
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/random.h>
|
||||
|
||||
#include <sys/module.h>
|
||||
#include <sys/conf.h>
|
||||
@@ -43,8 +44,20 @@
|
||||
|
||||
#include "tpm_if.h"
|
||||
|
||||
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
|
||||
#include <dev/random/randomdev.h>
|
||||
#include <dev/random/random_harvestq.h>
|
||||
#endif
|
||||
|
||||
MALLOC_DEFINE(M_TPM, "tpm12_buffer", "buffer for TPM 1.2 responses");
|
||||
|
||||
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
|
||||
static const struct random_source random_tpm12 = {
|
||||
.rs_ident = "TPM",
|
||||
.rs_source = RANDOM_PURE_TPM,
|
||||
};
|
||||
#endif
|
||||
|
||||
#define TPM_BUFSIZ 1024
|
||||
|
||||
#define TPM_HDRSIZE 10
|
||||
@@ -54,11 +67,15 @@ MALLOC_DEFINE(M_TPM, "tpm12_buffer", "buffer for TPM 1.2 responses");
|
||||
#define TPM_TAG_RQU_COMMAND 0x00c1
|
||||
#define TPM_TAG_RSP_COMMAND 0x00c4
|
||||
#define TPM_ORD_SAVESTATE 0x00000098
|
||||
#define TPM_ORD_GETRANDOM 0x00000046
|
||||
#define TPM_WARN_RETRY 0x00000800
|
||||
|
||||
#define TPM_SAVESTATE_RETRIES 50
|
||||
#define TPM_SAVESTATE_RETRY_MS 100
|
||||
|
||||
#define TPM_HARVEST_SIZE 16
|
||||
#define TPM_HARVEST_INTERVAL 10
|
||||
|
||||
#define TPM_ACCESS 0x0000 /* access register */
|
||||
#define TPM_ACCESS_ESTABLISHMENT 0x01 /* establishment */
|
||||
#define TPM_ACCESS_REQUEST_USE 0x02 /* request using locality */
|
||||
@@ -196,6 +213,11 @@ int tpm_legacy_end(struct tpm_softc *, int, int);
|
||||
static int tpm_attach_finish(struct tpm_softc *);
|
||||
static void tpm_delayed_attach(void *);
|
||||
static int tpm_transmit_header(struct tpm_softc *, uint32_t, uint32_t *);
|
||||
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
|
||||
static int tpm_get_random(struct tpm_softc *, uint8_t *, size_t *);
|
||||
static void tpm_harvest(void *, int);
|
||||
static void tpm_harvest_enqueue(const uint8_t *, size_t);
|
||||
#endif
|
||||
static void tpm_tis12_abort(struct tpm_softc *);
|
||||
static int tpm_tis12_devid_index(uint32_t);
|
||||
static int tpm_tis12_quiesce(struct tpm_softc *);
|
||||
@@ -254,6 +276,9 @@ tpm_attach(device_t dev)
|
||||
sc->sc_intr_received = false;
|
||||
sc->sc_irq_config.valid = false;
|
||||
TASK_INIT(&sc->sc_intr_task, 0, tpm_tis12_intr_task, sc);
|
||||
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
|
||||
sc->sc_harvest_registered = false;
|
||||
#endif
|
||||
|
||||
sc->mem_rid = 0;
|
||||
sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
|
||||
@@ -366,7 +391,19 @@ tpm_attach_finish(struct tpm_softc *sc)
|
||||
args.mda_gid = GID_WHEEL;
|
||||
args.mda_mode = 0600;
|
||||
args.mda_si_drv1 = sc;
|
||||
return (make_dev_s(&args, &sc->sc_cdev, "tpm"));
|
||||
error = make_dev_s(&args, &sc->sc_cdev, "tpm");
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
|
||||
TIMEOUT_TASK_INIT(taskqueue_thread, &sc->sc_harvest_task, 0,
|
||||
tpm_harvest, sc);
|
||||
random_source_register(&random_tpm12);
|
||||
sc->sc_harvest_registered = true;
|
||||
taskqueue_enqueue_timeout(taskqueue_thread, &sc->sc_harvest_task, 0);
|
||||
#endif
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -401,6 +438,15 @@ tpm_detach(device_t dev)
|
||||
sx_xunlock(&sc->sc_lock);
|
||||
taskqueue_drain(taskqueue_thread, &sc->sc_intr_task);
|
||||
|
||||
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
|
||||
if (sc->sc_harvest_registered) {
|
||||
taskqueue_drain_timeout(taskqueue_thread,
|
||||
&sc->sc_harvest_task);
|
||||
random_source_deregister(&random_tpm12);
|
||||
sc->sc_harvest_registered = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Prevent new methods from touching the transport. Do not hold the
|
||||
* lock while destroy_dev() drains methods and cdevpriv destructors
|
||||
@@ -1039,6 +1085,142 @@ tpm_transmit_header(struct tpm_softc *sc, uint32_t ordinal, uint32_t *tpm_rc)
|
||||
return (0);
|
||||
}
|
||||
|
||||
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
|
||||
/*
|
||||
* Fetch random bytes as one command/response transaction. The caller owns
|
||||
* sc_lock so a character-device request cannot be interposed between the
|
||||
* write and read phases.
|
||||
*/
|
||||
static int
|
||||
tpm_get_random(struct tpm_softc *sc, uint8_t *entropy, size_t *entropy_size)
|
||||
{
|
||||
uint8_t command[TPM_HDRSIZE + sizeof(uint32_t)];
|
||||
uint8_t response[TPM_HDRSIZE + sizeof(uint32_t) + TPM_HARVEST_SIZE];
|
||||
size_t count, response_size;
|
||||
uint32_t random_size, tpm_rc;
|
||||
int end_error, error;
|
||||
|
||||
sx_assert(&sc->sc_lock, SA_XLOCKED);
|
||||
*entropy_size = 0;
|
||||
random_size = 0;
|
||||
|
||||
be16enc(command, TPM_TAG_RQU_COMMAND);
|
||||
be32enc(command + 2, sizeof(command));
|
||||
be32enc(command + 6, TPM_ORD_GETRANDOM);
|
||||
be32enc(command + TPM_HDRSIZE, TPM_HARVEST_SIZE);
|
||||
|
||||
error = sc->sc_start(sc, UIO_WRITE);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
error = sc->sc_write(sc, command, sizeof(command));
|
||||
end_error = sc->sc_end(sc, UIO_WRITE, error);
|
||||
if (error == 0)
|
||||
error = end_error;
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
error = sc->sc_start(sc, UIO_READ);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
count = 0;
|
||||
error = sc->sc_read(sc, response, TPM_HDRSIZE, &count,
|
||||
TPM_PARAM_SIZE);
|
||||
if (error != 0)
|
||||
goto end;
|
||||
if (count != TPM_HDRSIZE ||
|
||||
be16dec(response) != TPM_TAG_RSP_COMMAND) {
|
||||
error = EPROTO;
|
||||
goto end;
|
||||
}
|
||||
|
||||
response_size = be32dec(response + 2);
|
||||
if (response_size < TPM_HDRSIZE || response_size > sizeof(response)) {
|
||||
error = EPROTO;
|
||||
goto end;
|
||||
}
|
||||
if (response_size > TPM_HDRSIZE) {
|
||||
count = 0;
|
||||
error = sc->sc_read(sc, response + TPM_HDRSIZE,
|
||||
(int)(response_size - TPM_HDRSIZE), &count,
|
||||
TPM_PARAM_SIZE);
|
||||
if (error != 0)
|
||||
goto end;
|
||||
if (count != response_size - TPM_HDRSIZE) {
|
||||
error = EIO;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
tpm_rc = be32dec(response + 6);
|
||||
if (tpm_rc != 0) {
|
||||
error = EIO;
|
||||
goto end;
|
||||
}
|
||||
if (response_size < TPM_HDRSIZE + sizeof(uint32_t)) {
|
||||
error = EPROTO;
|
||||
goto end;
|
||||
}
|
||||
random_size = be32dec(response + TPM_HDRSIZE);
|
||||
if (random_size > response_size - TPM_HDRSIZE - sizeof(uint32_t)) {
|
||||
error = EPROTO;
|
||||
goto end;
|
||||
}
|
||||
random_size = MIN(random_size, TPM_HARVEST_SIZE);
|
||||
|
||||
end:
|
||||
end_error = sc->sc_end(sc, UIO_READ, error);
|
||||
if (error == 0)
|
||||
error = end_error;
|
||||
if (error == 0) {
|
||||
memcpy(entropy, response + TPM_HDRSIZE + sizeof(uint32_t),
|
||||
random_size);
|
||||
*entropy_size = random_size;
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
static void
|
||||
tpm_harvest_enqueue(const uint8_t *entropy, size_t entropy_size)
|
||||
{
|
||||
size_t chunk_size;
|
||||
|
||||
/* Preserve all RNG bytes rather than squashing an oversized event. */
|
||||
chunk_size = sizeof(((struct harvest_event *)0)->he_entropy);
|
||||
while (entropy_size != 0) {
|
||||
size_t size;
|
||||
|
||||
size = MIN(entropy_size, chunk_size);
|
||||
random_harvest_queue(entropy, (u_int)size, RANDOM_PURE_TPM);
|
||||
entropy += size;
|
||||
entropy_size -= size;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tpm_harvest(void *arg, int pending)
|
||||
{
|
||||
struct tpm_softc *sc;
|
||||
uint8_t entropy[TPM_HARVEST_SIZE];
|
||||
size_t entropy_size;
|
||||
|
||||
sc = arg;
|
||||
entropy_size = 0;
|
||||
sx_xlock(&sc->sc_lock);
|
||||
if (sc->sc_dying || sc->sc_suspend != 0)
|
||||
goto out;
|
||||
|
||||
/* Do not interpose between a user's command and response. */
|
||||
if ((sc->sc_flags & TPM_OPEN) == 0 && !sc->sc_command_pending)
|
||||
(void)tpm_get_random(sc, entropy, &entropy_size);
|
||||
taskqueue_enqueue_timeout(taskqueue_thread, &sc->sc_harvest_task,
|
||||
hz * TPM_HARVEST_INTERVAL);
|
||||
out:
|
||||
sx_xunlock(&sc->sc_lock);
|
||||
if (entropy_size != 0)
|
||||
tpm_harvest_enqueue(entropy, entropy_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Save TPM state on suspend. */
|
||||
int
|
||||
tpm_suspend(device_t dev)
|
||||
@@ -1053,6 +1235,11 @@ tpm_suspend(device_t dev)
|
||||
* invalidate that saved state, so retry SaveState before entering S3.
|
||||
*/
|
||||
sc = device_get_softc(dev);
|
||||
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
|
||||
if (sc->sc_harvest_registered)
|
||||
taskqueue_drain_timeout(taskqueue_thread,
|
||||
&sc->sc_harvest_task);
|
||||
#endif
|
||||
sx_xlock(&sc->sc_lock);
|
||||
if (sc->sc_dying) {
|
||||
error = ENXIO;
|
||||
@@ -1088,6 +1275,11 @@ tpm_suspend(device_t dev)
|
||||
|
||||
out:
|
||||
sx_xunlock(&sc->sc_lock);
|
||||
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
|
||||
if (error != 0 && sc->sc_harvest_registered)
|
||||
taskqueue_enqueue_timeout(taskqueue_thread,
|
||||
&sc->sc_harvest_task, hz * TPM_HARVEST_INTERVAL);
|
||||
#endif
|
||||
return (error);
|
||||
}
|
||||
|
||||
@@ -1117,6 +1309,11 @@ tpm_resume(device_t dev)
|
||||
|
||||
out:
|
||||
sx_xunlock(&sc->sc_lock);
|
||||
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
|
||||
if (error == 0 && sc->sc_harvest_registered)
|
||||
taskqueue_enqueue_timeout(taskqueue_thread,
|
||||
&sc->sc_harvest_task, hz * TPM_HARVEST_INTERVAL);
|
||||
#endif
|
||||
return (error);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#define _TPMVAR_H
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include "opt_tpm.h"
|
||||
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
@@ -77,6 +79,10 @@ struct tpm_softc {
|
||||
bool sc_dying;
|
||||
bool sc_locality;
|
||||
bool sc_command_pending;
|
||||
#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM
|
||||
struct timeout_task sc_harvest_task;
|
||||
bool sc_harvest_registered;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef __FreeBSD__
|
||||
|
||||
@@ -218,7 +218,7 @@ device iicoc # OpenCores I2C controller support
|
||||
|
||||
# random(4)
|
||||
device tpm # Trusted Platform Module
|
||||
options RANDOM_ENABLE_TPM # enable entropy from TPM 2.0
|
||||
options RANDOM_ENABLE_TPM # enable entropy from TPM devices
|
||||
|
||||
# Include SoC specific configuration
|
||||
include "std.allwinner"
|
||||
|
||||
Reference in new issue
Block a user