From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0E33C2D73AD; Sun, 7 Dec 2025 17:32:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765128751; cv=none; b=VE1obtJ+sh5mrCqZO91jgNFTRYjpoWPbszfpa9s8/IcsiVP8Yu+w/ORE9ye6wK8f7z7XUhSQNGKNkeFfswfUUgqacDC31bcU0IwsirXk4Qz81Wpat7fAH0sL01OJZkLcVMlvUUTYHbHXe3HUyCZkF0GyPYUncm1xvYiwncZGoBA= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765128751; c=relaxed/simple; bh=0WuiOVdp45K5ksQqcWAvpJF8apRTucmYUxOoIcX6Cw8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=BI5mmPrLHm2i86c+l18Fato0FvIuaVB8182D8kmvRbsU4KowDWxCayrbYqsGpRUx5yiruC390ll+FdkU6KD1XykaW3+KZr8RbAwewSNrdMaoZoS0VnmeAtlzyZADfuUmzS+8iAwDhVKIoLYLen230hb2sde4y0IrZyA+DztZdDI= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=GSbGsTGm; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="GSbGsTGm" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 02FCCC4CEFB; Sun, 7 Dec 2025 17:32:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1765128750; bh=0WuiOVdp45K5ksQqcWAvpJF8apRTucmYUxOoIcX6Cw8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=GSbGsTGmOO1Mf+MuEcjFLfyYka+YEQrOOtkFwNrJzgbAWOWM4FIuDJQ0sa02jA5/c kdqZZGuS3XjF6JOyQlhfr6FhnhFAWSpCJc5vkGGvpXNHWP3452pyE1a/GnoYDm+y3i ZWG46UrtmZpcIhJHYwiOn4D3Apb8Fabl92U09Cw0V3Ry7xFH5emeB/XeQ0pBeNs60r kDyLsKgUb0jhZ0tZvTUd074TwfWaTaHHbv5zQjZg+Y5Gwl72GAph+Y+CPHWhiItbgf RRKvXEgk64lZIG4lZao0Ta9eGctD7PxeDIEQRFI+g/hakz1Te0LNdTn5RpYduY5f7u FM63ejwJKWk8w== From: Jarkko Sakkinen To: linux-integrity@vger.kernel.org Cc: tpm2@lists.linux.dev, Jarkko Sakkinen , Peter Huewe , Jason Gunthorpe , James Bottomley , Mimi Zohar , David Howells , Paul Moore , James Morris , "Serge E. Hallyn" , linux-kernel@vger.kernel.org (open list), keyrings@vger.kernel.org (open list:KEYS-TRUSTED), linux-security-module@vger.kernel.org (open list:SECURITY SUBSYSTEM) Subject: [PATCH v6 2/2] KEYS: trusted: Store parent's name to the encoded keys Date: Sun, 7 Dec 2025 19:32:10 +0200 Message-Id: <20251207173210.93765-3-jarkko@kernel.org> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20251207173210.93765-1-jarkko@kernel.org> References: <20251207173210.93765-1-jarkko@kernel.org> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Extend TPMKey ASN.1 speciication [1] with an optional 'parentName' attribute containing TPM name of the parent key (in other words, TPMT_HA blob). The life-cycle for trusted keys will now proceed as follows: 1. Encode parent's name to the 'paretName' during tpm2_key_encode(). 2. During tpm2_unseal_trusted, read parent's name from 'parentName'. When the attribute is not available, fallback on doing tpm2_read_public(). In other words, in the common (i.e., not loading a legacy key blob), tpm2_read_public() will now only happen at the time when a key is first created. In addition, move tpm2_read_public() to 'tpm2-cmd.c' and make its body unconditional so that the binary format of the saved keys is not dependent on kernel configuration. [1] https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.txt Cc: tpm2@lists.linux.dev Signed-off-by: Jarkko Sakkinen --- v6: - Addressed the issues reported by James. Updated tag index to '7', as '6' is reserved by "creation data proposal" to which I could not find a reference but presumably exists: https://lore.kernel.org/linux-integrity/89d90617ba9b7a5eff1d5fad6bb9773033d3c18c.camel@HansenPartnership.com/#t Once I become aware of the mailing list of IETF WG for TPM2 ASN.1 key spec, so could not cc to patch over there buth at least the conflict has been addressed. I can add the list ass CC one I know th address. v5: - Rebased to the mainline. v4: - No changes. v3: - Fixed three low-hanging fruit issues I found myself. v2: - A new patch. --- drivers/char/tpm/tpm2-cmd.c | 83 ++++++++++++++++++++ drivers/char/tpm/tpm2-sessions.c | 85 --------------------- include/linux/tpm.h | 9 +-- security/keys/trusted-keys/tpm2key.asn1 | 17 ++++- security/keys/trusted-keys/trusted_tpm2.c | 93 ++++++++++++++++------- 5 files changed, 166 insertions(+), 121 deletions(-) diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 5b04e74b6377..83a70680cfe9 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -757,3 +757,86 @@ int tpm2_find_cc(struct tpm_chip *chip, u32 cc) return -1; } + +/* + * Name Size based on TPM algorithm (assumes no hash bigger than 255) + */ +static int name_size(const u8 *name) +{ + u16 hash_alg = get_unaligned_be16(name); + + switch (hash_alg) { + case TPM_ALG_SHA1: + return SHA1_DIGEST_SIZE + 2; + case TPM_ALG_SHA256: + return SHA256_DIGEST_SIZE + 2; + case TPM_ALG_SHA384: + return SHA384_DIGEST_SIZE + 2; + case TPM_ALG_SHA512: + return SHA512_DIGEST_SIZE + 2; + default: + pr_warn("tpm: unsupported name algorithm: 0x%04x\n", hash_alg); + return -EINVAL; + } +} + +/** + * tpm2_read_public: Resolve TPM name for a handle + * @chip: TPM chip to use. + * @handle: TPM handle. + * @name: A buffer for returning the name blob. Must have a + * capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum + * + * Returns size of TPM handle name of success. + * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails. + */ +int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name) +{ + u32 mso = tpm2_handle_mso(handle); + off_t offset = TPM_HEADER_SIZE; + int rc, name_size_alg; + + if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE && + mso != TPM2_MSO_NVRAM) { + memcpy(name, &handle, sizeof(u32)); + return sizeof(u32); + } + + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + tpm_buf_init(buf, TPM_BUFSIZE); + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC); + tpm_buf_append_u32(buf, handle); + + rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_ReadPublic"); + if (rc) + return tpm_ret_to_err(rc); + + /* Skip TPMT_PUBLIC: */ + offset += tpm_buf_read_u16(buf, &offset); + + /* + * Ensure space for the length field of TPM2B_NAME and hashAlg field of + * TPMT_HA (the extra four bytes). + */ + if (offset + 4 > tpm_buf_length(buf)) + return -EIO; + + rc = tpm_buf_read_u16(buf, &offset); + name_size_alg = name_size(&buf->data[offset]); + + if (name_size_alg < 0) + return name_size_alg; + + if (rc != name_size_alg) + return -EIO; + + if (offset + rc > tpm_buf_length(buf)) + return -EIO; + + memcpy(name, &buf->data[offset], rc); + return name_size_alg; +} +EXPORT_SYMBOL_GPL(tpm2_read_public); diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c index 6b7112a7c30c..daf78c2e905f 100644 --- a/drivers/char/tpm/tpm2-sessions.c +++ b/drivers/char/tpm/tpm2-sessions.c @@ -140,91 +140,6 @@ struct tpm2_auth { u16 name_size_tbl[AUTH_MAX_NAMES]; }; -#ifdef CONFIG_TCG_TPM2_HMAC -/* - * Name Size based on TPM algorithm (assumes no hash bigger than 255) - */ -static int name_size(const u8 *name) -{ - u16 hash_alg = get_unaligned_be16(name); - - switch (hash_alg) { - case TPM_ALG_SHA1: - return SHA1_DIGEST_SIZE + 2; - case TPM_ALG_SHA256: - return SHA256_DIGEST_SIZE + 2; - case TPM_ALG_SHA384: - return SHA384_DIGEST_SIZE + 2; - case TPM_ALG_SHA512: - return SHA512_DIGEST_SIZE + 2; - default: - pr_warn("tpm: unsupported name algorithm: 0x%04x\n", hash_alg); - return -EINVAL; - } -} - -/** - * tpm2_read_public: Resolve TPM name for a handle - * @chip: TPM chip to use. - * @handle: TPM handle. - * @name: A buffer for returning the name blob. Must have a - * capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum - * - * Returns size of TPM handle name of success. - * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails. - */ -int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name) -{ - u32 mso = tpm2_handle_mso(handle); - off_t offset = TPM_HEADER_SIZE; - int rc, name_size_alg; - - if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE && - mso != TPM2_MSO_NVRAM) { - memcpy(name, &handle, sizeof(u32)); - return sizeof(u32); - } - - struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - tpm_buf_init(buf, TPM_BUFSIZE); - tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC); - tpm_buf_append_u32(buf, handle); - - rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_ReadPublic"); - if (rc) - return tpm_ret_to_err(rc); - - /* Skip TPMT_PUBLIC: */ - offset += tpm_buf_read_u16(buf, &offset); - - /* - * Ensure space for the length field of TPM2B_NAME and hashAlg field of - * TPMT_HA (the extra four bytes). - */ - if (offset + 4 > tpm_buf_length(buf)) - return -EIO; - - rc = tpm_buf_read_u16(buf, &offset); - name_size_alg = name_size(&buf->data[offset]); - - if (name_size_alg < 0) - return name_size_alg; - - if (rc != name_size_alg) - return -EIO; - - if (offset + rc > tpm_buf_length(buf)) - return -EIO; - - memcpy(name, &buf->data[offset], rc); - return name_size_alg; -} -EXPORT_SYMBOL_GPL(tpm2_read_public); -#endif /* CONFIG_TCG_TPM2_HMAC */ - /** * tpm_buf_append_name() - Append a handle and store TPM name * @chip: TPM chip to use. diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 3f1d8c27394b..40d1ede005b0 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -478,6 +478,7 @@ 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); int tpm2_find_hash_alg(unsigned int crypto_id); +int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name); static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle) { @@ -544,7 +545,6 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf); int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf, int rc); void tpm2_end_auth_session(struct tpm_chip *chip); -int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name); #else #include @@ -568,13 +568,6 @@ static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip, { return rc; } - -static inline int tpm2_read_public(struct tpm_chip *chip, u32 handle, - void *name) -{ - memcpy(name, &handle, sizeof(u32)); - return sizeof(u32); -} #endif /* CONFIG_TCG_TPM2_HMAC */ #endif diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1 index f57f869ad600..c48ea5a14219 100644 --- a/security/keys/trusted-keys/tpm2key.asn1 +++ b/security/keys/trusted-keys/tpm2key.asn1 @@ -1,11 +1,26 @@ --- --- ASN.1 for TPM 2.0 keys --- +TPMPolicy ::= SEQUENCE { + commandCode [0] EXPLICIT INTEGER, + commandPolicy [1] EXPLICIT OCTET STRING +} + +TPMAuthPolicy ::= SEQUENCE { + name [0] EXPLICIT UTF8String OPTIONAL, + policy [1] EXPLICIT SEQUENCE OF TPMPolicy +} TPMKey ::= SEQUENCE { type OBJECT IDENTIFIER ({tpm2_key_type}), emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL, + policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL, + secret [2] EXPLICIT OCTET STRING OPTIONAL, + authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL, + description [4] EXPLICIT UTF8String OPTIONAL, + rsaParent [5] EXPLICIT BOOLEAN OPTIONAL, + parentName [7] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_parent_name}), parent INTEGER ({tpm2_key_parent}), pubkey OCTET STRING ({tpm2_key_pub}), privkey OCTET STRING ({tpm2_key_priv}) - } +} diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index 0feb96cab62d..6ceb8cff3830 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -20,16 +20,26 @@ static u32 tpm2key_oid[] = { 2, 23, 133, 10, 1, 5 }; +enum tpm_key_tag { + TPM_KEY_TAG_EMPTY_AUTH = 0, + TPM_KEY_TAG_POLICY = 1, + TPM_KEY_TAG_SECRET = 2, + TPM_KEY_TAG_AUTH_POLICY = 3, + TPM_KEY_TAG_DESCRIPTION = 4, + TPM_KEY_TAG_RSA_PARENT = 5, + TPM_KEY_TAG_PARENT_NAME = 7, +}; + static int tpm2_key_encode(struct trusted_key_payload *payload, struct trusted_key_options *options, - u8 *src, u32 len) + u8 *src, u32 len, u8 *parent_name, + u16 parent_name_size) { const int SCRATCH_SIZE = PAGE_SIZE; - u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL); - u8 *work = scratch, *work1; - u8 *end_work = scratch + SCRATCH_SIZE; - u8 *priv, *pub; + u8 *end_work, *name_end; u16 priv_len, pub_len; + u8 *work, *work1; + u8 *priv, *pub; int ret; priv_len = get_unaligned_be16(src) + 2; @@ -40,23 +50,41 @@ static int tpm2_key_encode(struct trusted_key_payload *payload, pub_len = get_unaligned_be16(src) + 2; pub = src; + u8 *scratch __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL); if (!scratch) return -ENOMEM; + work = scratch; + end_work = scratch + SCRATCH_SIZE; + work = asn1_encode_oid(work, end_work, tpm2key_oid, asn1_oid_len(tpm2key_oid)); if (options->blobauth_len == 0) { - unsigned char bool[3], *w = bool; - /* tag 0 is emptyAuth */ - w = asn1_encode_boolean(w, w + sizeof(bool), true); - if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) { - ret = PTR_ERR(w); - goto err; + u8 *bool_end; + u8 bool[3]; + + bool_end = asn1_encode_boolean(&bool[0], &bool[sizeof(bool)], + true); + if (IS_ERR(bool_end)) { + pr_err("BUG: Boolean failed to encode\n"); + return PTR_ERR(bool_end); } - work = asn1_encode_tag(work, end_work, 0, bool, w - bool); + + work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_EMPTY_AUTH, + bool, bool_end - bool); } + u8 *name_encoded __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL); + if (!name_encoded) + return -ENOMEM; + + name_end = asn1_encode_octet_string(&name_encoded[0], + &name_encoded[SCRATCH_SIZE], + parent_name, parent_name_size); + work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_PARENT_NAME, + name_encoded, name_end - name_encoded); + /* * Assume both octet strings will encode to a 2 byte definite length * @@ -65,8 +93,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload, */ if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE, "BUG: scratch buffer is too small")) { - ret = -EINVAL; - goto err; + return -EINVAL; } work = asn1_encode_integer(work, end_work, options->keyhandle); @@ -79,15 +106,10 @@ static int tpm2_key_encode(struct trusted_key_payload *payload, if (IS_ERR(work1)) { ret = PTR_ERR(work1); pr_err("BUG: ASN.1 encoder failed with %d\n", ret); - goto err; + return ret; } - kfree(scratch); return work1 - payload->blob; - -err: - kfree(scratch); - return ret; } struct tpm2_key_context { @@ -96,11 +118,13 @@ struct tpm2_key_context { u32 pub_len; const u8 *priv; u32 priv_len; + const u8 *name; + u32 name_len; }; static int tpm2_key_decode(struct trusted_key_payload *payload, struct trusted_key_options *options, - u8 **buf) + u8 **buf, u8 *parent_name, u16 *parent_name_size) { int ret; struct tpm2_key_context ctx; @@ -127,6 +151,8 @@ static int tpm2_key_decode(struct trusted_key_payload *payload, blob += ctx.priv_len; memcpy(blob, ctx.pub, ctx.pub_len); + memcpy(parent_name, ctx.name, ctx.name_len); + *parent_name_size = ctx.name_len; return 0; } @@ -190,6 +216,16 @@ int tpm2_key_priv(void *context, size_t hdrlen, return 0; } +int tpm2_key_parent_name(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct tpm2_key_context *ctx = context; + + ctx->name = value; + ctx->name_len = vlen; + + return 0; +} /** * tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer. * @@ -351,7 +387,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip, goto out; } - blob_len = tpm2_key_encode(payload, options, &buf->data[offset], blob_len); + blob_len = tpm2_key_encode(payload, options, &buf->data[offset], + blob_len, parent_name, parent_name_size); if (blob_len < 0) rc = blob_len; @@ -596,7 +633,7 @@ int tpm2_unseal_trusted(struct tpm_chip *chip, * Try to decode the provided blob as an ASN.1 blob. Assume that the * blob is in the legacy format if decoding does not end successfully. */ - rc = tpm2_key_decode(payload, options, &blob); + rc = tpm2_key_decode(payload, options, &blob, &parent_name[0], &parent_name_size); if (rc) { blob = payload->blob; payload->old_format = 1; @@ -611,11 +648,13 @@ int tpm2_unseal_trusted(struct tpm_chip *chip, if (rc) return rc; - rc = tpm2_read_public(chip, options->keyhandle, parent_name); - if (rc < 0) - goto out; + if (!parent_name_size) { + rc = tpm2_read_public(chip, options->keyhandle, parent_name); + if (rc < 0) + goto out; - parent_name_size = rc; + parent_name_size = rc; + } rc = tpm2_load_cmd(chip, payload, options, parent_name, parent_name_size, blob, &blob_handle); -- 2.39.5