From nobody Sun May 24 18:41:05 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 8BCDE2EFDAF; Sun, 24 May 2026 05:15:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779599735; cv=none; b=KSymYrDqqLm1CtqISttOjdFUAjEUtFzaNGp63BeSqWtkYX0DX6bt+P38q43+thVzROcXHUGtj1m56G3bMcSoxjcRYVPoAh9cfXIf9xnBkDXE244cT3R6CbHcYxjptLh0hUNsqSc9xqoydGRL63g1zzgwPcxUZJgD1A9oNIhtXLU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779599735; c=relaxed/simple; bh=YI9qK6UgcZSkpAxrngBlhDn+0JTPk5OJxnEUnm7fFrk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ky0fjaqG+ExWZsLpMQfzV5o7ZSUDr5smbFeb169obhU+72f1c13R8jhcWfnjjlmkUfEDZuc0IV5aHdOF4OIP1w5O+ajmlbgZqJ6nZaamWKIPZ+SX/+GUtNMPT/AzUkRv58rEGtj+xcIoAGh+2ARZjc7z9AjsdF9g3KFPR4uAxOA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=IR871SuC; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="IR871SuC" Received: by smtp.kernel.org (Postfix) with UTF8SMTPSA id 8439D1F000E9; Sun, 24 May 2026 05:15:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1779599733; bh=FKWO26ho61Nb4Vf1urgGRlJ3443TyMqUwpMqSFotZMw=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=IR871SuCM5f+t5BI2O1PgUK8NDo0wZ3e2SoG6JydtbrdujWBZLxeIs5vvTa4KYCAQ eCL4ikWuQMffuBEYS5nfGP78mm1G4tX5PC4KOs2b/i4sPG4SvzmB1Qin2HO1CEXiFs cz9Ky7Xqq22VRLWHLRU/bR/9hzCoKmMD5aB+6i9lUOHJis7qoX2glq/98CzWHZvDvM nhwuhdFGFQj56i4aSQmdjmibFLCaadJBL9pI3bIanrKhyc4cV+pGVTHGfXUhv3hk9j xYOz3fjiyMtTeed7nmxkizkRJh25GgLLVQbDvxWkhKM5PGu8y32+Z2jCOCSCyodL8S 1AirmbV1Ew49w== From: Jarkko Sakkinen To: keyrings@vger.kernel.org Cc: David Howells , linux-crypto@vger.kernel.org, linux-integrity@vger.kernel.org, David Woodhouse , James Bottomley , Stefan Berger , Herbert Xu , Jarkko Sakkinen , Andrew Morton , James Bottomley , Mimi Zohar , Paul Moore , James Morris , "Serge E. Hallyn" , linux-kernel@vger.kernel.org (open list), linux-security-module@vger.kernel.org (open list:SECURITY SUBSYSTEM) Subject: [PATCH v8 1/3] lib/asn1_encoder: Add asn1_encode_integer_bytes() Date: Sun, 24 May 2026 08:15:12 +0300 Message-ID: <20260524051519.3708075-2-jarkko@kernel.org> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260524051519.3708075-1-jarkko@kernel.org> References: <20260524051519.3708075-1-jarkko@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a helper encoding a positive integer from a byte array in big-endian format. Signed-off-by: Jarkko Sakkinen --- include/linux/asn1_encoder.h | 3 ++ lib/asn1_encoder.c | 62 ++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/include/linux/asn1_encoder.h b/include/linux/asn1_encoder.h index d17484dffb74..e206bd425854 100644 --- a/include/linux/asn1_encoder.h +++ b/include/linux/asn1_encoder.h @@ -12,6 +12,9 @@ unsigned char * asn1_encode_integer(unsigned char *data, const unsigned char *end_data, s64 integer); unsigned char * +asn1_encode_integer_bytes(unsigned char *data, const unsigned char *end_da= ta, + const unsigned char *integer, u32 integer_len); +unsigned char * asn1_encode_oid(unsigned char *data, const unsigned char *end_data, u32 oid[], int oid_len); unsigned char * diff --git a/lib/asn1_encoder.c b/lib/asn1_encoder.c index 92f35aae13b1..22e0acd6fe08 100644 --- a/lib/asn1_encoder.c +++ b/lib/asn1_encoder.c @@ -10,6 +10,8 @@ #include #include =20 +static int asn1_encode_length(unsigned char **data, int *data_len, int len= ); + /** * asn1_encode_integer() - encode positive integer to ASN.1 * @data: pointer to the pointer to the data @@ -85,6 +87,66 @@ asn1_encode_integer(unsigned char *data, const unsigned = char *end_data, } EXPORT_SYMBOL_GPL(asn1_encode_integer); =20 +/** + * asn1_encode_integer_bytes() - encode positive integer bytes to ASN.1 + * @data: pointer to the pointer to the data + * @end_data: end of data pointer, points one beyond last usable byte in = @data + * @bytes: integer bytes + * @bytes_len: amount of bytes + * + * Encode a positive integer from a byte array in big-endian format. Strip + * leading zeros. + */ +unsigned char * +asn1_encode_integer_bytes(unsigned char *data, const unsigned char *end_da= ta, + const unsigned char *bytes, u32 bytes_len) +{ + static const unsigned char zero; + int data_len =3D end_data - data; + bool add_pad =3D false; + int ret; + + if (IS_ERR(data)) + return data; + + if (!bytes || !bytes_len) + return ERR_PTR(-EINVAL); + + /* Strip leading zeros: */ + while (bytes_len > 1 && bytes[0] =3D=3D 0) { + bytes++; + bytes_len--; + } + + if (!bytes_len) { + bytes =3D &zero; + bytes_len =3D 1; + } else { + add_pad =3D bytes[0] & 0x80; + } + + if (data_len < 2) + return ERR_PTR(-EINVAL); + + *(data++) =3D _tag(UNIV, PRIM, INT); + data_len--; + + ret =3D asn1_encode_length(&data, &data_len, bytes_len + add_pad); + if (ret) + return ERR_PTR(ret); + + if (data_len < bytes_len + add_pad) + return ERR_PTR(-EINVAL); + + if (add_pad) + *(data++) =3D 0; + + memcpy(data, bytes, bytes_len); + data +=3D bytes_len; + return data; +} +EXPORT_SYMBOL_GPL(asn1_encode_integer_bytes); + /* calculate the base 128 digit values setting the top bit of the first oc= tet */ static int asn1_encode_oid_digit(unsigned char **_data, int *data_len, u32= oid) { --=20 2.47.3 From nobody Sun May 24 18:41:05 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 EB0F32D131D; Sun, 24 May 2026 05:15:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779599740; cv=none; b=CDvtgvuyN24aOZ3cMo/2AkYonRdbGa3eXr8P6iJDNbTlbyPeUQU0vHu4zbijinad7VNP2O4McNCQnhxIiWX9yMW2y73EutoppfttNZowph7D/q11i5lSudrFyQmPh88/p4IKRBP2DiG64Oob4Jbw4uhnhjOP5QXj3hIFMG/bRcc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779599740; c=relaxed/simple; bh=cprsDt8mvzg757EI5xzIDAuBPDpx1DPfjmZvAVre8lU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=W37cwp0tai6z/erj8hLLYZkJzsmRXRj8pQ8MSslNtr/CQ6kq0D0VYu/5VY6S9UI3i3QK9ZMR0zTYGiE8XQmP5YJiqKri/qVq9ylN/iazLbTSPx5hOCaQciIAvB5c6/Q1HebiO25IJR4C2XDtKOgGaU/FbiVSwQ/OK6mYQoBtXAM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=YlA92mMZ; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="YlA92mMZ" Received: by smtp.kernel.org (Postfix) with UTF8SMTPSA id 1C0451F000E9; Sun, 24 May 2026 05:15:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1779599738; bh=+ccOfGvZi4CTqd2/wXh9RtmD03GS9gPLCDKhk3bgDU8=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=YlA92mMZGiraG5aTIckIHnMXvPnPSYBANAUKo1spDqdlVINXb+5I3iNIJUlnQiLnr qQu1k6k9GmkvKR93JPiCXgx0W+oPYxNyqq7Xv2lwPPArb4zME7b9F18vz3J+U5w/Dn XCsn+RfQTHOsy6JTV0d7wKEpNeCSjpXuHMbDGgYHZRKEDtcXv6wUFnAo6SGdiLiKQ4 I8Y6HBj8yCfhofw2GKlQzCU2jLQSq3K4AuWaPlg1g2wUOi1Llr64jpdXgDhnVvM57X mcYTBKjRekhBYUNlBqb7kqkSKTQY3VaOCN9/n3zqm4qdN+25ePh9YUUl9KYkAVqr7V IcSFnEtYUNESA== From: Jarkko Sakkinen To: keyrings@vger.kernel.org Cc: David Howells , linux-crypto@vger.kernel.org, linux-integrity@vger.kernel.org, David Woodhouse , James Bottomley , Stefan Berger , Herbert Xu , Jarkko Sakkinen , "David S. Miller" , James Bottomley , Mimi Zohar , Paul Moore , James Morris , "Serge E. Hallyn" , linux-kernel@vger.kernel.org (open list), linux-security-module@vger.kernel.org (open list:SECURITY SUBSYSTEM) Subject: [PATCH v8 2/3] crypto: Migrate TPMKey ASN.1 objects from trusted-keys Date: Sun, 24 May 2026 08:15:13 +0300 Message-ID: <20260524051519.3708075-3-jarkko@kernel.org> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260524051519.3708075-1-jarkko@kernel.org> References: <20260524051519.3708075-1-jarkko@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Migrate the TPMKey ASN.1 code from trusted-keys to the crypto subsystem, and put the code behind CRYPTO_TPM2_KEY Kconfig flag. Signed-off-by: Jarkko Sakkinen --- crypto/Kconfig | 7 + crypto/Makefile | 6 + crypto/tpm2_key.asn1 | 11 ++ crypto/tpm2_key.c | 150 ++++++++++++++++++++++ include/crypto/tpm2_key.h | 46 +++++++ security/keys/trusted-keys/Kconfig | 2 +- security/keys/trusted-keys/Makefile | 2 - security/keys/trusted-keys/tpm2key.asn1 | 11 -- security/keys/trusted-keys/trusted_tpm2.c | 119 ++--------------- 9 files changed, 232 insertions(+), 122 deletions(-) create mode 100644 crypto/tpm2_key.asn1 create mode 100644 crypto/tpm2_key.c create mode 100644 include/crypto/tpm2_key.h delete mode 100644 security/keys/trusted-keys/tpm2key.asn1 diff --git a/crypto/Kconfig b/crypto/Kconfig index 103d1f58cb7c..5476d80372a1 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -3,6 +3,13 @@ # Generic algorithms support # =20 +config CRYPTO_TPM2_KEY + bool + depends on CRYPTO + select ASN1 + select OID_REGISTRY + default n + # # async_tx api: hardware offloaded memory transfer/transform support # diff --git a/crypto/Makefile b/crypto/Makefile index 162242593c7c..e232f9b9bee6 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -206,3 +206,9 @@ obj-$(CONFIG_CRYPTO_KDF800108_CTR) +=3D kdf_sp800108.o obj-$(CONFIG_CRYPTO_DF80090A) +=3D df_sp80090a.o =20 obj-$(CONFIG_CRYPTO_KRB5) +=3D krb5/ + +ifdef CONFIG_CRYPTO_TPM2_KEY +$(obj)/tpm2_key.asn1.o: $(obj)/tpm2_key.asn1.h $(obj)/tpm2_key.asn1.c +$(obj)/tpm2_key.o: $(obj)/tpm2_key.asn1.h +obj-y +=3D tpm2_key.o tpm2_key.asn1.o +endif diff --git a/crypto/tpm2_key.asn1 b/crypto/tpm2_key.asn1 new file mode 100644 index 000000000000..553bf996af59 --- /dev/null +++ b/crypto/tpm2_key.asn1 @@ -0,0 +1,11 @@ +--- +--- ASN.1 for TPM 2.0 keys +--- + +TPMKey ::=3D SEQUENCE { + type OBJECT IDENTIFIER ({tpm2_key_get_type}), + emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL ({tpm2_key_get_empty_auth}), + parent INTEGER ({tpm2_key_get_parent}), + pubkey OCTET STRING ({tpm2_get_public}), + privkey OCTET STRING ({tpm2_get_private}) + } diff --git a/crypto/tpm2_key.c b/crypto/tpm2_key.c new file mode 100644 index 000000000000..5704ccdb7c0d --- /dev/null +++ b/crypto/tpm2_key.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include "tpm2_key.asn1.h" + +#undef pr_fmt +#define pr_fmt(fmt) "tpm2_key: "fmt + +struct tpm2_key_decoder_context { + u32 parent; + const u8 *pub; + u32 pub_len; + const u8 *priv; + u32 priv_len; + enum OID oid; + bool empty_auth; +}; + +int tpm2_key_get_parent(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct tpm2_key_decoder_context *decoder =3D context; + const u8 *v =3D value; + int i; + + decoder->parent =3D 0; + for (i =3D 0; i < vlen; i++) { + decoder->parent <<=3D 8; + decoder->parent |=3D v[i]; + } + + return 0; +} + +int tpm2_key_get_type(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct tpm2_key_decoder_context *decoder =3D context; + + decoder->oid =3D look_up_OID(value, vlen); + return 0; +} + +int tpm2_key_get_empty_auth(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct tpm2_key_decoder_context *decoder =3D context; + const u8 *bool_value =3D value; + + if (!value || vlen !=3D 1) + return -EBADMSG; + + decoder->empty_auth =3D bool_value[0] !=3D 0; + return 0; +} + +static inline bool tpm2_key_is_valid(const void *value, size_t vlen) +{ + if (vlen < 2 || vlen > TPM2_KEY_BYTES_MAX) + return false; + + if (get_unaligned_be16(value) !=3D vlen - 2) + return false; + + return true; +} + +int tpm2_get_public(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct tpm2_key_decoder_context *decoder =3D context; + + if (!tpm2_key_is_valid(value, vlen)) + return -EBADMSG; + + if (sizeof(struct tpm2_key_desc) > vlen - 2) + return -EBADMSG; + + decoder->pub =3D value; + decoder->pub_len =3D vlen; + return 0; +} + +int tpm2_get_private(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct tpm2_key_decoder_context *decoder =3D context; + + if (!tpm2_key_is_valid(value, vlen)) + return -EBADMSG; + + decoder->priv =3D value; + decoder->priv_len =3D vlen; + return 0; +} + +/** + * tpm2_key_decode() - Decode TPM2 ASN.1 key + * @src: ASN.1 source. + * @src_len: ASN.1 source length. + * + * Decodes the TPM2 ASN.1 key and validates that the public key data has a= ll + * the shared fields of TPMT_PUBLIC. This is full coverage of the memory t= hat + * can be validated before doing any key type specific validation. + * + * Return: + * - TPM2 ASN.1 key on success. + * - -EBADMSG when decoding fails. + * - -ENOMEM when OOM while allocating struct tpm2_key. + */ +struct tpm2_key *tpm2_key_decode(const u8 *src, u32 src_len) +{ + struct tpm2_key_decoder_context decoder; + struct tpm2_key *key; + u8 *data; + int ret; + + memset(&decoder, 0, sizeof(decoder)); + ret =3D asn1_ber_decoder(&tpm2_key_decoder, &decoder, src, src_len); + if (ret < 0) { + if (ret !=3D -EBADMSG) + pr_info("Decoder error %d\n", ret); + + return ERR_PTR(-EBADMSG); + } + + key =3D kzalloc(sizeof(*key), GFP_KERNEL); + if (!key) + return ERR_PTR(-ENOMEM); + + data =3D &key->data[0]; + memcpy(&data[0], decoder.priv, decoder.priv_len); + memcpy(&data[decoder.priv_len], decoder.pub, decoder.pub_len); + + key->oid =3D decoder.oid; + key->priv_len =3D decoder.priv_len; + key->pub_len =3D decoder.pub_len; + key->parent =3D decoder.parent; + key->desc =3D (struct tpm2_key_desc *)&data[decoder.priv_len + 2]; + key->empty_auth =3D decoder.empty_auth; + return key; +} +EXPORT_SYMBOL_GPL(tpm2_key_decode); diff --git a/include/crypto/tpm2_key.h b/include/crypto/tpm2_key.h new file mode 100644 index 000000000000..883afaa596e5 --- /dev/null +++ b/include/crypto/tpm2_key.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __LINUX_TPM2_KEY_H__ +#define __LINUX_TPM2_KEY_H__ + +#include +#include + +#define TPM2_KEY_BYTES_MAX 1024 + +/* TPM2 Structures 12.2.4: TPMT_PUBLIC */ +struct tpm2_key_desc { + __be16 type; + __be16 name_alg; + __be32 object_attributes; + __be16 policy_size; +} __packed; + +/* Decoded TPM2 ASN.1 key. */ +struct tpm2_key { + u8 data[2 * TPM2_KEY_BYTES_MAX]; + struct tpm2_key_desc *desc; + u16 priv_len; + u16 pub_len; + u32 parent; + enum OID oid; + bool empty_auth; +}; + +struct tpm2_key *tpm2_key_decode(const u8 *src, u32 src_len); + +static inline const void *tpm2_key_data(const struct tpm2_key *key) +{ + return &key->data[0]; +} + +static inline u16 tpm2_key_type(const struct tpm2_key *key) +{ + return be16_to_cpu(key->desc->type); +} + +static inline int tpm2_key_policy_size(const struct tpm2_key *key) +{ + return be16_to_cpu(key->desc->policy_size); +} + +#endif /* __LINUX_TPM2_KEY_H__ */ diff --git a/security/keys/trusted-keys/Kconfig b/security/keys/trusted-key= s/Kconfig index e5a4a53aeab2..09b1ec1d5bc2 100644 --- a/security/keys/trusted-keys/Kconfig +++ b/security/keys/trusted-keys/Kconfig @@ -27,9 +27,9 @@ config TRUSTED_KEYS_TPM select CRYPTO_HASH_INFO select CRYPTO_LIB_SHA1 select CRYPTO_LIB_UTILS + select CRYPTO_TPM2_KEY select ASN1_ENCODER select OID_REGISTRY - select ASN1 select HAVE_TRUSTED_KEYS help Enable use of the Trusted Platform Module (TPM) as trusted key diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-ke= ys/Makefile index 5fc053a21dad..ac09d2d90051 100644 --- a/security/keys/trusted-keys/Makefile +++ b/security/keys/trusted-keys/Makefile @@ -7,9 +7,7 @@ obj-$(CONFIG_TRUSTED_KEYS) +=3D trusted.o trusted-y +=3D trusted_core.o trusted-$(CONFIG_TRUSTED_KEYS_TPM) +=3D trusted_tpm1.o =20 -$(obj)/trusted_tpm2.o: $(obj)/tpm2key.asn1.h trusted-$(CONFIG_TRUSTED_KEYS_TPM) +=3D trusted_tpm2.o -trusted-$(CONFIG_TRUSTED_KEYS_TPM) +=3D tpm2key.asn1.o =20 trusted-$(CONFIG_TRUSTED_KEYS_TEE) +=3D trusted_tee.o =20 diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/truste= d-keys/tpm2key.asn1 deleted file mode 100644 index f57f869ad600..000000000000 --- a/security/keys/trusted-keys/tpm2key.asn1 +++ /dev/null @@ -1,11 +0,0 @@ ---- ---- ASN.1 for TPM 2.0 keys ---- - -TPMKey ::=3D SEQUENCE { - type OBJECT IDENTIFIER ({tpm2_key_type}), - emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL, - 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/trus= ted-keys/trusted_tpm2.c index 6340823f8b53..5b079fe476d1 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -13,11 +13,10 @@ =20 #include #include +#include =20 #include =20 -#include "tpm2key.asn1.h" - static u32 tpm2key_oid[] =3D { 2, 23, 133, 10, 1, 5 }; =20 static int tpm2_key_encode(struct trusted_key_payload *payload, @@ -90,105 +89,6 @@ static int tpm2_key_encode(struct trusted_key_payload *= payload, return ret; } =20 -struct tpm2_key_context { - u32 parent; - const u8 *pub; - u32 pub_len; - const u8 *priv; - u32 priv_len; -}; - -static int tpm2_key_decode(struct trusted_key_payload *payload, - struct trusted_key_options *options, - u8 **buf) -{ - int ret; - struct tpm2_key_context ctx; - u8 *blob; - - memset(&ctx, 0, sizeof(ctx)); - - ret =3D asn1_ber_decoder(&tpm2key_decoder, &ctx, payload->blob, - payload->blob_len); - if (ret < 0) - return ret; - - if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE) - return -EINVAL; - - blob =3D kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL); - if (!blob) - return -ENOMEM; - - *buf =3D blob; - options->keyhandle =3D ctx.parent; - - memcpy(blob, ctx.priv, ctx.priv_len); - blob +=3D ctx.priv_len; - - memcpy(blob, ctx.pub, ctx.pub_len); - - return 0; -} - -int tpm2_key_parent(void *context, size_t hdrlen, - unsigned char tag, - const void *value, size_t vlen) -{ - struct tpm2_key_context *ctx =3D context; - const u8 *v =3D value; - int i; - - ctx->parent =3D 0; - for (i =3D 0; i < vlen; i++) { - ctx->parent <<=3D 8; - ctx->parent |=3D v[i]; - } - - return 0; -} - -int tpm2_key_type(void *context, size_t hdrlen, - unsigned char tag, - const void *value, size_t vlen) -{ - enum OID oid =3D look_up_OID(value, vlen); - - if (oid !=3D OID_TPMSealedData) { - char buffer[50]; - - sprint_oid(value, vlen, buffer, sizeof(buffer)); - pr_debug("OID is \"%s\" which is not TPMSealedData\n", - buffer); - return -EINVAL; - } - - return 0; -} - -int tpm2_key_pub(void *context, size_t hdrlen, - unsigned char tag, - const void *value, size_t vlen) -{ - struct tpm2_key_context *ctx =3D context; - - ctx->pub =3D value; - ctx->pub_len =3D vlen; - - return 0; -} - -int tpm2_key_priv(void *context, size_t hdrlen, - unsigned char tag, - const void *value, size_t vlen) -{ - struct tpm2_key_context *ctx =3D context; - - ctx->priv =3D value; - ctx->priv_len =3D vlen; - - return 0; -} =20 /** * tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer. @@ -372,23 +272,26 @@ static int tpm2_load_cmd(struct tpm_chip *chip, struct trusted_key_options *options, u32 *blob_handle) { - u8 *blob_ref __free(kfree) =3D NULL; + struct tpm2_key *key __free(kfree) =3D NULL; struct tpm_buf buf; unsigned int private_len; unsigned int public_len; unsigned int blob_len; - u8 *blob, *pub; + const u8 *blob, *pub; int rc; u32 attrs; =20 - rc =3D tpm2_key_decode(payload, options, &blob); - if (rc) { + key =3D tpm2_key_decode(payload->blob, payload->blob_len); + if (IS_ERR(key)) + key =3D NULL; + + if (key && key->oid =3D=3D OID_TPMSealedData) { + options->keyhandle =3D key->parent; + blob =3D tpm2_key_data(key); + } else { /* old form */ blob =3D payload->blob; payload->old_format =3D 1; - } else { - /* Bind for cleanup: */ - blob_ref =3D blob; } =20 /* new format carries keyhandle but old format doesn't */ --=20 2.47.3 From nobody Sun May 24 18:41:05 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 E7F7E2DE6FF; Sun, 24 May 2026 05:15:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779599747; cv=none; b=bmf2eOfp0JnkhCytO84tZkmNAWdY73BcnFeXDg4mF289KQbQ85apbwlS3zsBcOEMTq6PL46BY9Z3TJwkqb3gADhFUp5t128oL2CsHDdJVwJ9N7MJ242r4wWJU97mIsSWwIOUnK/J67jrrQA1Kimy/fMIBEKJt7F5cyh9f8Dw9Ko= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779599747; c=relaxed/simple; bh=R1c57vlFHeOi/WKWb1B1eqZNdffnljRoiQiI1+U+QWg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IEzgygbi05+aexvWIDyEpzfV168uUzqiO6tmjVn55EyAs11kHtgpmryU8EWXMf9kBsAUPlOdwuJdICsQaksqtJZ64ec/VPCPhxJRDmDDS1iRHePMMpX4GRMqvTxZwX7yUy3TPwH79MButCSmnHfZdB+vufNVprLUYsiRxAbRF6U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=egjfQth6; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="egjfQth6" Received: by smtp.kernel.org (Postfix) with UTF8SMTPSA id B1AC41F000E9; Sun, 24 May 2026 05:15:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1779599744; bh=J/zLQARNj0oHRSp/EG/kgexamTHn/b9Eny6z5lqUYXo=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=egjfQth6kYquTaHRylcxgw3Upl1d0K1CDvi2hVq0a6l5jSYXxKvrtBhcCtcHwPhuQ Z75nP4aSh6GhvxsDtbpHyAeT4H2xykPgy8O/KmHXasJDugMqAzH1xYpB5zhBWrPO0y 946MPp2A31gxtNPUf+PPHJYGFgbedQwjGi7YSoVI8QcNT+Vbwz6Jy0EZY+v3le30ES aCfeI1e7W0boLM+9GoE/r1TsMhF7LhykNNLOmVBiiAQq2r7YpxgDmaQt4o3KUV32rh objfz+GlPz9s01Z9U5jE0uzGejt70XIh76zjIXoDbSbXkeH9uBEMDqN9u6OdjcAmyA SctaqBDF2MBng== From: Jarkko Sakkinen To: keyrings@vger.kernel.org Cc: David Howells , linux-crypto@vger.kernel.org, linux-integrity@vger.kernel.org, David Woodhouse , James Bottomley , Stefan Berger , Herbert Xu , Jarkko Sakkinen , James Prestwood , Lukas Wunner , Ignat Korchagin , "David S. Miller" , Peter Huewe , Jason Gunthorpe , James Bottomley , Mimi Zohar , Paul Moore , James Morris , "Serge E. Hallyn" , linux-kernel@vger.kernel.org (open list), linux-security-module@vger.kernel.org (open list:SECURITY SUBSYSTEM) Subject: [PATCH v8 3/3] keys: asymmetric: tpm2_asymmetric Date: Sun, 24 May 2026 08:15:14 +0300 Message-ID: <20260524051519.3708075-4-jarkko@kernel.org> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260524051519.3708075-1-jarkko@kernel.org> References: <20260524051519.3708075-1-jarkko@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" tpm2_asymmetric is a key type for external keys generated outside the TPM chip but later imported to the chip's key hierarchy as leaf keys. The key type supports ECC-NIST-P256/384/521 and RSA keys and provides signing and verification operations for each. In addition, for RSA encryption and decryption operations are supported. Co-developed-by: James Prestwood Signed-off-by: James Prestwood Signed-off-by: Jarkko Sakkinen --- crypto/asymmetric_keys/Kconfig | 17 + crypto/asymmetric_keys/Makefile | 1 + crypto/asymmetric_keys/tpm2_asymmetric.c | 1096 ++++++++++++++++++++++ include/linux/tpm.h | 10 + 4 files changed, 1124 insertions(+) create mode 100644 crypto/asymmetric_keys/tpm2_asymmetric.c diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index e50bd9b3e27b..a93e13d5768f 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -15,6 +15,7 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE select MPILIB select CRYPTO_HASH_INFO select CRYPTO_AKCIPHER + select CRYPTO_RSA select CRYPTO_SIG select CRYPTO_HASH help @@ -23,6 +24,22 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE appropriate hash algorithms (such as SHA-1) must be available. ENOPKG will be reported if the requisite algorithm is unavailable. =20 +config ASYMMETRIC_TPM2_KEY_SUBTYPE + tristate "Asymmetric TPM2 crypto algorithm subtype" + depends on TCG_TPM + select CRYPTO_SHA256 + select CRYPTO_HASH_INFO + select CRYPTO_TPM2_KEY + select ASN1 + select ASN1_ENCODER + help + This option provides support for asymmetric TPM2 key type handling. + Asymmetric operations such as sign and verify are delegated to the + TPM, and bound to the kernel crypto subsystem. Both RSA and ECDSA + keys are supported. + + ENOPKG will be reported if the requisite algorithm is unavailable. + config X509_CERTIFICATE_PARSER tristate "X.509 certificate parser" depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makef= ile index bc65d3b98dcb..c83b40d021ac 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -11,6 +11,7 @@ asymmetric_keys-y :=3D \ signature.o =20 obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) +=3D public_key.o +obj-$(CONFIG_ASYMMETRIC_TPM2_KEY_SUBTYPE) +=3D tpm2_asymmetric.o =20 # # X.509 Certificate handling diff --git a/crypto/asymmetric_keys/tpm2_asymmetric.c b/crypto/asymmetric_k= eys/tpm2_asymmetric.c new file mode 100644 index 000000000000..f6598e6fd283 --- /dev/null +++ b/crypto/asymmetric_keys/tpm2_asymmetric.c @@ -0,0 +1,1096 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * An asymmetric TPM2 key subtype. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef pr_fmt +#define pr_fmt(fmt) "tpm2_asymmetric: "fmt + +/* TPM2 Structures 12.2.3.5: TPMS_RSA_PARMS */ +struct tpm2_asymmetric_rsa_parms { + __be16 symmetric; + __be16 scheme; + __be16 key_bits; + __be32 exponent; + __be16 modulus_size; +} __packed; + +/* TPM2 Structures 12.2.3.6: TPMS_ECC_PARMS */ +struct tpm2_asymmetric_ecc_parms { + __be16 symmetric; + __be16 scheme; + __be16 ecc; + __be16 kdf; +}; + +static const void *tpm2_asymmetric_parms(const struct tpm2_key *key) +{ + return &key->data[key->priv_len + 2 + sizeof(*key->desc)]; +} + +static u16 tpm2_asymmetric_rsa_mod_size(const struct tpm2_key *key) +{ + const struct tpm2_asymmetric_rsa_parms *p =3D tpm2_asymmetric_parms(key); + + return be16_to_cpu(p->modulus_size); +} + +static const u8 *tpm2_asymmetric_ecc_x(const struct tpm2_key *key) +{ + return tpm2_asymmetric_parms(key) + sizeof(struct tpm2_asymmetric_ecc_par= ms); +} + +static const u8 *tpm2_asymmetric_ecc_y(const struct tpm2_key *key) +{ + const u8 *x =3D tpm2_asymmetric_ecc_x(key); + u16 x_size =3D get_unaligned_be16(&x[0]); + + return &x[2 + x_size]; +} + +static unsigned int tpm2_asymmetric_ecc_key_bits(u16 ecc) +{ + switch (ecc) { + case TPM2_ECC_NIST_P256: + return 256; + case TPM2_ECC_NIST_P384: + return 384; + case TPM2_ECC_NIST_P521: + return 521; + default: + return 0; + } +} + +static int tpm2_asymmetric_hash_lookup(const char *hash_algo, + int *hash_id, int *tpm_hash) +{ + int id, alg; + + if (!hash_algo) + return -EINVAL; + + id =3D match_string(hash_algo_name, HASH_ALGO__LAST, hash_algo); + if (id < 0) + return -ENOPKG; + + alg =3D tpm2_find_hash_alg(id); + if (alg < 0) + return -ENOPKG; + + if (hash_id) + *hash_id =3D id; + + if (tpm_hash) + *tpm_hash =3D alg; + + return 0; +} + +static int tpm2_asymmetric_signature_scheme(const struct tpm2_key *key, + const char *encoding, + const char *hash_algo, + u16 *scheme, + int *tpm_hash) +{ + if (!encoding) + return -ENOPKG; + + switch (tpm2_key_type(key)) { + case TPM_ALG_RSA: + if (strcmp(encoding, "pkcs1") !=3D 0) + return -ENOPKG; + *scheme =3D TPM_ALG_RSASSA; + break; + case TPM_ALG_ECC: + if (strcmp(encoding, "x962") !=3D 0) + return -ENOPKG; + *scheme =3D TPM_ALG_ECDSA; + break; + default: + return -EOPNOTSUPP; + } + + return tpm2_asymmetric_hash_lookup(hash_algo, NULL, tpm_hash); +} + +/* + * Load a TPM2 key blob into the TPM. + * + * On success, @buf is initialized and the authorization session is kept o= pen. + * On failure, @buf is destroyed and the authorization session is closed. + */ +static int tpm2_asymmetric_load(struct tpm_chip *chip, struct tpm2_key *ke= y, + struct tpm_buf *buf, u32 *handle_out) +{ + int ret; + + ret =3D tpm2_start_auth_session(chip); + if (ret) + return ret; + + ret =3D tpm_buf_init(buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD); + if (ret < 0) + goto err_auth; + + ret =3D tpm_buf_append_name(chip, buf, key->parent, NULL); + if (ret) + goto err_buf; + tpm_buf_append_hmac_session(chip, buf, TPM2_SA_CONTINUE_SESSION | + TPM2_SA_ENCRYPT, NULL, 0); + tpm_buf_append(buf, &key->data[0], key->priv_len + key->pub_len); + if (buf->flags & TPM_BUF_OVERFLOW) { + ret =3D -E2BIG; + goto err_buf; + } + ret =3D tpm_buf_fill_hmac_session(chip, buf); + if (ret) + goto err_buf; + ret =3D tpm_transmit_cmd(chip, buf, 4, "TPM2_CC_LOAD"); + ret =3D tpm_buf_check_hmac_response(chip, buf, ret); + if (ret) { + ret =3D -EIO; + goto err_buf; + } + + *handle_out =3D be32_to_cpup((__be32 *)&buf->data[TPM_HEADER_SIZE]); + return 0; + +err_buf: + tpm_buf_destroy(buf); + +err_auth: + tpm2_end_auth_session(chip); + return ret; +} + +static void tpm2_asymmetric_key_destroy(void *payload0, void *payload3) +{ + kfree(payload0); +} + +/* + * Encrypt using TPM2_RSA_Encrypt with RSAES (PKCS#1 v1.5) scheme. + */ +static int tpm2_asymmetric_rsa_encrypt(struct tpm_chip *chip, + struct tpm2_key *key, + struct kernel_pkey_params *params, + const void *in, void *out) +{ + u32 key_handle =3D 0; + struct tpm_buf buf; + u16 ciphertext_len; + u16 scheme; + u8 *pos; + int ret; + + if (!params->encoding) + return -EINVAL; + + if (strcmp(params->encoding, "pkcs1") =3D=3D 0) + scheme =3D TPM_ALG_RSAES; + else if (strcmp(params->encoding, "raw") =3D=3D 0) + scheme =3D TPM_ALG_NULL; + else + return -ENOPKG; + + ret =3D tpm_try_get_ops(chip); + if (ret) + return ret; + + ret =3D tpm2_asymmetric_load(chip, key, &buf, &key_handle); + if (ret) + goto err_ops; + + tpm2_end_auth_session(chip); + tpm_buf_destroy(&buf); + + ret =3D tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_RSA_ENCRYPT); + if (ret) + goto err_key; + + tpm_buf_append_u32(&buf, key_handle); + + tpm_buf_append_u16(&buf, params->in_len); + tpm_buf_append(&buf, in, params->in_len); + + tpm_buf_append_u16(&buf, scheme); + + tpm_buf_append_u16(&buf, 0); + + ret =3D tpm_transmit_cmd(chip, &buf, 4, "TPM2_RSA_Encrypt"); + if (ret) { + ret =3D -EIO; + goto err_buf; + } + + pos =3D buf.data + TPM_HEADER_SIZE; + ciphertext_len =3D be16_to_cpup((__be16 *)pos); + pos +=3D 2; + if (pos + ciphertext_len > buf.data + buf.length) { + ret =3D -EIO; + goto err_buf; + } + + if (params->out_len < ciphertext_len) { + ret =3D -EMSGSIZE; + goto err_buf; + } + + memcpy(out, pos, ciphertext_len); + ret =3D ciphertext_len; + +err_buf: + tpm_buf_destroy(&buf); + +err_key: + tpm2_flush_context(chip, key_handle); + +err_ops: + tpm_put_ops(chip); + return ret; +} + +/* + * Convert a TPM2B_PUBLIC_KEY_RSA response into a raw RSA signature. + */ +static int tpm2_asymmetric_rsa_parse_signature(struct tpm_buf *buf, + off_t *offset, + struct kernel_pkey_params *params, + void *out) +{ + u16 sig_len; + + sig_len =3D tpm_buf_read_u16(buf, offset); + if (buf->flags & TPM_BUF_BOUNDARY_ERROR) + return -EIO; + if (*offset + sig_len > buf->length) + return -EIO; + if (sig_len > params->out_len) + return -EMSGSIZE; + + memcpy(out, &buf->data[*offset], sig_len); + return sig_len; +} + +/* + * Convert a TPMT_SIGNATURE ECDSA R/S response into DER SEQUENCE form. + */ +static int tpm2_asymmetric_ecc_parse_signature(struct tpm_buf *buf, off_t = *offset, + struct kernel_pkey_params *params, + void *out) +{ + u8 der[2 * (2 + ECC_MAX_BYTES + 1)]; + u8 *encoded, *ptr; + const u8 *s; + u16 r_size; + u16 s_size; + + r_size =3D tpm_buf_read_u16(buf, offset); + if (buf->flags & TPM_BUF_BOUNDARY_ERROR) + return -EIO; + if (r_size =3D=3D 0 || r_size > ECC_MAX_BYTES || + *offset + r_size + 2 > buf->length) + return -EIO; + + s_size =3D get_unaligned_be16(&buf->data[*offset + r_size]); + s =3D &buf->data[*offset + r_size + 2]; + if (s_size =3D=3D 0 || s_size > ECC_MAX_BYTES || + *offset + r_size + 2 + s_size > buf->length) + return -EIO; + + ptr =3D der; + ptr =3D asn1_encode_integer_bytes(ptr, der + sizeof(der), + &buf->data[*offset], r_size); + ptr =3D asn1_encode_integer_bytes(ptr, der + sizeof(der), s, s_size); + if (IS_ERR(ptr)) + return PTR_ERR(ptr); + + encoded =3D asn1_encode_sequence(out, (u8 *)out + params->out_len, + der, ptr - der); + if (IS_ERR(encoded)) + return PTR_ERR(encoded) =3D=3D -EINVAL ? -EMSGSIZE : PTR_ERR(encoded); + + return encoded - (u8 *)out; +} + +static int tpm2_asymmetric_parse_signature(struct tpm_buf *buf, + u16 scheme, int tpm_hash, + struct kernel_pkey_params *params, + void *out) +{ + off_t offset =3D TPM_HEADER_SIZE + 4; + u16 hash_alg; + u16 sig_alg; + + sig_alg =3D tpm_buf_read_u16(buf, &offset); + hash_alg =3D tpm_buf_read_u16(buf, &offset); + if (buf->flags & TPM_BUF_BOUNDARY_ERROR) + return -EIO; + if (sig_alg !=3D scheme || hash_alg !=3D tpm_hash) + return -EIO; + + switch (scheme) { + case TPM_ALG_RSASSA: + return tpm2_asymmetric_rsa_parse_signature(buf, &offset, params, out); + case TPM_ALG_ECDSA: + return tpm2_asymmetric_ecc_parse_signature(buf, &offset, params, out); + default: + return -EOPNOTSUPP; + } +} + +/* + * Sign a digest using TPM2_Sign. + */ +static int tpm2_asymmetric_sign(struct tpm_chip *chip, struct tpm2_key *ke= y, + struct kernel_pkey_params *params, + const void *in, void *out) +{ + struct tpm_buf buf; + u32 key_handle =3D 0; + int tpm_hash; + u16 scheme; + int ret; + + ret =3D tpm2_asymmetric_signature_scheme(key, params->encoding, + params->hash_algo, &scheme, + &tpm_hash); + if (ret) + return ret; + + ret =3D tpm_try_get_ops(chip); + if (ret) + return ret; + + ret =3D tpm2_asymmetric_load(chip, key, &buf, &key_handle); + if (ret) + goto err_ops; + + tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_SIGN); + ret =3D tpm_buf_append_name(chip, &buf, key_handle, NULL); + if (ret) + goto err_key; + tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT, NULL, 0); + + /* digest (TPM2B_DIGEST) */ + tpm_buf_append_u16(&buf, params->in_len); + tpm_buf_append(&buf, in, params->in_len); + + /* inScheme (TPMT_SIG_SCHEME) */ + tpm_buf_append_u16(&buf, scheme); + tpm_buf_append_u16(&buf, tpm_hash); + + /* validation (TPMT_TK_HASHCHECK): NULL ticket */ + tpm_buf_append_u16(&buf, TPM2_ST_HASHCHECK); + tpm_buf_append_u32(&buf, TPM2_RH_NULL); + tpm_buf_append_u16(&buf, 0); + + if (buf.flags & TPM_BUF_OVERFLOW) { + tpm2_end_auth_session(chip); + ret =3D -E2BIG; + goto err_key; + } + ret =3D tpm_buf_fill_hmac_session(chip, &buf); + if (ret) + goto err_key; + ret =3D tpm_transmit_cmd(chip, &buf, 4, "TPM2_Sign"); + ret =3D tpm_buf_check_hmac_response(chip, &buf, ret); + if (ret) { + ret =3D -EIO; + goto err_key; + } + + ret =3D tpm2_asymmetric_parse_signature(&buf, scheme, tpm_hash, params, o= ut); + +err_key: + tpm2_flush_context(chip, key_handle); + tpm_buf_destroy(&buf); + +err_ops: + tpm_put_ops(chip); + return ret; +} + +/* + * Decrypt using TPM2_RSA_Decrypt with RSAES-PKCS1-v1_5 scheme. + */ +static int tpm2_asymmetric_rsa_decrypt(struct tpm_chip *chip, + struct tpm2_key *key, + struct kernel_pkey_params *params, + const void *in, void *out) +{ + u32 key_handle =3D 0; + struct tpm_buf buf; + u16 decrypted_len; + off_t offset; + int ret; + + if (!params->encoding || strcmp(params->encoding, "pkcs1") !=3D 0) + return -ENOPKG; + + ret =3D tpm_try_get_ops(chip); + if (ret) + return ret; + + ret =3D tpm2_asymmetric_load(chip, key, &buf, &key_handle); + if (ret) + goto err_ops; + + tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_RSA_DECRYPT); + ret =3D tpm_buf_append_name(chip, &buf, key_handle, NULL); + if (ret) + goto err_key; + tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT, NULL, 0); + tpm_buf_append_u16(&buf, params->in_len); + tpm_buf_append(&buf, in, params->in_len); + tpm_buf_append_u16(&buf, TPM_ALG_RSAES); + tpm_buf_append_u16(&buf, 0); + if (buf.flags & TPM_BUF_OVERFLOW) { + tpm2_end_auth_session(chip); + ret =3D -E2BIG; + goto err_key; + } + ret =3D tpm_buf_fill_hmac_session(chip, &buf); + if (ret) + goto err_key; + ret =3D tpm_transmit_cmd(chip, &buf, 4, "TPM2_RSA_DECRYPT"); + ret =3D tpm_buf_check_hmac_response(chip, &buf, ret); + if (ret) { + ret =3D -EIO; + goto err_key; + } + + offset =3D TPM_HEADER_SIZE + 4; + decrypted_len =3D tpm_buf_read_u16(&buf, &offset); + if (buf.flags & TPM_BUF_BOUNDARY_ERROR) { + ret =3D -EIO; + goto err_key; + } + if (offset + decrypted_len > buf.length) { + ret =3D -EIO; + goto err_key; + } + + if (params->out_len < decrypted_len) { + ret =3D -EMSGSIZE; + goto err_key; + } + + memcpy(out, &buf.data[offset], decrypted_len); + ret =3D decrypted_len; + +err_key: + tpm2_flush_context(chip, key_handle); + tpm_buf_destroy(&buf); + +err_ops: + tpm_put_ops(chip); + return ret; +} + +/* + * Verify an RSA signature using TPM2_VerifySignature with RSASSA scheme. + */ +static int tpm2_asymmetric_rsa_verify(const struct key *key, + const struct public_key_signature *sig) +{ + struct tpm2_key *tpm2_key =3D key->payload.data[asym_crypto]; + struct tpm_chip *chip; + struct tpm_buf buf; + u32 key_handle =3D 0; + int tpm_hash; + int ret; + + if (!sig->m) + return -ENOPKG; + + if (!sig->encoding || strcmp(sig->encoding, "pkcs1") !=3D 0) + return -ENOPKG; + + if (!sig->hash_algo) + return -EINVAL; + + chip =3D tpm_default_chip(); + + if (!chip) + return -ENODEV; + + ret =3D tpm2_asymmetric_hash_lookup(sig->hash_algo, NULL, &tpm_hash); + if (ret) + goto err_chip; + + ret =3D tpm_try_get_ops(chip); + if (ret) + goto err_chip; + + ret =3D tpm2_asymmetric_load(chip, tpm2_key, &buf, &key_handle); + if (ret) + goto err_ops; + + tpm2_end_auth_session(chip); + tpm_buf_destroy(&buf); + + ret =3D tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, + TPM2_CC_VERIFY_SIGNATURE); + if (ret) + goto err_key; + + tpm_buf_append_u32(&buf, key_handle); + + tpm_buf_append_u16(&buf, sig->m_size); + tpm_buf_append(&buf, sig->m, sig->m_size); + + tpm_buf_append_u16(&buf, TPM_ALG_RSASSA); + tpm_buf_append_u16(&buf, tpm_hash); + tpm_buf_append_u16(&buf, sig->s_size); + tpm_buf_append(&buf, sig->s, sig->s_size); + + ret =3D tpm_transmit_cmd(chip, &buf, 0, "TPM2_VerifySignature"); + if (ret) + ret =3D -EKEYREJECTED; + + tpm_buf_destroy(&buf); + +err_key: + tpm2_flush_context(chip, key_handle); + +err_ops: + tpm_put_ops(chip); + +err_chip: + put_device(&chip->dev); + return ret; +} + +static int tpm2_asymmetric_rsa_query(const struct kernel_pkey_params *para= ms, + struct kernel_pkey_query *info) +{ + const struct tpm2_key *key =3D params->key->payload.data[asym_crypto]; + u16 max_data_size =3D TPM2_MAX_DIGEST_SIZE; + const u16 mod_size =3D tpm2_asymmetric_rsa_mod_size(key); + int hash_id, ret; + + if (!params->encoding) + return -EINVAL; + + memset(info, 0, sizeof(*info)); + info->key_size =3D mod_size * 8; + + if (strcmp(params->encoding, "pkcs1") =3D=3D 0) { + if (params->hash_algo) { + ret =3D tpm2_asymmetric_hash_lookup(params->hash_algo, &hash_id, NULL); + if (ret) + return ret; + max_data_size =3D hash_digest_size[hash_id]; + } + + info->max_data_size =3D max_data_size; + info->max_sig_size =3D mod_size; + info->max_enc_size =3D mod_size; + info->max_dec_size =3D mod_size; + info->supported_ops =3D KEYCTL_SUPPORTS_SIGN | + KEYCTL_SUPPORTS_VERIFY | + KEYCTL_SUPPORTS_ENCRYPT | + KEYCTL_SUPPORTS_DECRYPT; + return 0; + } + + if (strcmp(params->encoding, "raw") =3D=3D 0) { + info->max_data_size =3D mod_size; + info->max_enc_size =3D mod_size; + info->max_dec_size =3D mod_size; + info->supported_ops =3D KEYCTL_SUPPORTS_ENCRYPT; + return 0; + } + + return -ENOPKG; +} + +static int tpm2_asymmetric_rsa_validate(const struct tpm2_key *key) +{ + const struct tpm2_asymmetric_rsa_parms *p =3D tpm2_asymmetric_parms(key); + u16 key_bits; + u16 mod_size; + + if (tpm2_key_policy_size(key) !=3D 0) + return -EBADMSG; + + if (key->pub_len < 2 + sizeof(*key->desc) + sizeof(*p)) + return -EBADMSG; + + if (be16_to_cpu(p->symmetric) !=3D TPM_ALG_NULL) + return -EBADMSG; + + if (be16_to_cpu(p->scheme) !=3D TPM_ALG_NULL) + return -EBADMSG; + + key_bits =3D be16_to_cpu(p->key_bits); + if (key_bits !=3D 2048 && key_bits !=3D 3072 && key_bits !=3D 4096) + return -EBADMSG; + + if (be32_to_cpu(p->exponent) !=3D 0x00000000 && + be32_to_cpu(p->exponent) !=3D 0x00010001) + return -EBADMSG; + + mod_size =3D tpm2_asymmetric_rsa_mod_size(key); + if (mod_size !=3D key_bits / 8) + return -EBADMSG; + + if (key->pub_len < 2 + sizeof(*key->desc) + sizeof(*p) + mod_size) + return -EBADMSG; + + return 0; +} + +static unsigned int tpm2_asymmetric_der_len_size(unsigned int len) +{ + if (len < 128) + return 1; + if (len <=3D 255) + return 2; + return 3; +} + +/* + * Parse a DER-encoded ECDSA signature: SEQUENCE { INTEGER r, INTEGER s }. + * + * On success, @r/@r_len and @s/@s_len point into @der with leading zero + * pads stripped. + */ +static int tpm2_asymmetric_ecc_parse_der_signature(const u8 *der, u32 der_= len, + const u8 **r, u16 *r_len, + const u8 **s, u16 *s_len) +{ + const u8 *end =3D der + der_len; + u32 seq_len, int_len; + const u8 *p =3D der; + + if (p >=3D end || *p++ !=3D 0x30) + return -EBADMSG; + + if (p >=3D end) + return -EBADMSG; + if (*p < 0x80) { + seq_len =3D *p++; + } else if (*p =3D=3D 0x81) { + if (++p >=3D end) + return -EBADMSG; + seq_len =3D *p++; + } else { + return -EBADMSG; + } + + if (p + seq_len > end) + return -EBADMSG; + end =3D p + seq_len; + + /* INTEGER r */ + if (p >=3D end || *p++ !=3D 0x02) + return -EBADMSG; + if (p >=3D end) + return -EBADMSG; + int_len =3D *p++; + if (int_len =3D=3D 0 || int_len >=3D 0x80 || p + int_len > end) + return -EBADMSG; + while (int_len > 1 && *p =3D=3D 0x00) { + p++; + int_len--; + } + *r =3D p; + *r_len =3D int_len; + p +=3D int_len; + + /* INTEGER s */ + if (p >=3D end || *p++ !=3D 0x02) + return -EBADMSG; + if (p >=3D end) + return -EBADMSG; + int_len =3D *p++; + if (int_len =3D=3D 0 || int_len >=3D 0x80 || p + int_len > end) + return -EBADMSG; + while (int_len > 1 && *p =3D=3D 0x00) { + p++; + int_len--; + } + *s =3D p; + *s_len =3D int_len; + p +=3D int_len; + + if (p !=3D end) + return -EBADMSG; + + return 0; +} + +/* + * Verify an ECDSA signature using TPM2_VerifySignature. + * + * A DER-encoded signature is parsed into (r, s) components for the TPM co= mmand. + */ +static int tpm2_asymmetric_ecc_verify(const struct key *key, + const struct public_key_signature *sig) +{ + struct tpm2_key *tpm2_key =3D key->payload.data[asym_crypto]; + struct tpm_chip *chip; + const u8 *r, *s_data; + struct tpm_buf buf; + u32 key_handle =3D 0; + u16 r_len, s_len; + int tpm_hash; + int ret; + + if (!sig->m) + return -ENOPKG; + + if (!sig->encoding || strcmp(sig->encoding, "x962") !=3D 0) + return -ENOPKG; + + if (!sig->hash_algo) + return -EINVAL; + + chip =3D tpm_default_chip(); + + if (!chip) + return -ENODEV; + + ret =3D tpm2_asymmetric_hash_lookup(sig->hash_algo, NULL, &tpm_hash); + if (ret) + goto err_chip; + + ret =3D tpm2_asymmetric_ecc_parse_der_signature(sig->s, sig->s_size, + &r, &r_len, &s_data, + &s_len); + if (ret) + goto err_chip; + + ret =3D tpm_try_get_ops(chip); + if (ret) + goto err_chip; + + ret =3D tpm2_asymmetric_load(chip, tpm2_key, &buf, &key_handle); + if (ret) + goto err_ops; + + tpm2_end_auth_session(chip); + tpm_buf_destroy(&buf); + + ret =3D tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, + TPM2_CC_VERIFY_SIGNATURE); + if (ret) + goto err_key; + + tpm_buf_append_u32(&buf, key_handle); + + /* digest (TPM2B_DIGEST) */ + tpm_buf_append_u16(&buf, sig->m_size); + tpm_buf_append(&buf, sig->m, sig->m_size); + + /* signature (TPMT_SIGNATURE): ECDSA with the given hash */ + tpm_buf_append_u16(&buf, TPM_ALG_ECDSA); + tpm_buf_append_u16(&buf, tpm_hash); + + /* signatureR (TPM2B_ECC_PARAMETER) */ + tpm_buf_append_u16(&buf, r_len); + tpm_buf_append(&buf, r, r_len); + + /* signatureS (TPM2B_ECC_PARAMETER) */ + tpm_buf_append_u16(&buf, s_len); + tpm_buf_append(&buf, s_data, s_len); + + ret =3D tpm_transmit_cmd(chip, &buf, 0, "TPM2_VerifySignature"); + if (ret) + ret =3D -EKEYREJECTED; + + tpm_buf_destroy(&buf); + +err_key: + tpm2_flush_context(chip, key_handle); + +err_ops: + tpm_put_ops(chip); + +err_chip: + put_device(&chip->dev); + return ret; +} + +static int tpm2_asymmetric_ecc_query(const struct kernel_pkey_params *para= ms, + struct kernel_pkey_query *info) +{ + const struct tpm2_key *key =3D params->key->payload.data[asym_crypto]; + const struct tpm2_asymmetric_ecc_parms *p =3D tpm2_asymmetric_parms(key); + unsigned int int_len, seq_payload; + const u8 *x; + u16 ecc, n; + int ret; + + ecc =3D be16_to_cpu(p->ecc); + x =3D tpm2_asymmetric_ecc_x(key); + n =3D get_unaligned_be16(&x[0]); + int_len =3D n + 1; + + if (!params->encoding || strcmp(params->encoding, "x962") !=3D 0) + return -ENOPKG; + + ret =3D tpm2_asymmetric_hash_lookup(params->hash_algo, NULL, NULL); + if (ret) + return ret; + + /* + * SEQUENCE { INTEGER (<=3Dn+1 bytes), INTEGER (<=3Dn+1 bytes) } + */ + seq_payload =3D 2 * (1 + tpm2_asymmetric_der_len_size(int_len) + int_len); + + memset(info, 0, sizeof(*info)); + info->key_size =3D tpm2_asymmetric_ecc_key_bits(ecc); + info->max_sig_size =3D 1 + tpm2_asymmetric_der_len_size(seq_payload) + se= q_payload; + info->max_data_size =3D TPM2_MAX_DIGEST_SIZE; + info->supported_ops =3D KEYCTL_SUPPORTS_SIGN | KEYCTL_SUPPORTS_VERIFY; + + return 0; +} + +static int tpm2_asymmetric_ecc_validate(const struct tpm2_key *key) +{ + const struct tpm2_asymmetric_ecc_parms *p =3D tpm2_asymmetric_parms(key); + size_t min_len =3D 2 + sizeof(*key->desc) + sizeof(*p); + u16 x_size, y_size; + const u8 *x, *y; + + if (tpm2_key_policy_size(key) !=3D 0) + return -EBADMSG; + + if (key->pub_len < min_len + 2) + return -EBADMSG; + + if (be16_to_cpu(p->symmetric) !=3D TPM_ALG_NULL) + return -EBADMSG; + + if (be16_to_cpu(p->scheme) !=3D TPM_ALG_NULL) + return -EBADMSG; + + if (be16_to_cpu(p->ecc) !=3D TPM2_ECC_NIST_P256 && + be16_to_cpu(p->ecc) !=3D TPM2_ECC_NIST_P384 && + be16_to_cpu(p->ecc) !=3D TPM2_ECC_NIST_P521) + return -EBADMSG; + + if (be16_to_cpu(p->kdf) !=3D TPM_ALG_NULL) + return -EBADMSG; + + x =3D tpm2_asymmetric_ecc_x(key); + x_size =3D get_unaligned_be16(&x[0]); + if (x_size > ECC_MAX_BYTES) + return -EBADMSG; + + if (key->pub_len < min_len + 2 + x_size + 2) + return -EBADMSG; + + y =3D tpm2_asymmetric_ecc_y(key); + y_size =3D get_unaligned_be16(&y[0]); + if (y_size > ECC_MAX_BYTES) + return -EBADMSG; + + if (key->pub_len < min_len + 2 + x_size + 2 + y_size) + return -EBADMSG; + + if (x_size !=3D y_size) + return -EBADMSG; + + return 0; +} + +static const char *tpm2_asymmetric_ecc_name(const struct tpm2_key *key) +{ + const struct tpm2_asymmetric_ecc_parms *p; + + p =3D tpm2_asymmetric_parms(key); + + switch (be16_to_cpu(p->ecc)) { + case TPM2_ECC_NIST_P256: + return "ecdsa-nist-p256"; + case TPM2_ECC_NIST_P384: + return "ecdsa-nist-p384"; + case TPM2_ECC_NIST_P521: + return "ecdsa-nist-p521"; + default: + return "ecdsa"; + } +} + +static void tpm2_asymmetric_describe(const struct key *asymmetric_key, + struct seq_file *m) +{ + const struct tpm2_key *key; + + key =3D asymmetric_key->payload.data[asym_crypto]; + if (!key) + return; + + switch (tpm2_key_type(key)) { + case TPM_ALG_RSA: + seq_puts(m, "tpm2.rsa"); + break; + case TPM_ALG_ECC: + seq_printf(m, "tpm2.%s", tpm2_asymmetric_ecc_name(key)); + break; + default: + seq_puts(m, "tpm2.unknown"); + break; + } +} + +static int tpm2_asymmetric_query(const struct kernel_pkey_params *params, + struct kernel_pkey_query *info) +{ + struct tpm2_key *key =3D params->key->payload.data[asym_crypto]; + + switch (tpm2_key_type(key)) { + case TPM_ALG_RSA: + return tpm2_asymmetric_rsa_query(params, info); + case TPM_ALG_ECC: + return tpm2_asymmetric_ecc_query(params, info); + default: + return -EOPNOTSUPP; + } +} + +static int tpm2_asymmetric_eds_op(struct kernel_pkey_params *params, + const void *in, void *out) +{ + struct tpm2_key *key =3D params->key->payload.data[asym_crypto]; + struct tpm_chip *chip; + int ret; + + chip =3D tpm_default_chip(); + if (!chip) + return -ENODEV; + + switch (params->op) { + case kernel_pkey_encrypt: + if (tpm2_key_type(key) !=3D TPM_ALG_RSA) { + ret =3D -EOPNOTSUPP; + break; + } + ret =3D tpm2_asymmetric_rsa_encrypt(chip, key, params, in, out); + break; + case kernel_pkey_decrypt: + if (tpm2_key_type(key) !=3D TPM_ALG_RSA) { + ret =3D -EOPNOTSUPP; + break; + } + ret =3D tpm2_asymmetric_rsa_decrypt(chip, key, params, in, out); + break; + case kernel_pkey_sign: + ret =3D tpm2_asymmetric_sign(chip, key, params, in, out); + break; + default: + ret =3D -EOPNOTSUPP; + break; + } + + put_device(&chip->dev); + return ret; +} + +static int tpm2_asymmetric_verify_signature(const struct key *asymmetric_k= ey, + const struct public_key_signature *sig) +{ + struct tpm2_key *key =3D asymmetric_key->payload.data[asym_crypto]; + + switch (tpm2_key_type(key)) { + case TPM_ALG_RSA: + return tpm2_asymmetric_rsa_verify(asymmetric_key, sig); + case TPM_ALG_ECC: + return tpm2_asymmetric_ecc_verify(asymmetric_key, sig); + default: + return -EOPNOTSUPP; + } +} + +static struct asymmetric_key_subtype tpm2_asymmetric_subtype =3D { + .owner =3D THIS_MODULE, + .name =3D "tpm2_asymmetric_key", + .name_len =3D sizeof("tpm2_asymmetric_key") - 1, + .describe =3D tpm2_asymmetric_describe, + .destroy =3D tpm2_asymmetric_key_destroy, + .query =3D tpm2_asymmetric_query, + .eds_op =3D tpm2_asymmetric_eds_op, + .verify_signature =3D tpm2_asymmetric_verify_signature, +}; + +static int tpm2_asymmetric_preparse(struct key_preparsed_payload *prep) +{ + struct tpm2_key *key __free(kfree) =3D NULL; + int ret; + + key =3D tpm2_key_decode(prep->data, prep->datalen); + if (IS_ERR(key)) { + ret =3D PTR_ERR(key); + key =3D NULL; + return ret; + } + + if (key->oid !=3D OID_TPMLoadableKey) + return -EBADMSG; + + switch (tpm2_key_type(key)) { + case TPM_ALG_RSA: + ret =3D tpm2_asymmetric_rsa_validate(key); + break; + case TPM_ALG_ECC: + ret =3D tpm2_asymmetric_ecc_validate(key); + break; + default: + ret =3D -EBADMSG; + break; + } + + if (ret < 0) + return ret; + + __module_get(tpm2_asymmetric_subtype.owner); + + prep->payload.data[asym_subtype] =3D &tpm2_asymmetric_subtype; + prep->payload.data[asym_key_ids] =3D NULL; + prep->payload.data[asym_crypto] =3D no_free_ptr(key); + prep->payload.data[asym_auth] =3D NULL; + prep->quotalen =3D 100; + + return 0; +} + +static struct asymmetric_key_parser tpm2_asymmetric_parser =3D { + .owner =3D THIS_MODULE, + .name =3D "tpm2_asymmetric_parser", + .parse =3D tpm2_asymmetric_preparse, +}; + +static int __init tpm2_asymmetric_init(void) +{ + return register_asymmetric_key_parser(&tpm2_asymmetric_parser); +} + +static void __exit tpm2_asymmetric_exit(void) +{ + unregister_asymmetric_key_parser(&tpm2_asymmetric_parser); +} + +module_init(tpm2_asymmetric_init); +module_exit(tpm2_asymmetric_exit); + +MODULE_DESCRIPTION("Asymmetric TPM2 key"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 202da079d500..d4d5ddc0173a 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -45,6 +45,7 @@ enum tpm2_session_types { /* if you add a new hash to this, increment TPM_MAX_HASHES below */ enum tpm_algorithms { TPM_ALG_ERROR =3D 0x0000, + TPM_ALG_RSA =3D 0x0001, TPM_ALG_SHA1 =3D 0x0004, TPM_ALG_AES =3D 0x0006, TPM_ALG_KEYEDHASH =3D 0x0008, @@ -53,6 +54,9 @@ enum tpm_algorithms { TPM_ALG_SHA512 =3D 0x000D, TPM_ALG_NULL =3D 0x0010, TPM_ALG_SM3_256 =3D 0x0012, + TPM_ALG_RSASSA =3D 0x0014, + TPM_ALG_RSAES =3D 0x0015, + TPM_ALG_ECDSA =3D 0x0018, TPM_ALG_ECC =3D 0x0023, TPM_ALG_CFB =3D 0x0043, }; @@ -66,6 +70,8 @@ enum tpm_algorithms { enum tpm2_curves { TPM2_ECC_NONE =3D 0x0000, TPM2_ECC_NIST_P256 =3D 0x0003, + TPM2_ECC_NIST_P384 =3D 0x0004, + TPM2_ECC_NIST_P521 =3D 0x0005, }; =20 struct tpm_digest { @@ -242,6 +248,7 @@ enum tpm2_structures { TPM2_ST_NO_SESSIONS =3D 0x8001, TPM2_ST_SESSIONS =3D 0x8002, TPM2_ST_CREATION =3D 0x8021, + TPM2_ST_HASHCHECK =3D 0x8024, }; =20 /* Indicates from what layer of the software stack the error comes from */ @@ -276,12 +283,15 @@ enum tpm2_command_codes { TPM2_CC_NV_READ =3D 0x014E, TPM2_CC_CREATE =3D 0x0153, TPM2_CC_LOAD =3D 0x0157, + TPM2_CC_RSA_DECRYPT =3D 0x0159, TPM2_CC_SEQUENCE_UPDATE =3D 0x015C, + TPM2_CC_SIGN =3D 0x015D, TPM2_CC_UNSEAL =3D 0x015E, TPM2_CC_CONTEXT_LOAD =3D 0x0161, TPM2_CC_CONTEXT_SAVE =3D 0x0162, TPM2_CC_FLUSH_CONTEXT =3D 0x0165, TPM2_CC_READ_PUBLIC =3D 0x0173, + TPM2_CC_RSA_ENCRYPT =3D 0x0174, TPM2_CC_START_AUTH_SESS =3D 0x0176, TPM2_CC_VERIFY_SIGNATURE =3D 0x0177, TPM2_CC_GET_CAPABILITY =3D 0x017A, --=20 2.47.3