Message ID | 20240524130459.21510-3-James.Bottomley@HansenPartnership.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | Add policy to sealed keys | expand |
Please rewrite short summray to something understandable. Perhaps "tpm: authenticated policy sessions for TPM2" would be more to the point. On Fri May 24, 2024 at 4:04 PM EEST, James Bottomley wrote: > TPM security uses hmac sessions to implement data protection and TPM2 sessions use HMAC ... > integrity. The hash algorithm of these sessions isn't exposed to the > user (and doesn't depend on the object being authorized) so it is Either remove the text in parentheses or rewrite it as a separate sentence if it is relevant. > currently fixed at a safe sha256. Policy sessions may also be used SHA-256 > for the same purpose and in addition, they can be used to express a > rich policy language. However, the session hash of policy sessions is > defined to be the same as the name algorithm of the TPM object they're > operating on, so we must update the session code to allow a variable No information about the TPM object in question, which means no rationale to bundle variable digest size to this patch. I know that null key uses SHA-256 as name algorithm: /* name algorithm */ if (val != TPM_ALG_SHA256) return -EINVAL; val = tpm_buf_read_u32(buf, &offset_t); Please state what other TPM object might be in question. Also, describe your changes in imperative form. This is also stated in SubmittingPatches. > hash algorithm instead of the fixed sha256 one. Note that while this > affects most of the code, the KDFe algorithm still uses a fixed sha256 > algorithm because it is define to follow the name algorithm of the > salt encryption key (which is a key we derive from the null seed and > set its name algorithm to sha256). Split the long description to two paragraphs: one that describes the motivation and one that describes the solution. > > Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> > --- > drivers/char/tpm/tpm2-sessions.c | 307 +++++++++++++++++++------------ > include/linux/tpm.h | 6 + > 2 files changed, 200 insertions(+), 113 deletions(-) > > diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c > index ea8860661876..63c175b2165c 100644 > --- a/drivers/char/tpm/tpm2-sessions.c > +++ b/drivers/char/tpm/tpm2-sessions.c > @@ -105,10 +105,10 @@ struct tpm2_auth { > /* > * the size here is variable and set by the size of our_nonce > * which must be between 16 and the name hash length. we set > - * the maximum sha256 size for the greatest protection > + * the maximum hash size for the greatest protection > */ > - u8 our_nonce[SHA256_DIGEST_SIZE]; > - u8 tpm_nonce[SHA256_DIGEST_SIZE]; > + u8 our_nonce[HASH_MAX_DIGESTSIZE]; > + u8 tpm_nonce[HASH_MAX_DIGESTSIZE]; > /* > * the salt is only used across the session command/response > * after that it can be used as a scratch area > @@ -119,17 +119,15 @@ struct tpm2_auth { > u8 scratch[AES_KEY_BYTES + AES_BLOCK_SIZE]; > }; > /* > - * the session key and passphrase are the same size as the > - * name digest (sha256 again). The session key is constant > - * for the use of the session and the passphrase can change > - * with every invocation. > - * > - * Note: these fields must be adjacent and in this order > - * because several HMAC/KDF schemes use the combination of the > - * session_key and passphrase. > + * The session_key and passphrase must be next to each other > + * to allow us to hash them as a unit. With a variable hash, > + * this means that the passphrase must occur exactly one hash > + * size after the session_key, so make session_key house both > + * and passphrase is a pointer into where the passphrase > + * begins in session_key. > */ > - u8 session_key[SHA256_DIGEST_SIZE]; > - u8 passphrase[SHA256_DIGEST_SIZE]; > + u8 session_key[2*HASH_MAX_DIGESTSIZE]; > + u8 *passphrase; > int passphrase_len; > struct crypto_aes_ctx aes_ctx; > /* saved session attributes: */ > @@ -142,7 +140,8 @@ struct tpm2_auth { > * we must compute and remember > */ > u32 name_h[AUTH_MAX_NAMES]; > - u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE]; > + u8 name[AUTH_MAX_NAMES][2 + HASH_MAX_DIGESTSIZE]; > + struct crypto_shash *tfm; > }; > > /* > @@ -161,34 +160,36 @@ static u8 name_size(const u8 *name) > } > > /* > - * It turns out the crypto hmac(sha256) is hard for us to consume > + * It turns out the crypto hmac(shaX) is hard for us to consume > * because it assumes a fixed key and the TPM seems to change the key > * on every operation, so we weld the hmac init and final functions in > * here to give it the same usage characteristics as a regular hash > */ > -static void tpm2_hmac_init(struct sha256_state *sctx, u8 *key, u32 key_len) > +static void tpm2_hmac_init(struct shash_desc *sdesc, u8 *key, u32 key_len) > { > - u8 pad[SHA256_BLOCK_SIZE]; > + const int block_size = crypto_shash_blocksize(sdesc->tfm); > + u8 pad[SHA512_BLOCK_SIZE]; > int i; > > - sha256_init(sctx); > - for (i = 0; i < sizeof(pad); i++) { > + crypto_shash_init(sdesc); > + for (i = 0; i < block_size; i++) { > if (i < key_len) > pad[i] = key[i]; > else > pad[i] = 0; > pad[i] ^= HMAC_IPAD_VALUE; > } > - sha256_update(sctx, pad, sizeof(pad)); > + crypto_shash_update(sdesc, pad, block_size); > } > > -static void tpm2_hmac_final(struct sha256_state *sctx, u8 *key, u32 key_len, > +static void tpm2_hmac_final(struct shash_desc *sdesc, u8 *key, u32 key_len, > u8 *out) > { > - u8 pad[SHA256_BLOCK_SIZE]; > + const int block_size = crypto_shash_blocksize(sdesc->tfm); > + u8 pad[SHA512_BLOCK_SIZE]; > int i; > > - for (i = 0; i < sizeof(pad); i++) { > + for (i = 0; i < block_size; i++) { > if (i < key_len) > pad[i] = key[i]; > else > @@ -197,35 +198,42 @@ static void tpm2_hmac_final(struct sha256_state *sctx, u8 *key, u32 key_len, > } > > /* collect the final hash; use out as temporary storage */ > - sha256_final(sctx, out); > + crypto_shash_final(sdesc, out); > > - sha256_init(sctx); > - sha256_update(sctx, pad, sizeof(pad)); > - sha256_update(sctx, out, SHA256_DIGEST_SIZE); > - sha256_final(sctx, out); > + crypto_shash_init(sdesc); > + crypto_shash_update(sdesc, pad, block_size); > + crypto_shash_update(sdesc, out, crypto_shash_digestsize(sdesc->tfm)); > + crypto_shash_final(sdesc, out); > } > > /* > - * assume hash sha256 and nonces u, v of size SHA256_DIGEST_SIZE but > + * assume hash sha256 and nonces u, v of hash digest size but > * otherwise standard tpm2_KDFa. Note output is in bytes not bits. > */ > static void tpm2_KDFa(u8 *key, u32 key_len, const char *label, u8 *u, > - u8 *v, u32 bytes, u8 *out) > + u8 *v, u32 bytes, u8 *out, struct shash_desc *sdesc) > { > u32 counter = 1; > const __be32 bits = cpu_to_be32(bytes * 8); > + const int digest_size = crypto_shash_digestsize(sdesc->tfm); > > while (bytes > 0) { > - struct sha256_state sctx; > __be32 c = cpu_to_be32(counter); > > - tpm2_hmac_init(&sctx, key, key_len); > - sha256_update(&sctx, (u8 *)&c, sizeof(c)); > - sha256_update(&sctx, label, strlen(label)+1); > - sha256_update(&sctx, u, SHA256_DIGEST_SIZE); > - sha256_update(&sctx, v, SHA256_DIGEST_SIZE); > - sha256_update(&sctx, (u8 *)&bits, sizeof(bits)); > - tpm2_hmac_final(&sctx, key, key_len, out); > + tpm2_hmac_init(sdesc, key, key_len); > + crypto_shash_update(sdesc, (u8 *)&c, sizeof(c)); > + crypto_shash_update(sdesc, label, strlen(label)+1); > + crypto_shash_update(sdesc, u, digest_size); > + crypto_shash_update(sdesc, v, digest_size); > + crypto_shash_update(sdesc, (u8 *)&bits, sizeof(bits)); > + if (bytes < digest_size) { > + u8 buf[HASH_MAX_DIGESTSIZE]; > + > + tpm2_hmac_final(sdesc, key, key_len, buf); > + memcpy(out, buf, bytes); > + } else { > + tpm2_hmac_final(sdesc, key, key_len, out); > + } > > bytes -= SHA256_DIGEST_SIZE; > counter++; > @@ -236,9 +244,9 @@ static void tpm2_KDFa(u8 *key, u32 key_len, const char *label, u8 *u, > /* > * Somewhat of a bastardization of the real KDFe. We're assuming > * we're working with known point sizes for the input parameters and > - * the hash algorithm is fixed at sha256. Because we know that the > - * point size is 32 bytes like the hash size, there's no need to loop > - * in this KDF. > + * the hash algorithm is fixed at sha256 (name algorithm of the > + * encrypting key). Because we know that the point size is 32 bytes > + * like the hash size, there's no need to loop in this KDF. > */ > static void tpm2_KDFe(u8 z[EC_PT_SZ], const char *str, u8 *pt_u, u8 *pt_v, > u8 *out) > @@ -370,9 +378,10 @@ void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf, > u8 attributes, u8 *passphrase, > int passphrase_len) > { > - u8 nonce[SHA256_DIGEST_SIZE]; > + u8 nonce[HASH_MAX_DIGESTSIZE]; > u32 len; > struct tpm2_auth *auth = chip->auth; > + const int digest_size = crypto_shash_digestsize(auth->tfm); > > /* > * The Architecture Guide requires us to strip trailing zeros > @@ -384,8 +393,14 @@ void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf, > > auth->attrs = attributes; > auth->passphrase_len = passphrase_len; > - if (passphrase_len) > + if (passphrase_len) { > + /* > + * place the passphrase immediately adjacent to > + * the session key > + */ > + auth->passphrase = auth->session_key + digest_size; > memcpy(auth->passphrase, passphrase, passphrase_len); > + } > > if (auth->session != tpm_buf_length(buf)) { > /* we're not the first session */ > @@ -396,23 +411,23 @@ void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf, > } > > /* add our new session */ > - len += 9 + 2 * SHA256_DIGEST_SIZE; > + len += 9 + 2 * digest_size; > put_unaligned_be32(len, &buf->data[auth->session]); > } else { > - tpm_buf_append_u32(buf, 9 + 2 * SHA256_DIGEST_SIZE); > + tpm_buf_append_u32(buf, 9 + 2 * digest_size); > } > > /* random number for our nonce */ > - get_random_bytes(nonce, sizeof(nonce)); > - memcpy(auth->our_nonce, nonce, sizeof(nonce)); > + get_random_bytes(nonce, digest_size); > + memcpy(auth->our_nonce, nonce, digest_size); > tpm_buf_append_u32(buf, auth->handle); > /* our new nonce */ > - tpm_buf_append_u16(buf, SHA256_DIGEST_SIZE); > - tpm_buf_append(buf, nonce, SHA256_DIGEST_SIZE); > + tpm_buf_append_u16(buf, digest_size); > + tpm_buf_append(buf, nonce, digest_size); > tpm_buf_append_u8(buf, auth->attrs); > /* and put a placeholder for the hmac */ > - tpm_buf_append_u16(buf, SHA256_DIGEST_SIZE); > - tpm_buf_append(buf, nonce, SHA256_DIGEST_SIZE); > + tpm_buf_append_u16(buf, digest_size); > + tpm_buf_append(buf, nonce, digest_size); > } > EXPORT_SYMBOL(tpm_buf_append_hmac_session); > > @@ -443,8 +458,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf) > off_t offset_s = TPM_HEADER_SIZE, offset_p; > u8 *hmac = NULL; > u32 attrs; > - u8 cphash[SHA256_DIGEST_SIZE]; > - struct sha256_state sctx; > + const int digest_size = crypto_shash_digestsize(auth->tfm); > + u8 cphash[HASH_MAX_DIGESTSIZE]; > + SHASH_DESC_ON_STACK(sdesc, auth->tfm); > + > + sdesc->tfm = auth->tfm; > > /* save the command code in BE format */ > auth->ordinal = head->ordinal; > @@ -515,10 +533,10 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf) > u16 len; > > /* need key and IV */ > - tpm2_KDFa(auth->session_key, SHA256_DIGEST_SIZE > + tpm2_KDFa(auth->session_key, digest_size > + auth->passphrase_len, "CFB", auth->our_nonce, > auth->tpm_nonce, AES_KEY_BYTES + AES_BLOCK_SIZE, > - auth->scratch); > + auth->scratch, sdesc); > > len = tpm_buf_read_u16(buf, &offset_p); > aes_expandkey(&auth->aes_ctx, auth->scratch, AES_KEY_BYTES); > @@ -529,9 +547,10 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf) > offset_p -= 2; > } > > - sha256_init(&sctx); > + crypto_shash_init(sdesc); > /* ordinal is already BE */ > - sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal)); > + crypto_shash_update(sdesc, (u8 *)&head->ordinal, > + sizeof(head->ordinal)); > /* add the handle names */ > for (i = 0; i < handles; i++) { > enum tpm2_mso_type mso = tpm2_handle_mso(auth->name_h[i]); > @@ -539,27 +558,27 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf) > if (mso == TPM2_MSO_PERSISTENT || > mso == TPM2_MSO_VOLATILE || > mso == TPM2_MSO_NVRAM) { > - sha256_update(&sctx, auth->name[i], > - name_size(auth->name[i])); > + crypto_shash_update(sdesc, auth->name[i], > + name_size(auth->name[i])); > } else { > __be32 h = cpu_to_be32(auth->name_h[i]); > > - sha256_update(&sctx, (u8 *)&h, 4); > + crypto_shash_update(sdesc, (u8 *)&h, 4); > } > } > if (offset_s != tpm_buf_length(buf)) > - sha256_update(&sctx, &buf->data[offset_s], > - tpm_buf_length(buf) - offset_s); > - sha256_final(&sctx, cphash); > + crypto_shash_update(sdesc, &buf->data[offset_s], > + tpm_buf_length(buf) - offset_s); > + crypto_shash_final(sdesc, cphash); > > /* now calculate the hmac */ > - tpm2_hmac_init(&sctx, auth->session_key, sizeof(auth->session_key) > + tpm2_hmac_init(sdesc, auth->session_key, digest_size > + auth->passphrase_len); > - sha256_update(&sctx, cphash, sizeof(cphash)); > - sha256_update(&sctx, auth->our_nonce, sizeof(auth->our_nonce)); > - sha256_update(&sctx, auth->tpm_nonce, sizeof(auth->tpm_nonce)); > - sha256_update(&sctx, &auth->attrs, 1); > - tpm2_hmac_final(&sctx, auth->session_key, sizeof(auth->session_key) > + crypto_shash_update(sdesc, cphash, digest_size); > + crypto_shash_update(sdesc, auth->our_nonce, digest_size); > + crypto_shash_update(sdesc, auth->tpm_nonce, digest_size); > + crypto_shash_update(sdesc, &auth->attrs, 1); > + tpm2_hmac_final(sdesc, auth->session_key, digest_size > + auth->passphrase_len, hmac); > } > EXPORT_SYMBOL(tpm_buf_fill_hmac_session); > @@ -695,12 +714,15 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf, > struct tpm_header *head = (struct tpm_header *)buf->data; > struct tpm2_auth *auth = chip->auth; > off_t offset_s, offset_p; > - u8 rphash[SHA256_DIGEST_SIZE]; > + const int digest_size = crypto_shash_digestsize(auth->tfm); > + u8 rphash[HASH_MAX_DIGESTSIZE]; > u32 attrs; > - struct sha256_state sctx; > u16 tag = be16_to_cpu(head->tag); > u32 cc = be32_to_cpu(auth->ordinal); > int parm_len, len, i, handles; > + SHASH_DESC_ON_STACK(sdesc, auth->tfm); > + > + sdesc->tfm = auth->tfm; > > if (auth->session >= TPM_HEADER_SIZE) { > WARN(1, "tpm session not filled correctly\n"); > @@ -739,7 +761,7 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf, > len = tpm_buf_read_u16(buf, &offset_s); > if (offset_s + len > tpm_buf_length(buf)) > goto out; > - if (len != SHA256_DIGEST_SIZE) > + if (len != digest_size) > goto out; > memcpy(auth->tpm_nonce, &buf->data[offset_s], len); > offset_s += len; > @@ -747,32 +769,32 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf, > len = tpm_buf_read_u16(buf, &offset_s); > if (offset_s + len != tpm_buf_length(buf)) > goto out; > - if (len != SHA256_DIGEST_SIZE) > + if (len != digest_size) > goto out; > /* > * offset_s points to the HMAC. now calculate comparison, beginning > * with rphash > */ > - sha256_init(&sctx); > + crypto_shash_init(sdesc); > /* yes, I know this is now zero, but it's what the standard says */ > - sha256_update(&sctx, (u8 *)&head->return_code, > - sizeof(head->return_code)); > + crypto_shash_update(sdesc, (u8 *)&head->return_code, > + sizeof(head->return_code)); > /* ordinal is already BE */ > - sha256_update(&sctx, (u8 *)&auth->ordinal, sizeof(auth->ordinal)); > - sha256_update(&sctx, &buf->data[offset_p], parm_len); > - sha256_final(&sctx, rphash); > + crypto_shash_update(sdesc, (u8 *)&auth->ordinal, sizeof(auth->ordinal)); > + crypto_shash_update(sdesc, &buf->data[offset_p], parm_len); > + crypto_shash_final(sdesc, rphash); > > /* now calculate the hmac */ > - tpm2_hmac_init(&sctx, auth->session_key, sizeof(auth->session_key) > + tpm2_hmac_init(sdesc, auth->session_key, digest_size > + auth->passphrase_len); > - sha256_update(&sctx, rphash, sizeof(rphash)); > - sha256_update(&sctx, auth->tpm_nonce, sizeof(auth->tpm_nonce)); > - sha256_update(&sctx, auth->our_nonce, sizeof(auth->our_nonce)); > - sha256_update(&sctx, &auth->attrs, 1); > + crypto_shash_update(sdesc, rphash, digest_size); > + crypto_shash_update(sdesc, auth->tpm_nonce, digest_size); > + crypto_shash_update(sdesc, auth->our_nonce, digest_size); > + crypto_shash_update(sdesc, &auth->attrs, 1); > /* we're done with the rphash, so put our idea of the hmac there */ > - tpm2_hmac_final(&sctx, auth->session_key, sizeof(auth->session_key) > + tpm2_hmac_final(sdesc, auth->session_key, digest_size > + auth->passphrase_len, rphash); > - if (memcmp(rphash, &buf->data[offset_s], SHA256_DIGEST_SIZE) == 0) { > + if (memcmp(rphash, &buf->data[offset_s], digest_size) == 0) { > rc = 0; > } else { > dev_err(&chip->dev, "TPM: HMAC check failed\n"); > @@ -782,10 +804,10 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf, > /* now do response decryption */ > if (auth->attrs & TPM2_SA_ENCRYPT) { > /* need key and IV */ > - tpm2_KDFa(auth->session_key, SHA256_DIGEST_SIZE > + tpm2_KDFa(auth->session_key, digest_size > + auth->passphrase_len, "CFB", auth->tpm_nonce, > auth->our_nonce, AES_KEY_BYTES + AES_BLOCK_SIZE, > - auth->scratch); > + auth->scratch, sdesc); > > len = tpm_buf_read_u16(buf, &offset_p); > aes_expandkey(&auth->aes_ctx, auth->scratch, AES_KEY_BYTES); > @@ -799,6 +821,7 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf, > if (rc) > /* manually close the session if it wasn't consumed */ > tpm2_flush_context(chip, auth->handle); > + crypto_free_shash(auth->tfm); > memzero_explicit(auth, sizeof(*auth)); > } else { > /* reset for next use */ > @@ -821,8 +844,11 @@ EXPORT_SYMBOL(tpm_buf_check_hmac_response); > */ > void tpm2_end_auth_session(struct tpm_chip *chip) > { > - tpm2_flush_context(chip, chip->auth->handle); > - memzero_explicit(chip->auth, sizeof(*chip->auth)); > + struct tpm2_auth *auth = chip->auth; > + > + tpm2_flush_context(chip, auth->handle); > + crypto_free_shash(auth->tfm); > + memzero_explicit(auth, sizeof(*auth)); > } > EXPORT_SYMBOL(tpm2_end_auth_session); > > @@ -833,23 +859,26 @@ static int tpm2_parse_start_auth_session(struct tpm2_auth *auth, > u32 tot_len = be32_to_cpu(head->length); > off_t offset = TPM_HEADER_SIZE; > u32 val; > + const int digest_size = crypto_shash_digestsize(auth->tfm); > + SHASH_DESC_ON_STACK(sdesc, auth->tfm); > + > + sdesc->tfm = auth->tfm; > > /* we're starting after the header so adjust the length */ > tot_len -= TPM_HEADER_SIZE; > > /* should have handle plus nonce */ > - if (tot_len != 4 + 2 + sizeof(auth->tpm_nonce)) > + if (tot_len != 4 + 2 + digest_size) > return -EINVAL; > > auth->handle = tpm_buf_read_u32(buf, &offset); > val = tpm_buf_read_u16(buf, &offset); > - if (val != sizeof(auth->tpm_nonce)) > + if (val != digest_size) > return -EINVAL; > - memcpy(auth->tpm_nonce, &buf->data[offset], sizeof(auth->tpm_nonce)); > + memcpy(auth->tpm_nonce, &buf->data[offset], digest_size); > /* now compute the session key from the nonces */ > tpm2_KDFa(auth->salt, sizeof(auth->salt), "ATH", auth->tpm_nonce, > - auth->our_nonce, sizeof(auth->session_key), > - auth->session_key); > + auth->our_nonce, digest_size, auth->session_key, sdesc); > > return 0; > } > @@ -885,24 +914,23 @@ static int tpm2_load_null(struct tpm_chip *chip, u32 *null_key) > return rc; > } > > -/** > - * tpm2_start_auth_session() - create a HMAC authentication session with the TPM > - * @chip: the TPM chip structure to create the session with > - * > - * This function loads the NULL seed from its saved context and starts > - * an authentication session on the null seed, fills in the > - * @chip->auth structure to contain all the session details necessary > - * for performing the HMAC, encrypt and decrypt operations and > - * returns. The NULL seed is flushed before this function returns. > - * > - * Return: zero on success or actual error encountered. > - */ > -int tpm2_start_auth_session(struct tpm_chip *chip) > +static int __tpm2_start_session(struct tpm_chip *chip, u8 type, u16 hash) > { > struct tpm_buf buf; > struct tpm2_auth *auth = chip->auth; > int rc; > u32 null_key; > + int tpm_hash = tpm2_crypto_to_alg(hash); > + int digest_size; > + > + if (tpm_hash < 0) > + return -EINVAL; > + > + auth->tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0); > + if (IS_ERR(auth->tfm)) > + return PTR_ERR(auth->tfm); > + > + digest_size = crypto_shash_digestsize(auth->tfm); > > rc = tpm2_load_null(chip, &null_key); > if (rc) > @@ -919,14 +947,14 @@ int tpm2_start_auth_session(struct tpm_chip *chip) > /* bind key handle */ > tpm_buf_append_u32(&buf, TPM2_RH_NULL); > /* nonce caller */ > - get_random_bytes(auth->our_nonce, sizeof(auth->our_nonce)); > - tpm_buf_append_u16(&buf, sizeof(auth->our_nonce)); > - tpm_buf_append(&buf, auth->our_nonce, sizeof(auth->our_nonce)); > + get_random_bytes(auth->our_nonce, digest_size); > + tpm_buf_append_u16(&buf, digest_size); > + tpm_buf_append(&buf, auth->our_nonce, digest_size); > > /* append encrypted salt and squirrel away unencrypted in auth */ > tpm_buf_append_salt(&buf, chip); > /* session type (HMAC, audit or policy) */ > - tpm_buf_append_u8(&buf, TPM2_SE_HMAC); > + tpm_buf_append_u8(&buf, type); > > /* symmetric encryption parameters */ > /* symmetric algorithm */ > @@ -936,7 +964,7 @@ int tpm2_start_auth_session(struct tpm_chip *chip) > /* symmetric algorithm mode (must be CFB) */ > tpm_buf_append_u16(&buf, TPM_ALG_CFB); > /* hash algorithm for session */ > - tpm_buf_append_u16(&buf, TPM_ALG_SHA256); > + tpm_buf_append_u16(&buf, tpm_hash); > > rc = tpm_transmit_cmd(chip, &buf, 0, "start auth session"); > tpm2_flush_context(chip, null_key); > @@ -949,11 +977,64 @@ int tpm2_start_auth_session(struct tpm_chip *chip) > if (rc) > goto out; > > + return rc; > + > out: > + crypto_free_shash(auth->tfm); > + auth->tfm = NULL; > + > return rc; > } > + > +/** > + * tpm2_start_auth_session() - create a HMAC authentication session with the TPM > + * @chip: the TPM chip structure to create the session with > + * > + * This function loads the NULL seed from its saved context and starts > + * an authentication session on the null seed, fills in the > + * @chip->auth structure to contain all the session details necessary > + * for performing the HMAC, encrypt and decrypt operations and > + * returns. The NULL seed is flushed before this function returns. > + * > + * Return: zero on success or actual error encountered. > + */ > +int tpm2_start_auth_session(struct tpm_chip *chip) > +{ > + return __tpm2_start_session(chip, TPM2_SE_HMAC, HASH_ALGO_SHA256); > +} > EXPORT_SYMBOL(tpm2_start_auth_session); > > +/** > + * tpm2_start_policy_session - create a policy session with the TPM > + * @chip: the TPM chip structure to create the session with > + * @handle: the policy session handle > + * @hash: the crypto subsystem hash algorithm for the policy > + * > + * This function loads the NULL seed from its saved context and starts > + * a policy session on the null seed, fills in the @chip->auth > + * structure to contain all the session details necessary for > + * performing the HMAC, encrypt and decrypt operations and returns. > + * The NULL seed is flushed before this function returns. > + * > + * Note the hash algorthim has to match the name algorithm of the TPM > + * object the policy will be used to authorize. > + * > + * Return: zero on success or actual error encountered. > + */ > +int tpm2_start_policy_session(struct tpm_chip *chip, u32 *handle, u8 hash) > +{ > + int rc; > + > + rc = __tpm2_start_session(chip, TPM2_SE_POLICY, hash); > + if (rc) > + return rc; > + > + *handle = chip->auth->handle; > + > + return rc; > +} > +EXPORT_SYMBOL(tpm2_start_policy_session); > + > /** > * tpm2_parse_create_primary() - parse the data returned from TPM_CC_CREATE_PRIMARY > * > diff --git a/include/linux/tpm.h b/include/linux/tpm.h > index 07f532456a0c..dc2dd98cf104 100644 > --- a/include/linux/tpm.h > +++ b/include/linux/tpm.h > @@ -560,6 +560,7 @@ static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle) > #ifdef CONFIG_TCG_TPM2_HMAC > > int tpm2_start_auth_session(struct tpm_chip *chip); > +int tpm2_start_policy_session(struct tpm_chip *chip, u32 *handle, u8 hash); > void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf, > u32 handle, u8 *name); > void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf, > @@ -585,6 +586,11 @@ static inline int tpm2_start_auth_session(struct tpm_chip *chip) > { > return 0; > } > +static inline int tpm2_start_policy_session(struct tpm_chip *chip, u32 *handle, > + u8 hash) > +{ > + return -EINVAL; > +} > static inline void tpm2_end_auth_session(struct tpm_chip *chip) > { > } BR, Jarkko
On Tue Jul 16, 2024 at 2:53 PM EEST, Jarkko Sakkinen wrote: > > - u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE]; > > + u8 name[AUTH_MAX_NAMES][2 + HASH_MAX_DIGESTSIZE]; Ouch, we definitely do not want 2-dimensional arrays. I missed this in the hmac review. Why this is based on count (AUTH_MAX_NAMES) rather than space? Is that value from the specs? You could just as well replace name and name_h with a single tpm_buf instance in "sized" mode and return -E2BIG from the functions that use it. Right, those don't return anything but void, which should be also fixed. BR, Jarkko
On Tue Jul 16, 2024 at 5:07 PM EEST, Jarkko Sakkinen wrote: > On Tue Jul 16, 2024 at 2:53 PM EEST, Jarkko Sakkinen wrote: > > > - u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE]; > > > + u8 name[AUTH_MAX_NAMES][2 + HASH_MAX_DIGESTSIZE]; > > Ouch, we definitely do not want 2-dimensional arrays. I missed this in > the hmac review. > > Why this is based on count (AUTH_MAX_NAMES) rather than space? Is that > value from the specs? > > You could just as well replace name and name_h with a single tpm_buf > instance in "sized" mode and return -E2BIG from the functions that use > it. Right, those don't return anything but void, which should be also > fixed. tpm_buf_write_u32() tpm_buf_write() tpm_buf_write_u32() tpm_buf_write() Two buffers stored. The read functions are non-destructive. Let's not invent ad-hoc crap when we have already a tested and legit tool for this. BR, Jarkko
On Tue Jul 16, 2024 at 5:08 PM EEST, Jarkko Sakkinen wrote: > On Tue Jul 16, 2024 at 5:07 PM EEST, Jarkko Sakkinen wrote: > > On Tue Jul 16, 2024 at 2:53 PM EEST, Jarkko Sakkinen wrote: > > > > - u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE]; > > > > + u8 name[AUTH_MAX_NAMES][2 + HASH_MAX_DIGESTSIZE]; > > > > Ouch, we definitely do not want 2-dimensional arrays. I missed this in > > the hmac review. > > > > Why this is based on count (AUTH_MAX_NAMES) rather than space? Is that > > value from the specs? > > > > You could just as well replace name and name_h with a single tpm_buf > > instance in "sized" mode and return -E2BIG from the functions that use > > it. Right, those don't return anything but void, which should be also > > fixed. > > tpm_buf_write_u32() > tpm_buf_write() > tpm_buf_write_u32() > tpm_buf_write() > > Two buffers stored. The read functions are non-destructive. Let's not > invent ad-hoc crap when we have already a tested and legit tool for > this. Other issues that I saw is that the patch set does not apply anymore but it is been two months so no wonder. For the next version you should also specify a test transcript that allows to test the functionality similarly as I've done for asymmetric keys: https://lore.kernel.org/linux-integrity/20240528210823.28798-1-jarkko@kernel.org/T/#mb07f85a8c3f4af388cbc08438e71ac8aea447d85 I don't want to invent the test case myself, and very few will do I'd figure. BR, Jarkko
On Tue, 2024-07-16 at 17:07 +0300, Jarkko Sakkinen wrote: > On Tue Jul 16, 2024 at 2:53 PM EEST, Jarkko Sakkinen wrote: > > > - u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE]; > > > + u8 name[AUTH_MAX_NAMES][2 + HASH_MAX_DIGESTSIZE]; > > Ouch, we definitely do not want 2-dimensional arrays. I missed this > in the hmac review. > > Why this is based on count (AUTH_MAX_NAMES) rather than space? Is > that value from the specs? Yes, it's based on the maximum number of session handles a command can have. It's architecturally defined in Trusted Platform Module Library Part 1: Architecture chapter 18 (TPM Command/Response Structure) where it says in 18.1 "an Authorization Area containing one to three session structures" Although if I look at our code we really only use a maximum of two for all the commands the kernel does. > You could just as well replace name and name_h with a single tpm_buf > instance in "sized" mode and return -E2BIG from the functions that > use it. Right, those don't return anything but void, which should be > also fixed. I'll look into that: it would get us out of the buf->handles spat. James
On Wed, 2024-07-17 at 22:30 -0400, James Bottomley wrote: > On Tue, 2024-07-16 at 17:07 +0300, Jarkko Sakkinen wrote: > > On Tue Jul 16, 2024 at 2:53 PM EEST, Jarkko Sakkinen wrote: > > > > - u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE]; > > > > + u8 name[AUTH_MAX_NAMES][2 + HASH_MAX_DIGESTSIZE]; > > > > Ouch, we definitely do not want 2-dimensional arrays. I missed this > > in the hmac review. > > > > Why this is based on count (AUTH_MAX_NAMES) rather than space? Is > > that value from the specs? > > Yes, it's based on the maximum number of session handles a command can > have. It's architecturally defined in Trusted Platform Module Library > Part 1: Architecture chapter 18 (TPM Command/Response Structure) where > it says in 18.1 "an Authorization Area containing one to three session > structures" > > Although if I look at our code we really only use a maximum of two for > all the commands the kernel does. > > > You could just as well replace name and name_h with a single tpm_buf > > instance in "sized" mode and return -E2BIG from the functions that > > use it. Right, those don't return anything but void, which should be > > also fixed. > > I'll look into that: it would get us out of the buf->handles spat. Also one thing I recalled after reviewing this: when updating any of the exported functions: 1. Try to scope patch per function. Obviously when sanely possible but at least goal should be this granularity. 2. It would make sense to take the documentation from header and kdoc and merge them into a single entity. This way I think it would be more digestable and easier both test and review. BR, Jarkko
On Fri, 2024-07-19 at 16:21 +0300, Jarkko Sakkinen wrote: > On Wed, 2024-07-17 at 22:30 -0400, James Bottomley wrote: > > On Tue, 2024-07-16 at 17:07 +0300, Jarkko Sakkinen wrote: > > > On Tue Jul 16, 2024 at 2:53 PM EEST, Jarkko Sakkinen wrote: > > > > > - u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE]; > > > > > + u8 name[AUTH_MAX_NAMES][2 + HASH_MAX_DIGESTSIZE]; > > > > > > Ouch, we definitely do not want 2-dimensional arrays. I missed this > > > in the hmac review. > > > > > > Why this is based on count (AUTH_MAX_NAMES) rather than space? Is > > > that value from the specs? > > > > Yes, it's based on the maximum number of session handles a command can > > have. It's architecturally defined in Trusted Platform Module Library > > Part 1: Architecture chapter 18 (TPM Command/Response Structure) where > > it says in 18.1 "an Authorization Area containing one to three session > > structures" > > > > Although if I look at our code we really only use a maximum of two for > > all the commands the kernel does. > > > > > You could just as well replace name and name_h with a single tpm_buf > > > instance in "sized" mode and return -E2BIG from the functions that > > > use it. Right, those don't return anything but void, which should be > > > also fixed. > > > > I'll look into that: it would get us out of the buf->handles spat. > > > Also one thing I recalled after reviewing this: when updating > any of the exported functions: > > 1. Try to scope patch per function. Obviously when sanely > possible but at least goal should be this granularity. > 2. It would make sense to take the documentation from header > and kdoc and merge them into a single entity. > > This way I think it would be more digestable and easier both > test and review. My philosophy here is like for any other new feature: 1. The first version can be messier, and all the issues I've complained about (mostly because I came from holiday to fix them) are kind of part of the job. 2. The second version or supplemental feature should make the code base cleaner than it was before it existed. So I kind of see the second iteration also a chance fix all the glitches and rough corners. I don't think anything was done "wrong" durign the first iteration, now it is just easier to reflect some of the choices. BR, Jarkko
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c index ea8860661876..63c175b2165c 100644 --- a/drivers/char/tpm/tpm2-sessions.c +++ b/drivers/char/tpm/tpm2-sessions.c @@ -105,10 +105,10 @@ struct tpm2_auth { /* * the size here is variable and set by the size of our_nonce * which must be between 16 and the name hash length. we set - * the maximum sha256 size for the greatest protection + * the maximum hash size for the greatest protection */ - u8 our_nonce[SHA256_DIGEST_SIZE]; - u8 tpm_nonce[SHA256_DIGEST_SIZE]; + u8 our_nonce[HASH_MAX_DIGESTSIZE]; + u8 tpm_nonce[HASH_MAX_DIGESTSIZE]; /* * the salt is only used across the session command/response * after that it can be used as a scratch area @@ -119,17 +119,15 @@ struct tpm2_auth { u8 scratch[AES_KEY_BYTES + AES_BLOCK_SIZE]; }; /* - * the session key and passphrase are the same size as the - * name digest (sha256 again). The session key is constant - * for the use of the session and the passphrase can change - * with every invocation. - * - * Note: these fields must be adjacent and in this order - * because several HMAC/KDF schemes use the combination of the - * session_key and passphrase. + * The session_key and passphrase must be next to each other + * to allow us to hash them as a unit. With a variable hash, + * this means that the passphrase must occur exactly one hash + * size after the session_key, so make session_key house both + * and passphrase is a pointer into where the passphrase + * begins in session_key. */ - u8 session_key[SHA256_DIGEST_SIZE]; - u8 passphrase[SHA256_DIGEST_SIZE]; + u8 session_key[2*HASH_MAX_DIGESTSIZE]; + u8 *passphrase; int passphrase_len; struct crypto_aes_ctx aes_ctx; /* saved session attributes: */ @@ -142,7 +140,8 @@ struct tpm2_auth { * we must compute and remember */ u32 name_h[AUTH_MAX_NAMES]; - u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE]; + u8 name[AUTH_MAX_NAMES][2 + HASH_MAX_DIGESTSIZE]; + struct crypto_shash *tfm; }; /* @@ -161,34 +160,36 @@ static u8 name_size(const u8 *name) } /* - * It turns out the crypto hmac(sha256) is hard for us to consume + * It turns out the crypto hmac(shaX) is hard for us to consume * because it assumes a fixed key and the TPM seems to change the key * on every operation, so we weld the hmac init and final functions in * here to give it the same usage characteristics as a regular hash */ -static void tpm2_hmac_init(struct sha256_state *sctx, u8 *key, u32 key_len) +static void tpm2_hmac_init(struct shash_desc *sdesc, u8 *key, u32 key_len) { - u8 pad[SHA256_BLOCK_SIZE]; + const int block_size = crypto_shash_blocksize(sdesc->tfm); + u8 pad[SHA512_BLOCK_SIZE]; int i; - sha256_init(sctx); - for (i = 0; i < sizeof(pad); i++) { + crypto_shash_init(sdesc); + for (i = 0; i < block_size; i++) { if (i < key_len) pad[i] = key[i]; else pad[i] = 0; pad[i] ^= HMAC_IPAD_VALUE; } - sha256_update(sctx, pad, sizeof(pad)); + crypto_shash_update(sdesc, pad, block_size); } -static void tpm2_hmac_final(struct sha256_state *sctx, u8 *key, u32 key_len, +static void tpm2_hmac_final(struct shash_desc *sdesc, u8 *key, u32 key_len, u8 *out) { - u8 pad[SHA256_BLOCK_SIZE]; + const int block_size = crypto_shash_blocksize(sdesc->tfm); + u8 pad[SHA512_BLOCK_SIZE]; int i; - for (i = 0; i < sizeof(pad); i++) { + for (i = 0; i < block_size; i++) { if (i < key_len) pad[i] = key[i]; else @@ -197,35 +198,42 @@ static void tpm2_hmac_final(struct sha256_state *sctx, u8 *key, u32 key_len, } /* collect the final hash; use out as temporary storage */ - sha256_final(sctx, out); + crypto_shash_final(sdesc, out); - sha256_init(sctx); - sha256_update(sctx, pad, sizeof(pad)); - sha256_update(sctx, out, SHA256_DIGEST_SIZE); - sha256_final(sctx, out); + crypto_shash_init(sdesc); + crypto_shash_update(sdesc, pad, block_size); + crypto_shash_update(sdesc, out, crypto_shash_digestsize(sdesc->tfm)); + crypto_shash_final(sdesc, out); } /* - * assume hash sha256 and nonces u, v of size SHA256_DIGEST_SIZE but + * assume hash sha256 and nonces u, v of hash digest size but * otherwise standard tpm2_KDFa. Note output is in bytes not bits. */ static void tpm2_KDFa(u8 *key, u32 key_len, const char *label, u8 *u, - u8 *v, u32 bytes, u8 *out) + u8 *v, u32 bytes, u8 *out, struct shash_desc *sdesc) { u32 counter = 1; const __be32 bits = cpu_to_be32(bytes * 8); + const int digest_size = crypto_shash_digestsize(sdesc->tfm); while (bytes > 0) { - struct sha256_state sctx; __be32 c = cpu_to_be32(counter); - tpm2_hmac_init(&sctx, key, key_len); - sha256_update(&sctx, (u8 *)&c, sizeof(c)); - sha256_update(&sctx, label, strlen(label)+1); - sha256_update(&sctx, u, SHA256_DIGEST_SIZE); - sha256_update(&sctx, v, SHA256_DIGEST_SIZE); - sha256_update(&sctx, (u8 *)&bits, sizeof(bits)); - tpm2_hmac_final(&sctx, key, key_len, out); + tpm2_hmac_init(sdesc, key, key_len); + crypto_shash_update(sdesc, (u8 *)&c, sizeof(c)); + crypto_shash_update(sdesc, label, strlen(label)+1); + crypto_shash_update(sdesc, u, digest_size); + crypto_shash_update(sdesc, v, digest_size); + crypto_shash_update(sdesc, (u8 *)&bits, sizeof(bits)); + if (bytes < digest_size) { + u8 buf[HASH_MAX_DIGESTSIZE]; + + tpm2_hmac_final(sdesc, key, key_len, buf); + memcpy(out, buf, bytes); + } else { + tpm2_hmac_final(sdesc, key, key_len, out); + } bytes -= SHA256_DIGEST_SIZE; counter++; @@ -236,9 +244,9 @@ static void tpm2_KDFa(u8 *key, u32 key_len, const char *label, u8 *u, /* * Somewhat of a bastardization of the real KDFe. We're assuming * we're working with known point sizes for the input parameters and - * the hash algorithm is fixed at sha256. Because we know that the - * point size is 32 bytes like the hash size, there's no need to loop - * in this KDF. + * the hash algorithm is fixed at sha256 (name algorithm of the + * encrypting key). Because we know that the point size is 32 bytes + * like the hash size, there's no need to loop in this KDF. */ static void tpm2_KDFe(u8 z[EC_PT_SZ], const char *str, u8 *pt_u, u8 *pt_v, u8 *out) @@ -370,9 +378,10 @@ void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf, u8 attributes, u8 *passphrase, int passphrase_len) { - u8 nonce[SHA256_DIGEST_SIZE]; + u8 nonce[HASH_MAX_DIGESTSIZE]; u32 len; struct tpm2_auth *auth = chip->auth; + const int digest_size = crypto_shash_digestsize(auth->tfm); /* * The Architecture Guide requires us to strip trailing zeros @@ -384,8 +393,14 @@ void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf, auth->attrs = attributes; auth->passphrase_len = passphrase_len; - if (passphrase_len) + if (passphrase_len) { + /* + * place the passphrase immediately adjacent to + * the session key + */ + auth->passphrase = auth->session_key + digest_size; memcpy(auth->passphrase, passphrase, passphrase_len); + } if (auth->session != tpm_buf_length(buf)) { /* we're not the first session */ @@ -396,23 +411,23 @@ void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf, } /* add our new session */ - len += 9 + 2 * SHA256_DIGEST_SIZE; + len += 9 + 2 * digest_size; put_unaligned_be32(len, &buf->data[auth->session]); } else { - tpm_buf_append_u32(buf, 9 + 2 * SHA256_DIGEST_SIZE); + tpm_buf_append_u32(buf, 9 + 2 * digest_size); } /* random number for our nonce */ - get_random_bytes(nonce, sizeof(nonce)); - memcpy(auth->our_nonce, nonce, sizeof(nonce)); + get_random_bytes(nonce, digest_size); + memcpy(auth->our_nonce, nonce, digest_size); tpm_buf_append_u32(buf, auth->handle); /* our new nonce */ - tpm_buf_append_u16(buf, SHA256_DIGEST_SIZE); - tpm_buf_append(buf, nonce, SHA256_DIGEST_SIZE); + tpm_buf_append_u16(buf, digest_size); + tpm_buf_append(buf, nonce, digest_size); tpm_buf_append_u8(buf, auth->attrs); /* and put a placeholder for the hmac */ - tpm_buf_append_u16(buf, SHA256_DIGEST_SIZE); - tpm_buf_append(buf, nonce, SHA256_DIGEST_SIZE); + tpm_buf_append_u16(buf, digest_size); + tpm_buf_append(buf, nonce, digest_size); } EXPORT_SYMBOL(tpm_buf_append_hmac_session); @@ -443,8 +458,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf) off_t offset_s = TPM_HEADER_SIZE, offset_p; u8 *hmac = NULL; u32 attrs; - u8 cphash[SHA256_DIGEST_SIZE]; - struct sha256_state sctx; + const int digest_size = crypto_shash_digestsize(auth->tfm); + u8 cphash[HASH_MAX_DIGESTSIZE]; + SHASH_DESC_ON_STACK(sdesc, auth->tfm); + + sdesc->tfm = auth->tfm; /* save the command code in BE format */ auth->ordinal = head->ordinal; @@ -515,10 +533,10 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf) u16 len; /* need key and IV */ - tpm2_KDFa(auth->session_key, SHA256_DIGEST_SIZE + tpm2_KDFa(auth->session_key, digest_size + auth->passphrase_len, "CFB", auth->our_nonce, auth->tpm_nonce, AES_KEY_BYTES + AES_BLOCK_SIZE, - auth->scratch); + auth->scratch, sdesc); len = tpm_buf_read_u16(buf, &offset_p); aes_expandkey(&auth->aes_ctx, auth->scratch, AES_KEY_BYTES); @@ -529,9 +547,10 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf) offset_p -= 2; } - sha256_init(&sctx); + crypto_shash_init(sdesc); /* ordinal is already BE */ - sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal)); + crypto_shash_update(sdesc, (u8 *)&head->ordinal, + sizeof(head->ordinal)); /* add the handle names */ for (i = 0; i < handles; i++) { enum tpm2_mso_type mso = tpm2_handle_mso(auth->name_h[i]); @@ -539,27 +558,27 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf) if (mso == TPM2_MSO_PERSISTENT || mso == TPM2_MSO_VOLATILE || mso == TPM2_MSO_NVRAM) { - sha256_update(&sctx, auth->name[i], - name_size(auth->name[i])); + crypto_shash_update(sdesc, auth->name[i], + name_size(auth->name[i])); } else { __be32 h = cpu_to_be32(auth->name_h[i]); - sha256_update(&sctx, (u8 *)&h, 4); + crypto_shash_update(sdesc, (u8 *)&h, 4); } } if (offset_s != tpm_buf_length(buf)) - sha256_update(&sctx, &buf->data[offset_s], - tpm_buf_length(buf) - offset_s); - sha256_final(&sctx, cphash); + crypto_shash_update(sdesc, &buf->data[offset_s], + tpm_buf_length(buf) - offset_s); + crypto_shash_final(sdesc, cphash); /* now calculate the hmac */ - tpm2_hmac_init(&sctx, auth->session_key, sizeof(auth->session_key) + tpm2_hmac_init(sdesc, auth->session_key, digest_size + auth->passphrase_len); - sha256_update(&sctx, cphash, sizeof(cphash)); - sha256_update(&sctx, auth->our_nonce, sizeof(auth->our_nonce)); - sha256_update(&sctx, auth->tpm_nonce, sizeof(auth->tpm_nonce)); - sha256_update(&sctx, &auth->attrs, 1); - tpm2_hmac_final(&sctx, auth->session_key, sizeof(auth->session_key) + crypto_shash_update(sdesc, cphash, digest_size); + crypto_shash_update(sdesc, auth->our_nonce, digest_size); + crypto_shash_update(sdesc, auth->tpm_nonce, digest_size); + crypto_shash_update(sdesc, &auth->attrs, 1); + tpm2_hmac_final(sdesc, auth->session_key, digest_size + auth->passphrase_len, hmac); } EXPORT_SYMBOL(tpm_buf_fill_hmac_session); @@ -695,12 +714,15 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf, struct tpm_header *head = (struct tpm_header *)buf->data; struct tpm2_auth *auth = chip->auth; off_t offset_s, offset_p; - u8 rphash[SHA256_DIGEST_SIZE]; + const int digest_size = crypto_shash_digestsize(auth->tfm); + u8 rphash[HASH_MAX_DIGESTSIZE]; u32 attrs; - struct sha256_state sctx; u16 tag = be16_to_cpu(head->tag); u32 cc = be32_to_cpu(auth->ordinal); int parm_len, len, i, handles; + SHASH_DESC_ON_STACK(sdesc, auth->tfm); + + sdesc->tfm = auth->tfm; if (auth->session >= TPM_HEADER_SIZE) { WARN(1, "tpm session not filled correctly\n"); @@ -739,7 +761,7 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf, len = tpm_buf_read_u16(buf, &offset_s); if (offset_s + len > tpm_buf_length(buf)) goto out; - if (len != SHA256_DIGEST_SIZE) + if (len != digest_size) goto out; memcpy(auth->tpm_nonce, &buf->data[offset_s], len); offset_s += len; @@ -747,32 +769,32 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf, len = tpm_buf_read_u16(buf, &offset_s); if (offset_s + len != tpm_buf_length(buf)) goto out; - if (len != SHA256_DIGEST_SIZE) + if (len != digest_size) goto out; /* * offset_s points to the HMAC. now calculate comparison, beginning * with rphash */ - sha256_init(&sctx); + crypto_shash_init(sdesc); /* yes, I know this is now zero, but it's what the standard says */ - sha256_update(&sctx, (u8 *)&head->return_code, - sizeof(head->return_code)); + crypto_shash_update(sdesc, (u8 *)&head->return_code, + sizeof(head->return_code)); /* ordinal is already BE */ - sha256_update(&sctx, (u8 *)&auth->ordinal, sizeof(auth->ordinal)); - sha256_update(&sctx, &buf->data[offset_p], parm_len); - sha256_final(&sctx, rphash); + crypto_shash_update(sdesc, (u8 *)&auth->ordinal, sizeof(auth->ordinal)); + crypto_shash_update(sdesc, &buf->data[offset_p], parm_len); + crypto_shash_final(sdesc, rphash); /* now calculate the hmac */ - tpm2_hmac_init(&sctx, auth->session_key, sizeof(auth->session_key) + tpm2_hmac_init(sdesc, auth->session_key, digest_size + auth->passphrase_len); - sha256_update(&sctx, rphash, sizeof(rphash)); - sha256_update(&sctx, auth->tpm_nonce, sizeof(auth->tpm_nonce)); - sha256_update(&sctx, auth->our_nonce, sizeof(auth->our_nonce)); - sha256_update(&sctx, &auth->attrs, 1); + crypto_shash_update(sdesc, rphash, digest_size); + crypto_shash_update(sdesc, auth->tpm_nonce, digest_size); + crypto_shash_update(sdesc, auth->our_nonce, digest_size); + crypto_shash_update(sdesc, &auth->attrs, 1); /* we're done with the rphash, so put our idea of the hmac there */ - tpm2_hmac_final(&sctx, auth->session_key, sizeof(auth->session_key) + tpm2_hmac_final(sdesc, auth->session_key, digest_size + auth->passphrase_len, rphash); - if (memcmp(rphash, &buf->data[offset_s], SHA256_DIGEST_SIZE) == 0) { + if (memcmp(rphash, &buf->data[offset_s], digest_size) == 0) { rc = 0; } else { dev_err(&chip->dev, "TPM: HMAC check failed\n"); @@ -782,10 +804,10 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf, /* now do response decryption */ if (auth->attrs & TPM2_SA_ENCRYPT) { /* need key and IV */ - tpm2_KDFa(auth->session_key, SHA256_DIGEST_SIZE + tpm2_KDFa(auth->session_key, digest_size + auth->passphrase_len, "CFB", auth->tpm_nonce, auth->our_nonce, AES_KEY_BYTES + AES_BLOCK_SIZE, - auth->scratch); + auth->scratch, sdesc); len = tpm_buf_read_u16(buf, &offset_p); aes_expandkey(&auth->aes_ctx, auth->scratch, AES_KEY_BYTES); @@ -799,6 +821,7 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf, if (rc) /* manually close the session if it wasn't consumed */ tpm2_flush_context(chip, auth->handle); + crypto_free_shash(auth->tfm); memzero_explicit(auth, sizeof(*auth)); } else { /* reset for next use */ @@ -821,8 +844,11 @@ EXPORT_SYMBOL(tpm_buf_check_hmac_response); */ void tpm2_end_auth_session(struct tpm_chip *chip) { - tpm2_flush_context(chip, chip->auth->handle); - memzero_explicit(chip->auth, sizeof(*chip->auth)); + struct tpm2_auth *auth = chip->auth; + + tpm2_flush_context(chip, auth->handle); + crypto_free_shash(auth->tfm); + memzero_explicit(auth, sizeof(*auth)); } EXPORT_SYMBOL(tpm2_end_auth_session); @@ -833,23 +859,26 @@ static int tpm2_parse_start_auth_session(struct tpm2_auth *auth, u32 tot_len = be32_to_cpu(head->length); off_t offset = TPM_HEADER_SIZE; u32 val; + const int digest_size = crypto_shash_digestsize(auth->tfm); + SHASH_DESC_ON_STACK(sdesc, auth->tfm); + + sdesc->tfm = auth->tfm; /* we're starting after the header so adjust the length */ tot_len -= TPM_HEADER_SIZE; /* should have handle plus nonce */ - if (tot_len != 4 + 2 + sizeof(auth->tpm_nonce)) + if (tot_len != 4 + 2 + digest_size) return -EINVAL; auth->handle = tpm_buf_read_u32(buf, &offset); val = tpm_buf_read_u16(buf, &offset); - if (val != sizeof(auth->tpm_nonce)) + if (val != digest_size) return -EINVAL; - memcpy(auth->tpm_nonce, &buf->data[offset], sizeof(auth->tpm_nonce)); + memcpy(auth->tpm_nonce, &buf->data[offset], digest_size); /* now compute the session key from the nonces */ tpm2_KDFa(auth->salt, sizeof(auth->salt), "ATH", auth->tpm_nonce, - auth->our_nonce, sizeof(auth->session_key), - auth->session_key); + auth->our_nonce, digest_size, auth->session_key, sdesc); return 0; } @@ -885,24 +914,23 @@ static int tpm2_load_null(struct tpm_chip *chip, u32 *null_key) return rc; } -/** - * tpm2_start_auth_session() - create a HMAC authentication session with the TPM - * @chip: the TPM chip structure to create the session with - * - * This function loads the NULL seed from its saved context and starts - * an authentication session on the null seed, fills in the - * @chip->auth structure to contain all the session details necessary - * for performing the HMAC, encrypt and decrypt operations and - * returns. The NULL seed is flushed before this function returns. - * - * Return: zero on success or actual error encountered. - */ -int tpm2_start_auth_session(struct tpm_chip *chip) +static int __tpm2_start_session(struct tpm_chip *chip, u8 type, u16 hash) { struct tpm_buf buf; struct tpm2_auth *auth = chip->auth; int rc; u32 null_key; + int tpm_hash = tpm2_crypto_to_alg(hash); + int digest_size; + + if (tpm_hash < 0) + return -EINVAL; + + auth->tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0); + if (IS_ERR(auth->tfm)) + return PTR_ERR(auth->tfm); + + digest_size = crypto_shash_digestsize(auth->tfm); rc = tpm2_load_null(chip, &null_key); if (rc) @@ -919,14 +947,14 @@ int tpm2_start_auth_session(struct tpm_chip *chip) /* bind key handle */ tpm_buf_append_u32(&buf, TPM2_RH_NULL); /* nonce caller */ - get_random_bytes(auth->our_nonce, sizeof(auth->our_nonce)); - tpm_buf_append_u16(&buf, sizeof(auth->our_nonce)); - tpm_buf_append(&buf, auth->our_nonce, sizeof(auth->our_nonce)); + get_random_bytes(auth->our_nonce, digest_size); + tpm_buf_append_u16(&buf, digest_size); + tpm_buf_append(&buf, auth->our_nonce, digest_size); /* append encrypted salt and squirrel away unencrypted in auth */ tpm_buf_append_salt(&buf, chip); /* session type (HMAC, audit or policy) */ - tpm_buf_append_u8(&buf, TPM2_SE_HMAC); + tpm_buf_append_u8(&buf, type); /* symmetric encryption parameters */ /* symmetric algorithm */ @@ -936,7 +964,7 @@ int tpm2_start_auth_session(struct tpm_chip *chip) /* symmetric algorithm mode (must be CFB) */ tpm_buf_append_u16(&buf, TPM_ALG_CFB); /* hash algorithm for session */ - tpm_buf_append_u16(&buf, TPM_ALG_SHA256); + tpm_buf_append_u16(&buf, tpm_hash); rc = tpm_transmit_cmd(chip, &buf, 0, "start auth session"); tpm2_flush_context(chip, null_key); @@ -949,11 +977,64 @@ int tpm2_start_auth_session(struct tpm_chip *chip) if (rc) goto out; + return rc; + out: + crypto_free_shash(auth->tfm); + auth->tfm = NULL; + return rc; } + +/** + * tpm2_start_auth_session() - create a HMAC authentication session with the TPM + * @chip: the TPM chip structure to create the session with + * + * This function loads the NULL seed from its saved context and starts + * an authentication session on the null seed, fills in the + * @chip->auth structure to contain all the session details necessary + * for performing the HMAC, encrypt and decrypt operations and + * returns. The NULL seed is flushed before this function returns. + * + * Return: zero on success or actual error encountered. + */ +int tpm2_start_auth_session(struct tpm_chip *chip) +{ + return __tpm2_start_session(chip, TPM2_SE_HMAC, HASH_ALGO_SHA256); +} EXPORT_SYMBOL(tpm2_start_auth_session); +/** + * tpm2_start_policy_session - create a policy session with the TPM + * @chip: the TPM chip structure to create the session with + * @handle: the policy session handle + * @hash: the crypto subsystem hash algorithm for the policy + * + * This function loads the NULL seed from its saved context and starts + * a policy session on the null seed, fills in the @chip->auth + * structure to contain all the session details necessary for + * performing the HMAC, encrypt and decrypt operations and returns. + * The NULL seed is flushed before this function returns. + * + * Note the hash algorthim has to match the name algorithm of the TPM + * object the policy will be used to authorize. + * + * Return: zero on success or actual error encountered. + */ +int tpm2_start_policy_session(struct tpm_chip *chip, u32 *handle, u8 hash) +{ + int rc; + + rc = __tpm2_start_session(chip, TPM2_SE_POLICY, hash); + if (rc) + return rc; + + *handle = chip->auth->handle; + + return rc; +} +EXPORT_SYMBOL(tpm2_start_policy_session); + /** * tpm2_parse_create_primary() - parse the data returned from TPM_CC_CREATE_PRIMARY * diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 07f532456a0c..dc2dd98cf104 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -560,6 +560,7 @@ static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle) #ifdef CONFIG_TCG_TPM2_HMAC int tpm2_start_auth_session(struct tpm_chip *chip); +int tpm2_start_policy_session(struct tpm_chip *chip, u32 *handle, u8 hash); void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf, u32 handle, u8 *name); void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf, @@ -585,6 +586,11 @@ static inline int tpm2_start_auth_session(struct tpm_chip *chip) { return 0; } +static inline int tpm2_start_policy_session(struct tpm_chip *chip, u32 *handle, + u8 hash) +{ + return -EINVAL; +} static inline void tpm2_end_auth_session(struct tpm_chip *chip) { }
TPM security uses hmac sessions to implement data protection and integrity. The hash algorithm of these sessions isn't exposed to the user (and doesn't depend on the object being authorized) so it is currently fixed at a safe sha256. Policy sessions may also be used for the same purpose and in addition, they can be used to express a rich policy language. However, the session hash of policy sessions is defined to be the same as the name algorithm of the TPM object they're operating on, so we must update the session code to allow a variable hash algorithm instead of the fixed sha256 one. Note that while this affects most of the code, the KDFe algorithm still uses a fixed sha256 algorithm because it is define to follow the name algorithm of the salt encryption key (which is a key we derive from the null seed and set its name algorithm to sha256). Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> --- drivers/char/tpm/tpm2-sessions.c | 307 +++++++++++++++++++------------ include/linux/tpm.h | 6 + 2 files changed, 200 insertions(+), 113 deletions(-)