Message ID | 20240213171334.30479-13-James.Bottomley@HansenPartnership.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | add integrity and security to TPM2 transactions | expand |
On Tue Feb 13, 2024 at 7:13 PM EET, James Bottomley wrote: > The session handling code uses a "salted" session, meaning a session > whose salt is encrypted to the public part of another TPM key so an > observer cannot obtain it (and thus deduce the session keys). This > patch creates and context saves in the tpm_chip area the primary key > of the NULL hierarchy for this purpose. > > Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> > > --- > v6: split out of original HMAC patch update config name > v7: rename null seed parameters > --- > drivers/char/tpm/Kconfig | 11 ++ > drivers/char/tpm/Makefile | 1 + > drivers/char/tpm/tpm.h | 10 ++ > drivers/char/tpm/tpm2-cmd.c | 5 + > drivers/char/tpm/tpm2-sessions.c | 276 +++++++++++++++++++++++++++++++ > include/linux/tpm.h | 49 ++++++ > 6 files changed, 352 insertions(+) > create mode 100644 drivers/char/tpm/tpm2-sessions.c > > diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig > index 927088b2c3d3..e3c39a83171b 100644 > --- a/drivers/char/tpm/Kconfig > +++ b/drivers/char/tpm/Kconfig > @@ -27,6 +27,17 @@ menuconfig TCG_TPM > > if TCG_TPM > > +config TCG_TPM2_HMAC > + bool "Use encrypted and HMACd transactions on the TPM bus" How about simply "Use HMAC-encrypted transactions" The details are anyway in the description. > + default y > + help > + Setting this causes us to deploy a scheme which uses request ~~~ extra spaces > + and response HMACs in addition to encryption for > + communicating with the TPM to prevent or detect bus snooping > + and interposer attacks (see tpm-security.rst). Saying Y > + here adds some encryption overhead to all kernel to TPM > + transactions. > + > config HW_RANDOM_TPM > bool "TPM HW Random Number Generator support" > depends on TCG_TPM && HW_RANDOM && !(TCG_TPM=y && HW_RANDOM=m) > diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile > index ad3594e383e1..4c695b0388f3 100644 > --- a/drivers/char/tpm/Makefile > +++ b/drivers/char/tpm/Makefile > @@ -17,6 +17,7 @@ tpm-y += eventlog/tpm1.o > tpm-y += eventlog/tpm2.o > tpm-y += tpm-buf.o > > +tpm-$(CONFIG_TCG_TPM2_HMAC) += tpm2-sessions.o > tpm-$(CONFIG_ACPI) += tpm_ppi.o eventlog/acpi.o > tpm-$(CONFIG_EFI) += eventlog/efi.o > tpm-$(CONFIG_OF) += eventlog/of.o > diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h > index cbc9d1e2974d..6b8b9956ba69 100644 > --- a/drivers/char/tpm/tpm.h > +++ b/drivers/char/tpm/tpm.h > @@ -321,4 +321,14 @@ void tpm_bios_log_setup(struct tpm_chip *chip); > void tpm_bios_log_teardown(struct tpm_chip *chip); > int tpm_dev_common_init(void); > void tpm_dev_common_exit(void); > + > +#ifdef CONFIG_TCG_TPM2_HMAC > +int tpm2_sessions_init(struct tpm_chip *chip); I'm sorry but "sessions" and "init" are the worst possible terminology I could every pick when trying to make a function that self-documents itself :-) I'd like to see here well-scoped name so that it is easy to connect this to its purpose when reviewing some other patches. > +#else > +static inline int tpm2_sessions_init(struct tpm_chip *chip) > +{ > + return 0; > +} > +#endif > + > #endif > diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c > index 93545be190a5..b0e72fb563d9 100644 > --- a/drivers/char/tpm/tpm2-cmd.c > +++ b/drivers/char/tpm/tpm2-cmd.c > @@ -759,6 +759,11 @@ int tpm2_auto_startup(struct tpm_chip *chip) > rc = 0; > } > > + if (rc) > + goto out; > + > + rc = tpm2_sessions_init(chip); > + > out: > /* > * Infineon TPM in field upgrade mode will return no data for the number > diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c > new file mode 100644 > index 000000000000..9fc263ee02c2 > --- /dev/null > +++ b/drivers/char/tpm/tpm2-sessions.c > @@ -0,0 +1,276 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +/* > + * Copyright (C) 2018 James.Bottomley@HansenPartnership.com > + * useless extra line in the commment > + */ > + > +#include "tpm.h" > + spurious newline > +#include <asm/unaligned.h> spurious newline > + > +#include <crypto/aes.h> spurious newline > + > +/* if you change to AES256, you only need change this */ > +#define AES_KEYBYTES AES_KEYSIZE_128 AES_KEY_BYTES. Also the comment is talking about changing something without documenting what it is. Even not having the comment at all would be less confusing. > + spurious newline > +#define AES_KEYBITS (AES_KEYBYTES*8) AES_KEY_BITS Also, documentation missing. > + Documentation missing for "tpm2_parse_create_primary". It is a complex function despite being not exported so definitely needs documentation. > +static int tpm2_parse_create_primary(struct tpm_chip *chip, struct tpm_buf *buf, > + u32 *nullkey) ~~~~~~~ null_key > +{ > + struct tpm_header *head = (struct tpm_header *)buf->data; > + off_t offset_r = TPM_HEADER_SIZE, offset_t; > + u16 len = TPM_HEADER_SIZE; > + u32 tot_len = be32_to_cpu(head->length); no good reason to use more confusing namme "tot_len", instead of more readable "total_len". > + u32 val, parm_len; > + > + *nullkey = tpm_buf_read_u32(buf, &offset_r); > + parm_len = tpm_buf_read_u32(buf, &offset_r); ~~~~~~~~ param_len > + /* > + * parm_len doesn't include the header, but all the other > + * lengths and offsets do, so add it to parm len to make > + * the comparisons easier > + */ > + parm_len += TPM_HEADER_SIZE; > + > + if (parm_len + 8 > tot_len) > + return -EINVAL; > + len = tpm_buf_read_u16(buf, &offset_r); > + offset_t = offset_r; > + /* now we have the public area, compute the name of the object */ > + put_unaligned_be16(TPM_ALG_SHA256, chip->null_key_name); > + sha256(&buf->data[offset_r], len, chip->null_key_name + 2); > + > + /* validate the public key */ > + val = tpm_buf_read_u16(buf, &offset_t); > + /* key type (must be what we asked for) */ > + if (val != TPM_ALG_ECC) > + return -EINVAL; > + val = tpm_buf_read_u16(buf, &offset_t); > + /* name algorithm */ > + if (val != TPM_ALG_SHA256) > + return -EINVAL; > + val = tpm_buf_read_u32(buf, &offset_t); > + /* object properties */ > + if (val != (TPM2_OA_NO_DA | > + TPM2_OA_FIXED_TPM | > + TPM2_OA_FIXED_PARENT | > + TPM2_OA_SENSITIVE_DATA_ORIGIN | > + TPM2_OA_USER_WITH_AUTH | > + TPM2_OA_DECRYPT | > + TPM2_OA_RESTRICTED)) Please make define a mask constant for these bits and use it here instead. There's just too many and make the code really unredable. I'd even suggesting documenting that constant with some rationale for the mask. Here the logic is obviously to fix the primary key to the exact chip and make unmovable (cannot be migrated), isn't it? That would perfectly sufficient documentation for the constant, or along the lines. > + return -EINVAL; > + /* auth policy (empty) */ > + val = tpm_buf_read_u16(buf, &offset_t); > + if (val != 0) > + return -EINVAL; I'd add empty line betwen each of these checks to make it more readable. > + /* symmetric key parameters */ > + val = tpm_buf_read_u16(buf, &offset_t); > + if (val != TPM_ALG_AES) > + return -EINVAL; > + /* symmetric key length */ > + val = tpm_buf_read_u16(buf, &offset_t); > + if (val != AES_KEYBITS) > + return -EINVAL; > + /* symmetric encryption scheme */ > + val = tpm_buf_read_u16(buf, &offset_t); > + if (val != TPM_ALG_CFB) > + return -EINVAL; > + /* signing scheme */ > + val = tpm_buf_read_u16(buf, &offset_t); > + if (val != TPM_ALG_NULL) > + return -EINVAL; > + /* ECC Curve */ > + val = tpm_buf_read_u16(buf, &offset_t); > + if (val != TPM2_ECC_NIST_P256) > + return -EINVAL; > + /* KDF Scheme */ > + val = tpm_buf_read_u16(buf, &offset_t); > + if (val != TPM_ALG_NULL) > + return -EINVAL; > + /* x point */ > + val = tpm_buf_read_u16(buf, &offset_t); > + if (val != EC_PT_SZ) > + return -EINVAL; > + memcpy(chip->null_ec_key_x, &buf->data[offset_t], val); > + offset_t += val; > + val = tpm_buf_read_u16(buf, &offset_t); > + if (val != EC_PT_SZ) > + return -EINVAL; > + memcpy(chip->null_ec_key_y, &buf->data[offset_t], val); > + offset_t += val; > + > + /* original length of the whole TPM2B */ > + offset_r += len; > + > + /* should have exactly consumed the TPM2B public structure */ > + if (offset_t != offset_r) > + return -EINVAL; > + if (offset_r > parm_len) > + return -EINVAL; > + /* creation data (skip) */ > + len = tpm_buf_read_u16(buf, &offset_r); > + offset_r += len; > + if (offset_r > parm_len) > + return -EINVAL; > + /* creation digest (must be sha256) */ > + len = tpm_buf_read_u16(buf, &offset_r); > + offset_r += len; > + if (len != SHA256_DIGEST_SIZE || offset_r > parm_len) > + return -EINVAL; > + /* TPMT_TK_CREATION follows */ > + /* tag, must be TPM_ST_CREATION (0x8021) */ > + val = tpm_buf_read_u16(buf, &offset_r); > + if (val != TPM2_ST_CREATION || offset_r > parm_len) > + return -EINVAL; > + /* hierarchy (must be NULL) */ > + val = tpm_buf_read_u32(buf, &offset_r); > + if (val != TPM2_RH_NULL || offset_r > parm_len) > + return -EINVAL; > + /* the ticket digest HMAC (might not be sha256) */ > + len = tpm_buf_read_u16(buf, &offset_r); > + offset_r += len; > + if (offset_r > parm_len) > + return -EINVAL; > + /* > + * finally we have the name, which is a sha256 digest plus a 2 > + * byte algorithm type > + */ > + len = tpm_buf_read_u16(buf, &offset_r); > + if (offset_r + len != parm_len + 8) > + return -EINVAL; > + if (len != SHA256_DIGEST_SIZE + 2) > + return -EINVAL; > + > + if (memcmp(chip->null_key_name, &buf->data[offset_r], > + SHA256_DIGEST_SIZE + 2) != 0) { > + dev_err(&chip->dev, "NULL Seed name comparison failed\n"); > + return -EINVAL; > + } > + > + return 0; > +} > documentation missing: > +static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy, u32 *handle) > +{ > + int rc; > + struct tpm_buf buf; > + struct tpm_buf template; > + > + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE_PRIMARY); > + if (rc) > + return rc; > + > + rc = tpm_buf_init_sized(&template); > + if (rc) { > + tpm_buf_destroy(&buf); > + return rc; > + } > + > + /* > + * create the template. Note: in order for userspace to > + * verify the security of the system, it will have to create > + * and certify this NULL primary, meaning all the template > + * parameters will have to be identical, so conform exactly to > + * the TCG TPM v2.0 Provisioning Guidance for the SRK ECC > + * key > + */ > + > + /* key type */ > + tpm_buf_append_u16(&template, TPM_ALG_ECC); > + /* name algorithm */ > + tpm_buf_append_u16(&template, TPM_ALG_SHA256); > + /* object properties */ > + tpm_buf_append_u32(&template, TPM2_OA_NO_DA | > + TPM2_OA_FIXED_TPM | > + TPM2_OA_FIXED_PARENT | > + TPM2_OA_SENSITIVE_DATA_ORIGIN | > + TPM2_OA_USER_WITH_AUTH | > + TPM2_OA_DECRYPT | > + TPM2_OA_RESTRICTED); > + /* sauth policy (empty) */ > + tpm_buf_append_u16(&template, 0); > + > + /* BEGIN parameters: key specific; for ECC*/ > + /* symmetric algorithm */ > + tpm_buf_append_u16(&template, TPM_ALG_AES); > + /* bits for symmetric algorithm */ > + tpm_buf_append_u16(&template, 128); > + /* algorithm mode (must be CFB) */ > + tpm_buf_append_u16(&template, TPM_ALG_CFB); > + /* scheme (NULL means any scheme) */ > + tpm_buf_append_u16(&template, TPM_ALG_NULL); > + /* ECC Curve ID */ > + tpm_buf_append_u16(&template, TPM2_ECC_NIST_P256); > + /* KDF Scheme */ > + tpm_buf_append_u16(&template, TPM_ALG_NULL); > + /* unique: key specific; for ECC it is two points */ > + tpm_buf_append_u16(&template, 0); > + tpm_buf_append_u16(&template, 0); > + /* END parameters */ > + > + /* primary handle */ > + tpm_buf_append_u32(&buf, hierarchy); > + tpm_buf_append_empty_auth(&buf, TPM2_RS_PW); > + /* sensitive create size is 4 for two empty buffers */ > + tpm_buf_append_u16(&buf, 4); > + /* sensitive create auth data (empty) */ > + tpm_buf_append_u16(&buf, 0); > + /* sensitive create sensitive data (empty) */ > + tpm_buf_append_u16(&buf, 0); > + /* the public template */ > + tpm_buf_append(&buf, template.data, template.length); > + tpm_buf_destroy(&template); > + /* outside info (empty) */ > + tpm_buf_append_u16(&buf, 0); > + /* creation PCR (none) */ > + tpm_buf_append_u32(&buf, 0); > + > + rc = tpm_transmit_cmd(chip, &buf, 0, > + "attempting to create NULL primary"); > + > + if (rc == TPM2_RC_SUCCESS) > + rc = tpm2_parse_create_primary(chip, &buf, handle); > + > + tpm_buf_destroy(&buf); > + > + return rc; > +} > + > +static int tpm2_create_null_primary(struct tpm_chip *chip) > +{ > + u32 nullkey; ~~~~~~~ null_key > + int rc; > + > + rc = tpm2_create_primary(chip, TPM2_RH_NULL, &nullkey); > + > + if (rc == TPM2_RC_SUCCESS) { > + unsigned int offset = 0; /* dummy offset for null key context */ > + > + rc = tpm2_save_context(chip, nullkey, chip->null_key_context, > + sizeof(chip->null_key_context), &offset); > + tpm2_flush_context(chip, nullkey); > + } > + > + return rc; > +} > + > +/** > + * tpm2_sessions_init() - start of day initialization for the sessions code > + * @chip: TPM chip > + * > + * Derive and context save the null primary and allocate memory in the > + * struct tpm_chip for the authorizations. isn't this exactly for HMAC authorization at least in the patch set scope? plural confuses me here. > + */ > +int tpm2_sessions_init(struct tpm_chip *chip) > +{ > + int rc; > + > + rc = tpm2_create_null_primary(chip); > + if (rc) > + dev_err(&chip->dev, "TPM: security failed (NULL seed derivation): %d\n", rc); > + > + return rc; > +} > +EXPORT_SYMBOL(tpm2_sessions_init); > diff --git a/include/linux/tpm.h b/include/linux/tpm.h > index 6be263509e81..3060ab794afb 100644 > --- a/include/linux/tpm.h > +++ b/include/linux/tpm.h > @@ -35,12 +35,15 @@ struct trusted_key_options; > enum tpm_algorithms { > TPM_ALG_ERROR = 0x0000, > TPM_ALG_SHA1 = 0x0004, > + TPM_ALG_AES = 0x0006, > TPM_ALG_KEYEDHASH = 0x0008, > TPM_ALG_SHA256 = 0x000B, > TPM_ALG_SHA384 = 0x000C, > TPM_ALG_SHA512 = 0x000D, > TPM_ALG_NULL = 0x0010, > TPM_ALG_SM3_256 = 0x0012, > + TPM_ALG_ECC = 0x0023, > + TPM_ALG_CFB = 0x0043, > }; > > /* > @@ -49,6 +52,11 @@ enum tpm_algorithms { > */ > #define TPM_MAX_HASHES 5 > > +enum tpm2_curves { > + TPM2_ECC_NONE = 0x0000, > + TPM2_ECC_NIST_P256 = 0x0003, > +}; > + > struct tpm_digest { > u16 alg_id; > u8 digest[TPM_MAX_DIGEST_SIZE]; > @@ -116,6 +124,20 @@ struct tpm_chip_seqops { > const struct seq_operations *seqops; > }; > > +/* fixed define for the curve we use which is NIST_P256 */ > +#define EC_PT_SZ 32 > + > +/* > + * fixed define for the size of a name. This is actually HASHALG size > + * plus 2, so 32 for SHA256 > + */ > +#define TPM2_NAME_SIZE 34 > + > +/* > + * The maximum size for an object context > + */ > +#define TPM2_MAX_CONTEXT_SIZE 4096 > + > struct tpm_chip { > struct device dev; > struct device devs; > @@ -170,6 +192,14 @@ struct tpm_chip { > > /* active locality */ > int locality; > + > +#ifdef CONFIG_TCG_TPM2_HMAC > + /* details for communication security via sessions */ > + u8 null_key_context[TPM2_MAX_CONTEXT_SIZE]; /* context for NULL seed */ /* Saved context of the null seed: */ I like the description on top because they suffer less alignment issues and can have more verbose explanation. "context for NULL seed" is good as no comment at all./ > + u8 null_key_name[TPM2_NAME_SIZE]; /* name of NULL seed */ /* Digest of the public area: */ This is not totally accurate but way more documenting than the current description, which is tautology, i.e. you are saying that "name is name" in the comment leaving the reader questioning what the heck is name. Not totally accurate i.e. missing concatenated alg identifier but good enough... > + u8 null_ec_key_x[EC_PT_SZ]; > + u8 null_ec_key_y[EC_PT_SZ]; > +#endif this is now very nice clean and udnerstandable: I'd prefer tho this formatting: /* Name of the NULL seed: */ u8 null_key_name[TPM2_NAME_SIZE]; > }; > > #define TPM_HEADER_SIZE 10 > @@ -194,6 +224,7 @@ enum tpm2_timeouts { > enum tpm2_structures { > TPM2_ST_NO_SESSIONS = 0x8001, > TPM2_ST_SESSIONS = 0x8002, > + TPM2_ST_CREATION = 0x8021, > }; > > /* Indicates from what layer of the software stack the error comes from */ > @@ -243,6 +274,7 @@ enum tpm2_command_codes { > }; > > enum tpm2_permanent_handles { > + TPM2_RH_NULL = 0x40000007, > TPM2_RS_PW = 0x40000009, > }; > > @@ -318,7 +350,11 @@ struct tpm_buf { > enum tpm2_object_attributes { > TPM2_OA_FIXED_TPM = BIT(1), > TPM2_OA_FIXED_PARENT = BIT(4), > + TPM2_OA_SENSITIVE_DATA_ORIGIN = BIT(5), > TPM2_OA_USER_WITH_AUTH = BIT(6), > + TPM2_OA_NO_DA = BIT(10), > + TPM2_OA_RESTRICTED = BIT(16), > + TPM2_OA_DECRYPT = BIT(17), > }; > Here would be a great place to declarate aforementioned mask. > enum tpm2_session_attributes { > @@ -373,6 +409,15 @@ extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, > extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max); > extern struct tpm_chip *tpm_default_chip(void); > void tpm2_flush_context(struct tpm_chip *chip, u32 handle); please add empty line here. > +static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle) > +{ > + /* simple authorization for empty auth */ > + tpm_buf_append_u32(buf, 9); /* total length of auth */ > + tpm_buf_append_u32(buf, handle); > + tpm_buf_append_u16(buf, 0); /* nonce len */ > + tpm_buf_append_u8(buf, 0); /* attributes */ > + tpm_buf_append_u16(buf, 0); /* hmac len */ > +} > #else > static inline int tpm_is_tpm2(struct tpm_chip *chip) > { > @@ -399,5 +444,9 @@ static inline struct tpm_chip *tpm_default_chip(void) > { > return NULL; > } > + > +static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle) > +{ > +} > #endif > #endif I skipped first eleven patches because they are completed. This patch will be finished once all the numerous style issues have been fixed. Approach is totally correct... BR, Jarkko
On Tue, 2024-02-13 at 12:13 -0500, James Bottomley wrote: > + /* unique: key specific; for ECC it is two points */ > + tpm_buf_append_u16(&template, 0); > + tpm_buf_append_u16(&template, 0); Shouldn't the points be 32 bytes each in size, filled with zeros? The TCP TPM 2.0 Provisioning Guidance defines the SRK Template as a diff on top of Template L-2 (for ECC keys) as defined in the EK Credential Profile 2.0 document. Template L-2 calls for the X and Y points to be of 32 bytes each, filled with zeros. The Provisioning Guidance does not call for zero- sized points. For example, let's create an ECC Endorsement Key using the standard template then print its name: tpm2_createek -G ecc -c /dev/null -u ./ek.pub tpm2_loadexternal -c n -u ./ek.pub Equivalently using tpm2_createprimary: perl -e 'print "\0"x64' | tpm2_createprimary -C e -o ./ek.pub -G ecc -a 'fixedtpm|fixedparent|sensitivedataorigin|adminwithpolicy|restricted|decrypt' -L 837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa -u - tpm2_loadexternal -c n -u ./ek.pub You'll find that the key's public modulus matches that of the EK Certificate imprinted by the manufacturer, indicating we got the template correct. To generate a standard SRK key, the TCG TPM2 Provisioning Guidance states we should: 1. set userWithAuth, 2. clear adminWithPolicy 3. set noDA, and 4. clear the authorization policy. There's no mention of alterations to the unique field. Let's also create the key in the null hierarchy: perl -e 'print "\0"x64' | tpm2_createprimary -C n -o ./null.pub -G ecc -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricted|decrypt' -u - tpm2_loadexternal -c n -u ./null.pub The name does not match the kernel's name for the null key.
On Sat Mar 30, 2024 at 8:48 PM EET, Gabríel Arthúr Pétursson wrote: > On Tue, 2024-02-13 at 12:13 -0500, James Bottomley wrote: > > + /* unique: key specific; for ECC it is two points */ > > + tpm_buf_append_u16(&template, 0); > > + tpm_buf_append_u16(&template, 0); > > Shouldn't the points be 32 bytes each in size, filled with zeros? > > The TCP TPM 2.0 Provisioning Guidance defines the SRK Template as a > diff on top of Template L-2 (for ECC keys) as defined in the EK > Credential Profile 2.0 document. > > Template L-2 calls for the X and Y points to be of 32 bytes each, > filled with zeros. The Provisioning Guidance does not call for zero- > sized points. > > For example, let's create an ECC Endorsement Key using the standard > template then print its name: > > tpm2_createek -G ecc -c /dev/null -u ./ek.pub > tpm2_loadexternal -c n -u ./ek.pub > > Equivalently using tpm2_createprimary: > > perl -e 'print "\0"x64' | tpm2_createprimary -C e -o ./ek.pub -G ecc -a 'fixedtpm|fixedparent|sensitivedataorigin|adminwithpolicy|restricted|decrypt' -L 837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa -u - > tpm2_loadexternal -c n -u ./ek.pub > > You'll find that the key's public modulus matches that of the EK > Certificate imprinted by the manufacturer, indicating we got the > template correct. > > To generate a standard SRK key, the TCG TPM2 Provisioning Guidance > states we should: > > 1. set userWithAuth, > 2. clear adminWithPolicy > 3. set noDA, and > 4. clear the authorization policy. > > There's no mention of alterations to the unique field. > > Let's also create the key in the null hierarchy: > > perl -e 'print "\0"x64' | tpm2_createprimary -C n -o ./null.pub -G ecc -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricted|decrypt' -u - > tpm2_loadexternal -c n -u ./null.pub > > The name does not match the kernel's name for the null key. Null key is not provisioned, what is the motivation here? Not saying no, just asking for details... There's couple of things that lack in this patch set ATM: 1. Neither kselftest additions nor not even proper testing instructions. Why 21 patches and zero tests? How one should decide when the work is "complete"? 2. I still don't know what version of QEMU I should patch to test corner cases from an URL, which I cannot recall :-) Highlights the first issue. So for the time being the patch set is NAK just because we lack clear definition of done here. I revisit it only when I know how to test it. BR, Jarkko
On Sun Mar 31, 2024 at 7:00 PM EEST, Jarkko Sakkinen wrote: > On Sat Mar 30, 2024 at 8:48 PM EET, Gabríel Arthúr Pétursson wrote: > > On Tue, 2024-02-13 at 12:13 -0500, James Bottomley wrote: > > > + /* unique: key specific; for ECC it is two points */ > > > + tpm_buf_append_u16(&template, 0); > > > + tpm_buf_append_u16(&template, 0); > > > > Shouldn't the points be 32 bytes each in size, filled with zeros? > > > > The TCP TPM 2.0 Provisioning Guidance defines the SRK Template as a > > diff on top of Template L-2 (for ECC keys) as defined in the EK > > Credential Profile 2.0 document. > > > > Template L-2 calls for the X and Y points to be of 32 bytes each, > > filled with zeros. The Provisioning Guidance does not call for zero- > > sized points. > > > > For example, let's create an ECC Endorsement Key using the standard > > template then print its name: > > > > tpm2_createek -G ecc -c /dev/null -u ./ek.pub > > tpm2_loadexternal -c n -u ./ek.pub > > > > Equivalently using tpm2_createprimary: > > > > perl -e 'print "\0"x64' | tpm2_createprimary -C e -o ./ek.pub -G ecc -a 'fixedtpm|fixedparent|sensitivedataorigin|adminwithpolicy|restricted|decrypt' -L 837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa -u - > > tpm2_loadexternal -c n -u ./ek.pub > > > > You'll find that the key's public modulus matches that of the EK > > Certificate imprinted by the manufacturer, indicating we got the > > template correct. > > > > To generate a standard SRK key, the TCG TPM2 Provisioning Guidance > > states we should: > > > > 1. set userWithAuth, > > 2. clear adminWithPolicy > > 3. set noDA, and > > 4. clear the authorization policy. > > > > There's no mention of alterations to the unique field. > > > > Let's also create the key in the null hierarchy: > > > > perl -e 'print "\0"x64' | tpm2_createprimary -C n -o ./null.pub -G ecc -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricted|decrypt' -u - > > tpm2_loadexternal -c n -u ./null.pub > > > > The name does not match the kernel's name for the null key. > > Null key is not provisioned, what is the motivation here? > > Not saying no, just asking for details... > > There's couple of things that lack in this patch set ATM: > > 1. Neither kselftest additions nor not even proper testing > instructions. Why 21 patches and zero tests? How one should > decide when the work is "complete"? > 2. I still don't know what version of QEMU I should patch to > test corner cases from an URL, which I cannot recall :-) > Highlights the first issue. > > So for the time being the patch set is NAK just because we lack > clear definition of done here. I revisit it only when I know how > to test it. Your feedback was great because it highlights the issue that the excepted behaviour is non-existent at this point. BR, Jarkko
On Sun, 2024-03-31 at 19:00 +0300, Jarkko Sakkinen wrote: > Null key is not provisioned, what is the motivation here? The terms here, to create a key and to provision a key, are almost interchangeable. The latter merely implies the additional step of saving the key to NVRAM and making it available through a persistent handle. The template, aside from defining what kind of key we want to create, is fed into the TPM's KDF used to generate the key. Different template, different key. Userspace will want to recreate the same key the kernel has, thus must use the same template. Which template shall be used then? + /* + * create the template. Note: in order for userspace to + * verify the security of the system, it will have to create + * and certify this NULL primary, meaning all the template + * parameters will have to be identical, so conform exactly to + * the TCG TPM v2.0 Provisioning Guidance for the SRK ECC + * key + */ The TPM specifications have a standardized set of templates for the Endorsement Keys, and a recommendation on a template to create/provision the shared SRK. Why not use that one then, above something else? We are required to use a template for key generation after all, might as well pick the one most fitting from the standards. That's at least my understanding of the author's motivation. > So for the time being the patch set is NAK just because we lack > clear definition of done here. I revisit it only when I know how > to test it. I want to note that I'm not affiliated with the patches' author. I'm merely an outside observer who has taken interest in the proposed changes. Wanted to share my thoughts.
On Sun Mar 31, 2024 at 7:52 PM EEST, Gabríel Arthúr Pétursson wrote: > On Sun, 2024-03-31 at 19:00 +0300, Jarkko Sakkinen wrote: > > Null key is not provisioned, what is the motivation here? > > The terms here, to create a key and to provision a key, are almost > interchangeable. The latter merely implies the additional step of > saving the key to NVRAM and making it available through a persistent > handle. > > The template, aside from defining what kind of key we want to create, > is fed into the TPM's KDF used to generate the key. Different template, > different key. > > Userspace will want to recreate the same key the kernel has, thus must > use the same template. Which template shall be used then? I don't disagree with you, nor did when sending the first response :-) Downstream seems to break that guide tho. E.g. Ubuntu uses 0x80100001 NV index for storage key unsealing the key slot with PCR values. I did some time ago a quick VM install of Ubuntu just to see how it uses TPM2. I got: $ sudo tpm2_getcap handles-persistent - 0x81000001 - 0x81010001 0x81010001 is according to provisioning guide EK handle but Ubuntu uses it for completely different purpose. What I would like to understand when it comes to the provisioning guide is how important it is in reality? I have no information at this point is e.g. systemd-boot going to follow it but would like to know. > > + /* > + * create the template. Note: in order for userspace to > + * verify the security of the system, it will have to create > + * and certify this NULL primary, meaning all the template > + * parameters will have to be identical, so conform exactly to > + * the TCG TPM v2.0 Provisioning Guidance for the SRK ECC > + * key > + */ > > The TPM specifications have a standardized set of templates for the > Endorsement Keys, and a recommendation on a template to > create/provision the shared SRK. > > Why not use that one then, above something else? We are required to use > a template for key generation after all, might as well pick the one > most fitting from the standards. > > That's at least my understanding of the author's motivation. Yeah, not necessarily disagree with this or I'm somewhat aligned to this view. Ubuntu's architecture confuses me so would like to understand the magnitude of the provisioning guide. > > > So for the time being the patch set is NAK just because we lack > > clear definition of done here. I revisit it only when I know how > > to test it. > > I want to note that I'm not affiliated with the patches' author. I'm > merely an outside observer who has taken interest in the proposed > changes. Wanted to share my thoughts. BR, Jarkko
On Mon Apr 1, 2024 at 3:57 PM EEST, Jarkko Sakkinen wrote: > > > So for the time being the patch set is NAK just because we lack > > > clear definition of done here. I revisit it only when I know how > > > to test it. > > > > I want to note that I'm not affiliated with the patches' author. I'm > > merely an outside observer who has taken interest in the proposed > > changes. Wanted to share my thoughts. I.e. thanks a lot for the feedback. It makes sense to I'm just confused overally of the provisioning topic as one big player (Canonical) in the cloud seems to break that guide, unless I missed something. It would interesting to get some feedback on this from Ubuntu developers but I don't know who has worked on TPM2 boot feature on that side. BR, Jarkko
On Sat, 2024-03-30 at 18:48 +0000, Gabríel Arthúr Pétursson wrote: > On Tue, 2024-02-13 at 12:13 -0500, James Bottomley wrote: > > + /* unique: key specific; for ECC it is two points */ > > + tpm_buf_append_u16(&template, 0); > > + tpm_buf_append_u16(&template, 0); > > Shouldn't the points be 32 bytes each in size, filled with zeros? > > The TCP TPM 2.0 Provisioning Guidance defines the SRK Template as a > diff on top of Template L-2 (for ECC keys) as defined in the EK > Credential Profile 2.0 document. > > Template L-2 calls for the X and Y points to be of 32 bytes each, > filled with zeros. The Provisioning Guidance does not call for zero- > sized points. > > For example, let's create an ECC Endorsement Key using the standard > template then print its name: > > tpm2_createek -G ecc -c /dev/null -u ./ek.pub > tpm2_loadexternal -c n -u ./ek.pub > > Equivalently using tpm2_createprimary: > > perl -e 'print "\0"x64' | tpm2_createprimary -C e -o ./ek.pub -G > ecc -a > 'fixedtpm|fixedparent|sensitivedataorigin|adminwithpolicy|restricted| > decrypt' -L > 837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa -u - > tpm2_loadexternal -c n -u ./ek.pub > > You'll find that the key's public modulus matches that of the EK > Certificate imprinted by the manufacturer, indicating we got the > template correct. > > To generate a standard SRK key, the TCG TPM2 Provisioning Guidance > states we should: > > 1. set userWithAuth, > 2. clear adminWithPolicy > 3. set noDA, and > 4. clear the authorization policy. > > There's no mention of alterations to the unique field. > > Let's also create the key in the null hierarchy: > > perl -e 'print "\0"x64' | tpm2_createprimary -C n -o ./null.pub -G > ecc -a > 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricte > d|decrypt' -u - > tpm2_loadexternal -c n -u ./null.pub > > The name does not match the kernel's name for the null key. So this seems to be a fairly global problem. If you look at openssl_tpm2_engine, openconnect and gnutls, they all formulate the ECC primary template this way (zero size points). They all look to have inherited it from the IBM TSS: https://github.com/kgoldman/ibmtss/blob/master/utils/objecttemplates.c#L272-L275 Ironically, even the Intel TSS based openssl engine and the provider seem to define it this way as well, and they definitely didn't get it from the IBM TSS: https://github.com/tpm2-software/tpm2-tss-engine/blob/master/src/tpm2-tss-engine-common.h#L149-L152 The problem is if we change it now, it would break all the current keys with primary handles (which is pretty much all of them thanks to almost no-one following the provisioning guidance about persistent handles). So I'm not really sure how to solve this. At the moment the kernel doesn't use permanent handles for keys, but it should and it should follow what all of the industry is doing for interoperability (i.e. zero size points), which means the NULL primary should also follow it. James
On Mon, 2024-04-01 at 10:19 -0400, James Bottomley wrote: > So I'm not really sure how to solve this. At the moment the kernel > doesn't use permanent handles for keys, but it should and it should > follow what all of the industry is doing for interoperability (i.e. > zero size points), which means the NULL primary should also follow > it. Actually, it turns out this is already solved by the TCG. The template we're using is the correct one (zero size points). Apparently they regretted their earlier decision to zero fill and issued this guidance: 2.2.1.2.2 EK Template An EK Template is stored in an NV Index as a TPMT_PUBLIC structure marshaled as described in the TPM 2.0 Library Specification [1]. The default EK Templates are defined in annex B. The EK Template NV Index MUST be Populated if non-default values are used. It SHOULD be Absent if default values are used. The EK Template unique field buffer size(s) SHOULD be zero. But since they can't revoke the previous guidance, we now have two templates defined: the L one which has the old n bytes of zeros and the new (and recommended) H one which has zero size unique field. https://trustedcomputinggroup.org/resource/http-trustedcomputinggroup-org-wp-content-uploads-tcg-ek-credential-profile-v-2-5-r2_published-pdf/ So in other words, we're doing the later correct thing and there's no problem. I'll update the ASN.1 draft https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html to state that we MUST use the H template to remove any ambiguity James
On Mon Apr 1, 2024 at 7:55 PM EEST, James Bottomley wrote: > On Mon, 2024-04-01 at 10:19 -0400, James Bottomley wrote: > > So I'm not really sure how to solve this. At the moment the kernel > > doesn't use permanent handles for keys, but it should and it should > > follow what all of the industry is doing for interoperability (i.e. > > zero size points), which means the NULL primary should also follow > > it. > > Actually, it turns out this is already solved by the TCG. The template > we're using is the correct one (zero size points). Apparently they > regretted their earlier decision to zero fill and issued this guidance: > > 2.2.1.2.2 EK Template > > An EK Template is stored in an NV Index as a TPMT_PUBLIC structure > marshaled as described in the TPM 2.0 Library Specification [1]. The > default EK Templates are defined in annex B. The EK Template NV Index > MUST be Populated if non-default values are used. It SHOULD be Absent > if default values are used. > > The EK Template unique field buffer size(s) SHOULD be zero. > > But since they can't revoke the previous guidance, we now have two > templates defined: the L one which has the old n bytes of zeros and the > new (and recommended) H one which has zero size unique field. > > https://trustedcomputinggroup.org/resource/http-trustedcomputinggroup-org-wp-content-uploads-tcg-ek-credential-profile-v-2-5-r2_published-pdf/ > > So in other words, we're doing the later correct thing and there's no > problem. I'll update the ASN.1 draft > > https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html First time I'm seeing this document or URL. > > to state that we MUST use the H template to remove any ambiguity > > James BR, Jarkko
On Mon Apr 1, 2024 at 11:54 PM EEST, Jarkko Sakkinen wrote: > On Mon Apr 1, 2024 at 7:55 PM EEST, James Bottomley wrote: > > On Mon, 2024-04-01 at 10:19 -0400, James Bottomley wrote: > > > So I'm not really sure how to solve this. At the moment the kernel > > > doesn't use permanent handles for keys, but it should and it should > > > follow what all of the industry is doing for interoperability (i.e. > > > zero size points), which means the NULL primary should also follow > > > it. > > > > Actually, it turns out this is already solved by the TCG. The template > > we're using is the correct one (zero size points). Apparently they > > regretted their earlier decision to zero fill and issued this guidance: > > > > 2.2.1.2.2 EK Template > > > > An EK Template is stored in an NV Index as a TPMT_PUBLIC structure > > marshaled as described in the TPM 2.0 Library Specification [1]. The > > default EK Templates are defined in annex B. The EK Template NV Index > > MUST be Populated if non-default values are used. It SHOULD be Absent > > if default values are used. > > > > The EK Template unique field buffer size(s) SHOULD be zero. > > > > But since they can't revoke the previous guidance, we now have two > > templates defined: the L one which has the old n bytes of zeros and the > > new (and recommended) H one which has zero size unique field. > > > > https://trustedcomputinggroup.org/resource/http-trustedcomputinggroup-org-wp-content-uploads-tcg-ek-credential-profile-v-2-5-r2_published-pdf/ > > > > So in other words, we're doing the later correct thing and there's no > > problem. I'll update the ASN.1 draft > > > > https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html > > First time I'm seeing this document or URL. Anyway, only thing that we align with is the latest in kernel documentation, outside URL's are ignored. I.e. the legit ref is trusted-encrypted.rst. BR, Jarkko
On 3/31/2024 12:52 PM, Gabríel Arthúr Pétursson wrote: > The TPM specifications have a standardized set of templates for the > Endorsement Keys, and a recommendation on a template to > create/provision the shared SRK. The original TCG guidance document for an SRK used arrays of zeros for the unique field. This was either a holdover from TPM 1.2, where arrays were 20 bytes, or a misinterpretation of text that said: NULL. The reality is that it's a TPM2B, and the size(s) can be zero. The answer for the EK is different. It has to use the TCG standard. The EK is not a 'guidance document'.
On Tue Apr 2, 2024 at 10:30 PM EEST, Ken Goldman wrote: > On 3/31/2024 12:52 PM, Gabríel Arthúr Pétursson wrote: > > The TPM specifications have a standardized set of templates for the > > Endorsement Keys, and a recommendation on a template to > > create/provision the shared SRK. > > The original TCG guidance document for an SRK used arrays of zeros for > the unique field. > > This was either a holdover from TPM 1.2, where arrays were 20 bytes, > or a misinterpretation of text that said: NULL. > > The reality is that it's a TPM2B, and the size(s) can be zero. > > The answer for the EK is different. It has to use the TCG > standard. The EK is not a 'guidance document'. Does anyone follow TCG's provisioning guide? I.e. is it implemented "in the industry"? I'm just trying to understand the real value of this document. BR, Jarkko
On Fri, 2024-02-23 at 17:51 +0200, Jarkko Sakkinen wrote: > On Tue Feb 13, 2024 at 7:13 PM EET, James Bottomley wrote: [...] > > --- a/drivers/char/tpm/tpm.h > > +++ b/drivers/char/tpm/tpm.h > > @@ -321,4 +321,14 @@ void tpm_bios_log_setup(struct tpm_chip > > *chip); > > void tpm_bios_log_teardown(struct tpm_chip *chip); > > int tpm_dev_common_init(void); > > void tpm_dev_common_exit(void); > > + > > +#ifdef CONFIG_TCG_TPM2_HMAC > > +int tpm2_sessions_init(struct tpm_chip *chip); > > I'm sorry but "sessions" and "init" are the worst possible > terminology I could every pick when trying to make a function that > self-documents itself :-) About 95% of the kernel that run initialization routines are suffixed _init. The TCG documents that describe how to effect HMAC and encryption of commands call the entities used to do this "sessions". You can argue they should have chosen a better name, but given it's the name they chose, we have to use it to avoid confusion. > I'd like to see here well-scoped name so that it is easy to connect > this to its purpose when reviewing some other patches. It's purpose is to initialize the sessions handling code in the tpm2- sessions.c file; I'm not sure I could come up with anything as concise for self description. James
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 927088b2c3d3..e3c39a83171b 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -27,6 +27,17 @@ menuconfig TCG_TPM if TCG_TPM +config TCG_TPM2_HMAC + bool "Use encrypted and HMACd transactions on the TPM bus" + default y + help + Setting this causes us to deploy a scheme which uses request + and response HMACs in addition to encryption for + communicating with the TPM to prevent or detect bus snooping + and interposer attacks (see tpm-security.rst). Saying Y + here adds some encryption overhead to all kernel to TPM + transactions. + config HW_RANDOM_TPM bool "TPM HW Random Number Generator support" depends on TCG_TPM && HW_RANDOM && !(TCG_TPM=y && HW_RANDOM=m) diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index ad3594e383e1..4c695b0388f3 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -17,6 +17,7 @@ tpm-y += eventlog/tpm1.o tpm-y += eventlog/tpm2.o tpm-y += tpm-buf.o +tpm-$(CONFIG_TCG_TPM2_HMAC) += tpm2-sessions.o tpm-$(CONFIG_ACPI) += tpm_ppi.o eventlog/acpi.o tpm-$(CONFIG_EFI) += eventlog/efi.o tpm-$(CONFIG_OF) += eventlog/of.o diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index cbc9d1e2974d..6b8b9956ba69 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -321,4 +321,14 @@ void tpm_bios_log_setup(struct tpm_chip *chip); void tpm_bios_log_teardown(struct tpm_chip *chip); int tpm_dev_common_init(void); void tpm_dev_common_exit(void); + +#ifdef CONFIG_TCG_TPM2_HMAC +int tpm2_sessions_init(struct tpm_chip *chip); +#else +static inline int tpm2_sessions_init(struct tpm_chip *chip) +{ + return 0; +} +#endif + #endif diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 93545be190a5..b0e72fb563d9 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -759,6 +759,11 @@ int tpm2_auto_startup(struct tpm_chip *chip) rc = 0; } + if (rc) + goto out; + + rc = tpm2_sessions_init(chip); + out: /* * Infineon TPM in field upgrade mode will return no data for the number diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c new file mode 100644 index 000000000000..9fc263ee02c2 --- /dev/null +++ b/drivers/char/tpm/tpm2-sessions.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (C) 2018 James.Bottomley@HansenPartnership.com + * + */ + +#include "tpm.h" + +#include <asm/unaligned.h> + +#include <crypto/aes.h> + +/* if you change to AES256, you only need change this */ +#define AES_KEYBYTES AES_KEYSIZE_128 + +#define AES_KEYBITS (AES_KEYBYTES*8) + +static int tpm2_parse_create_primary(struct tpm_chip *chip, struct tpm_buf *buf, + u32 *nullkey) +{ + struct tpm_header *head = (struct tpm_header *)buf->data; + off_t offset_r = TPM_HEADER_SIZE, offset_t; + u16 len = TPM_HEADER_SIZE; + u32 tot_len = be32_to_cpu(head->length); + u32 val, parm_len; + + *nullkey = tpm_buf_read_u32(buf, &offset_r); + parm_len = tpm_buf_read_u32(buf, &offset_r); + /* + * parm_len doesn't include the header, but all the other + * lengths and offsets do, so add it to parm len to make + * the comparisons easier + */ + parm_len += TPM_HEADER_SIZE; + + if (parm_len + 8 > tot_len) + return -EINVAL; + len = tpm_buf_read_u16(buf, &offset_r); + offset_t = offset_r; + /* now we have the public area, compute the name of the object */ + put_unaligned_be16(TPM_ALG_SHA256, chip->null_key_name); + sha256(&buf->data[offset_r], len, chip->null_key_name + 2); + + /* validate the public key */ + val = tpm_buf_read_u16(buf, &offset_t); + /* key type (must be what we asked for) */ + if (val != TPM_ALG_ECC) + return -EINVAL; + val = tpm_buf_read_u16(buf, &offset_t); + /* name algorithm */ + if (val != TPM_ALG_SHA256) + return -EINVAL; + val = tpm_buf_read_u32(buf, &offset_t); + /* object properties */ + if (val != (TPM2_OA_NO_DA | + TPM2_OA_FIXED_TPM | + TPM2_OA_FIXED_PARENT | + TPM2_OA_SENSITIVE_DATA_ORIGIN | + TPM2_OA_USER_WITH_AUTH | + TPM2_OA_DECRYPT | + TPM2_OA_RESTRICTED)) + return -EINVAL; + /* auth policy (empty) */ + val = tpm_buf_read_u16(buf, &offset_t); + if (val != 0) + return -EINVAL; + /* symmetric key parameters */ + val = tpm_buf_read_u16(buf, &offset_t); + if (val != TPM_ALG_AES) + return -EINVAL; + /* symmetric key length */ + val = tpm_buf_read_u16(buf, &offset_t); + if (val != AES_KEYBITS) + return -EINVAL; + /* symmetric encryption scheme */ + val = tpm_buf_read_u16(buf, &offset_t); + if (val != TPM_ALG_CFB) + return -EINVAL; + /* signing scheme */ + val = tpm_buf_read_u16(buf, &offset_t); + if (val != TPM_ALG_NULL) + return -EINVAL; + /* ECC Curve */ + val = tpm_buf_read_u16(buf, &offset_t); + if (val != TPM2_ECC_NIST_P256) + return -EINVAL; + /* KDF Scheme */ + val = tpm_buf_read_u16(buf, &offset_t); + if (val != TPM_ALG_NULL) + return -EINVAL; + /* x point */ + val = tpm_buf_read_u16(buf, &offset_t); + if (val != EC_PT_SZ) + return -EINVAL; + memcpy(chip->null_ec_key_x, &buf->data[offset_t], val); + offset_t += val; + val = tpm_buf_read_u16(buf, &offset_t); + if (val != EC_PT_SZ) + return -EINVAL; + memcpy(chip->null_ec_key_y, &buf->data[offset_t], val); + offset_t += val; + + /* original length of the whole TPM2B */ + offset_r += len; + + /* should have exactly consumed the TPM2B public structure */ + if (offset_t != offset_r) + return -EINVAL; + if (offset_r > parm_len) + return -EINVAL; + /* creation data (skip) */ + len = tpm_buf_read_u16(buf, &offset_r); + offset_r += len; + if (offset_r > parm_len) + return -EINVAL; + /* creation digest (must be sha256) */ + len = tpm_buf_read_u16(buf, &offset_r); + offset_r += len; + if (len != SHA256_DIGEST_SIZE || offset_r > parm_len) + return -EINVAL; + /* TPMT_TK_CREATION follows */ + /* tag, must be TPM_ST_CREATION (0x8021) */ + val = tpm_buf_read_u16(buf, &offset_r); + if (val != TPM2_ST_CREATION || offset_r > parm_len) + return -EINVAL; + /* hierarchy (must be NULL) */ + val = tpm_buf_read_u32(buf, &offset_r); + if (val != TPM2_RH_NULL || offset_r > parm_len) + return -EINVAL; + /* the ticket digest HMAC (might not be sha256) */ + len = tpm_buf_read_u16(buf, &offset_r); + offset_r += len; + if (offset_r > parm_len) + return -EINVAL; + /* + * finally we have the name, which is a sha256 digest plus a 2 + * byte algorithm type + */ + len = tpm_buf_read_u16(buf, &offset_r); + if (offset_r + len != parm_len + 8) + return -EINVAL; + if (len != SHA256_DIGEST_SIZE + 2) + return -EINVAL; + + if (memcmp(chip->null_key_name, &buf->data[offset_r], + SHA256_DIGEST_SIZE + 2) != 0) { + dev_err(&chip->dev, "NULL Seed name comparison failed\n"); + return -EINVAL; + } + + return 0; +} + +static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy, u32 *handle) +{ + int rc; + struct tpm_buf buf; + struct tpm_buf template; + + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE_PRIMARY); + if (rc) + return rc; + + rc = tpm_buf_init_sized(&template); + if (rc) { + tpm_buf_destroy(&buf); + return rc; + } + + /* + * create the template. Note: in order for userspace to + * verify the security of the system, it will have to create + * and certify this NULL primary, meaning all the template + * parameters will have to be identical, so conform exactly to + * the TCG TPM v2.0 Provisioning Guidance for the SRK ECC + * key + */ + + /* key type */ + tpm_buf_append_u16(&template, TPM_ALG_ECC); + /* name algorithm */ + tpm_buf_append_u16(&template, TPM_ALG_SHA256); + /* object properties */ + tpm_buf_append_u32(&template, TPM2_OA_NO_DA | + TPM2_OA_FIXED_TPM | + TPM2_OA_FIXED_PARENT | + TPM2_OA_SENSITIVE_DATA_ORIGIN | + TPM2_OA_USER_WITH_AUTH | + TPM2_OA_DECRYPT | + TPM2_OA_RESTRICTED); + /* sauth policy (empty) */ + tpm_buf_append_u16(&template, 0); + + /* BEGIN parameters: key specific; for ECC*/ + /* symmetric algorithm */ + tpm_buf_append_u16(&template, TPM_ALG_AES); + /* bits for symmetric algorithm */ + tpm_buf_append_u16(&template, 128); + /* algorithm mode (must be CFB) */ + tpm_buf_append_u16(&template, TPM_ALG_CFB); + /* scheme (NULL means any scheme) */ + tpm_buf_append_u16(&template, TPM_ALG_NULL); + /* ECC Curve ID */ + tpm_buf_append_u16(&template, TPM2_ECC_NIST_P256); + /* KDF Scheme */ + tpm_buf_append_u16(&template, TPM_ALG_NULL); + /* unique: key specific; for ECC it is two points */ + tpm_buf_append_u16(&template, 0); + tpm_buf_append_u16(&template, 0); + /* END parameters */ + + /* primary handle */ + tpm_buf_append_u32(&buf, hierarchy); + tpm_buf_append_empty_auth(&buf, TPM2_RS_PW); + /* sensitive create size is 4 for two empty buffers */ + tpm_buf_append_u16(&buf, 4); + /* sensitive create auth data (empty) */ + tpm_buf_append_u16(&buf, 0); + /* sensitive create sensitive data (empty) */ + tpm_buf_append_u16(&buf, 0); + /* the public template */ + tpm_buf_append(&buf, template.data, template.length); + tpm_buf_destroy(&template); + /* outside info (empty) */ + tpm_buf_append_u16(&buf, 0); + /* creation PCR (none) */ + tpm_buf_append_u32(&buf, 0); + + rc = tpm_transmit_cmd(chip, &buf, 0, + "attempting to create NULL primary"); + + if (rc == TPM2_RC_SUCCESS) + rc = tpm2_parse_create_primary(chip, &buf, handle); + + tpm_buf_destroy(&buf); + + return rc; +} + +static int tpm2_create_null_primary(struct tpm_chip *chip) +{ + u32 nullkey; + int rc; + + rc = tpm2_create_primary(chip, TPM2_RH_NULL, &nullkey); + + if (rc == TPM2_RC_SUCCESS) { + unsigned int offset = 0; /* dummy offset for null key context */ + + rc = tpm2_save_context(chip, nullkey, chip->null_key_context, + sizeof(chip->null_key_context), &offset); + tpm2_flush_context(chip, nullkey); + } + + return rc; +} + +/** + * tpm2_sessions_init() - start of day initialization for the sessions code + * @chip: TPM chip + * + * Derive and context save the null primary and allocate memory in the + * struct tpm_chip for the authorizations. + */ +int tpm2_sessions_init(struct tpm_chip *chip) +{ + int rc; + + rc = tpm2_create_null_primary(chip); + if (rc) + dev_err(&chip->dev, "TPM: security failed (NULL seed derivation): %d\n", rc); + + return rc; +} +EXPORT_SYMBOL(tpm2_sessions_init); diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 6be263509e81..3060ab794afb 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -35,12 +35,15 @@ struct trusted_key_options; enum tpm_algorithms { TPM_ALG_ERROR = 0x0000, TPM_ALG_SHA1 = 0x0004, + TPM_ALG_AES = 0x0006, TPM_ALG_KEYEDHASH = 0x0008, TPM_ALG_SHA256 = 0x000B, TPM_ALG_SHA384 = 0x000C, TPM_ALG_SHA512 = 0x000D, TPM_ALG_NULL = 0x0010, TPM_ALG_SM3_256 = 0x0012, + TPM_ALG_ECC = 0x0023, + TPM_ALG_CFB = 0x0043, }; /* @@ -49,6 +52,11 @@ enum tpm_algorithms { */ #define TPM_MAX_HASHES 5 +enum tpm2_curves { + TPM2_ECC_NONE = 0x0000, + TPM2_ECC_NIST_P256 = 0x0003, +}; + struct tpm_digest { u16 alg_id; u8 digest[TPM_MAX_DIGEST_SIZE]; @@ -116,6 +124,20 @@ struct tpm_chip_seqops { const struct seq_operations *seqops; }; +/* fixed define for the curve we use which is NIST_P256 */ +#define EC_PT_SZ 32 + +/* + * fixed define for the size of a name. This is actually HASHALG size + * plus 2, so 32 for SHA256 + */ +#define TPM2_NAME_SIZE 34 + +/* + * The maximum size for an object context + */ +#define TPM2_MAX_CONTEXT_SIZE 4096 + struct tpm_chip { struct device dev; struct device devs; @@ -170,6 +192,14 @@ struct tpm_chip { /* active locality */ int locality; + +#ifdef CONFIG_TCG_TPM2_HMAC + /* details for communication security via sessions */ + u8 null_key_context[TPM2_MAX_CONTEXT_SIZE]; /* context for NULL seed */ + u8 null_key_name[TPM2_NAME_SIZE]; /* name of NULL seed */ + u8 null_ec_key_x[EC_PT_SZ]; + u8 null_ec_key_y[EC_PT_SZ]; +#endif }; #define TPM_HEADER_SIZE 10 @@ -194,6 +224,7 @@ enum tpm2_timeouts { enum tpm2_structures { TPM2_ST_NO_SESSIONS = 0x8001, TPM2_ST_SESSIONS = 0x8002, + TPM2_ST_CREATION = 0x8021, }; /* Indicates from what layer of the software stack the error comes from */ @@ -243,6 +274,7 @@ enum tpm2_command_codes { }; enum tpm2_permanent_handles { + TPM2_RH_NULL = 0x40000007, TPM2_RS_PW = 0x40000009, }; @@ -318,7 +350,11 @@ struct tpm_buf { enum tpm2_object_attributes { TPM2_OA_FIXED_TPM = BIT(1), TPM2_OA_FIXED_PARENT = BIT(4), + TPM2_OA_SENSITIVE_DATA_ORIGIN = BIT(5), TPM2_OA_USER_WITH_AUTH = BIT(6), + TPM2_OA_NO_DA = BIT(10), + TPM2_OA_RESTRICTED = BIT(16), + TPM2_OA_DECRYPT = BIT(17), }; enum tpm2_session_attributes { @@ -373,6 +409,15 @@ extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max); extern struct tpm_chip *tpm_default_chip(void); void tpm2_flush_context(struct tpm_chip *chip, u32 handle); +static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle) +{ + /* simple authorization for empty auth */ + tpm_buf_append_u32(buf, 9); /* total length of auth */ + tpm_buf_append_u32(buf, handle); + tpm_buf_append_u16(buf, 0); /* nonce len */ + tpm_buf_append_u8(buf, 0); /* attributes */ + tpm_buf_append_u16(buf, 0); /* hmac len */ +} #else static inline int tpm_is_tpm2(struct tpm_chip *chip) { @@ -399,5 +444,9 @@ static inline struct tpm_chip *tpm_default_chip(void) { return NULL; } + +static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle) +{ +} #endif #endif
The session handling code uses a "salted" session, meaning a session whose salt is encrypted to the public part of another TPM key so an observer cannot obtain it (and thus deduce the session keys). This patch creates and context saves in the tpm_chip area the primary key of the NULL hierarchy for this purpose. Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> --- v6: split out of original HMAC patch update config name v7: rename null seed parameters --- drivers/char/tpm/Kconfig | 11 ++ drivers/char/tpm/Makefile | 1 + drivers/char/tpm/tpm.h | 10 ++ drivers/char/tpm/tpm2-cmd.c | 5 + drivers/char/tpm/tpm2-sessions.c | 276 +++++++++++++++++++++++++++++++ include/linux/tpm.h | 49 ++++++ 6 files changed, 352 insertions(+) create mode 100644 drivers/char/tpm/tpm2-sessions.c