From patchwork Tue May 28 21:08:06 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 800279 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 5C28617DE1D; Tue, 28 May 2024 21:08:34 +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=1716930514; cv=none; b=supyzjxK5E4ABvXZZ6wFZZ2LFCYdXzF3efZVI0zHejlblAPbymFpAZHYOvXz9HgG40EXMAfhKHoeVBsP9lecwO8jqV+qJ/tJn0uQXrknFqNHcWIbwAQSL7OWoz2vHvHshdn1RHMteU3gRvJj10Se7M0C3UuEm6N23Kl38fGCPE0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716930514; c=relaxed/simple; bh=rQTPLrmfpCPpQ7Zz53PlLkcw1m7mUahOVVqZUGpxmKU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=tlyIy7S0Q7KyvzUqc+9+p4i7PNf7IvLt7zqrouBSzH3pgep9irNI7pOpMuijeihx5+6//IhIA9mxgJaRZmrI7sdwh3dDPudh8N83QroTiZWVD3gZoZJj1Cv2L4sOs1y8YVrwMg8dfAr6ju/WmiZGvBFiqaAXOQHSX03KeiBU83A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=LgXqVEoe; 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="LgXqVEoe" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 894A3C32786; Tue, 28 May 2024 21:08:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1716930513; bh=rQTPLrmfpCPpQ7Zz53PlLkcw1m7mUahOVVqZUGpxmKU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=LgXqVEoesHL7udjdouPoqNM4e4ZfMWkuBheNDPagWX7xyAanW3r0QV1m3Mganzd8C xg5trrsv6v4sre+l3LNDG7t8Ff74GwhNBPuaiYIQeVbaB/b8I8Qt9zljRXLgQbzO/w 8RauqS5k4CwZf503fuithFHYzgFL7m23oR8ZX2iNDah20NO34cI1gPRPDqBADyM68K UiOOOZj+9D2xK1HaCmEPlBKklplAHLSYLwMs9XSnkAh9CwrBs3wgOTRhw8YAHv47lH zza7fUo0EWRObWX+1qtIGL4ullA85tY2fvHjHbb3h0Y3SEgaAo5fowoiz9h6l1JEe8 pZPx5OwsbgL+g== From: Jarkko Sakkinen To: Herbert Xu Cc: linux-integrity@vger.kernel.org, keyrings@vger.kernel.org, Andreas.Fuchs@infineon.com, James Prestwood , David Woodhouse , Eric Biggers , James Bottomley , linux-crypto@vger.kernel.org, Stefan Berger , Lennart Poettering , Jarkko Sakkinen , "David S. Miller" , linux-kernel@vger.kernel.org (open list) Subject: [PATCH v7 1/5] crypto: rsa-pkcs1pad: export rsa1_asn_lookup() Date: Wed, 29 May 2024 00:08:06 +0300 Message-ID: <20240528210823.28798-2-jarkko@kernel.org> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240528210823.28798-1-jarkko@kernel.org> References: <20240528210823.28798-1-jarkko@kernel.org> Precedence: bulk X-Mailing-List: linux-crypto@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 ASN.1 template is required for TPM2 asymmetric keys, as it needs to be piggy-packed with the input data before applying TPM2_RSA_Decrypt. This patch prepares crypto subsystem for the addition of those keys. Signed-off-by: Jarkko Sakkinen --- crypto/rsa-pkcs1pad.c | 16 ++++++++++------ include/crypto/rsa-pkcs1pad.h | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 include/crypto/rsa-pkcs1pad.h diff --git a/crypto/rsa-pkcs1pad.c b/crypto/rsa-pkcs1pad.c index cd501195f34a..00b6c14f861c 100644 --- a/crypto/rsa-pkcs1pad.c +++ b/crypto/rsa-pkcs1pad.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -79,11 +80,7 @@ static const u8 rsa_digest_info_sha3_512[] = { 0x05, 0x00, 0x04, 0x40 }; -static const struct rsa_asn1_template { - const char *name; - const u8 *data; - size_t size; -} rsa_asn1_templates[] = { +static const struct rsa_asn1_template rsa_asn1_templates[] = { #define _(X) { #X, rsa_digest_info_##X, sizeof(rsa_digest_info_##X) } _(md5), _(sha1), @@ -101,7 +98,13 @@ static const struct rsa_asn1_template { { NULL } }; -static const struct rsa_asn1_template *rsa_lookup_asn1(const char *name) +/** + * rsa_lookup_asn1() - Lookup the ASN.1 digest info given the hash + * name: hash algorithm name + * + * Returns the ASN.1 digest info on success, and NULL on failure. + */ +const struct rsa_asn1_template *rsa_lookup_asn1(const char *name) { const struct rsa_asn1_template *p; @@ -110,6 +113,7 @@ static const struct rsa_asn1_template *rsa_lookup_asn1(const char *name) return p; return NULL; } +EXPORT_SYMBOL_GPL(rsa_lookup_asn1); struct pkcs1pad_ctx { struct crypto_akcipher *child; diff --git a/include/crypto/rsa-pkcs1pad.h b/include/crypto/rsa-pkcs1pad.h new file mode 100644 index 000000000000..32c7453ff644 --- /dev/null +++ b/include/crypto/rsa-pkcs1pad.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * RSA padding templates. + */ + +#ifndef _CRYPTO_RSA_PKCS1PAD_H +#define _CRYPTO_RSA_PKCS1PAD_H + +/* + * Hash algorithm name to ASN.1 template mapping. + */ +struct rsa_asn1_template { + const char *name; + const u8 *data; + size_t size; +}; + +const struct rsa_asn1_template *rsa_lookup_asn1(const char *name); + +#endif /* _CRYPTO_RSA_PKCS1PAD_H */ From patchwork Tue May 28 21:08:07 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 799613 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 4874B17F36D; Tue, 28 May 2024 21:08:38 +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=1716930518; cv=none; b=AzcfZdW+v+cvreEDCqMOLCJlltOKQdpcDnu9JqrJYwI+pmf+zeRN34jcUMSVkS+1ijyfnNhrU/qlQ8jt2xhRM/JfJ364Omhg3l+2XEQJgmciCS/2Lp5HxaGrqBmuPg7JiZ6QsA+SRTtI6KI/KztzzB+ihJrVRwMwjiB3OruIFuw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716930518; c=relaxed/simple; bh=h8orBF4zzRfW1mNSKt+5IoMKfbFfiHan4PWjJTDQxIE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=pL+5cP6ssRZMGIXlXJIkIsD+1g2DKGAwURuGEs/3PQv3E5yZJfm34SwenzZZJ9wCkwebXmBtbNGDkzFjKEvzl03VFJHXGuAmrGo4dEwGxcdvII+2aWgk6m6RtdEYzU41gn67quSFvtpAjz+LcZwY0LU0uUgq0tTX2hgxuDZynck= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=PmkhGx7V; 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="PmkhGx7V" Received: by smtp.kernel.org (Postfix) with ESMTPSA id BF93EC32786; Tue, 28 May 2024 21:08:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1716930518; bh=h8orBF4zzRfW1mNSKt+5IoMKfbFfiHan4PWjJTDQxIE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=PmkhGx7V8i6zLjttIWkdl+8o0Y5vSXsPkxD8AI5uBF0zi8KAAMZG+viURTxW6P/fa l3Ftx2zJfbVW6E0/rlu5cceP7gYnl9mYX3QZF8MZICsw9z4SOLoGWqARBJvPnUtOJn rXM1sgkXv/IuMaSIgX3jYcPWpHZgQ4bFarvPb/eYJf3s2om5AczldRaBVNJ8s7Mtum 86SAbCDdpNJuZ40qpo4aUCiIqt9lfEmXU1FA0HJlgJkq/O6JpSje82xJjagHnLMebc kW+MZaYaIftO4Cd8UuadqVDEyE4lnR4fZgFCsv5UQuvtn92uXtyCwxsSZxJUZaPmq7 WOakhQOBYGmxA== From: Jarkko Sakkinen To: Herbert Xu Cc: linux-integrity@vger.kernel.org, keyrings@vger.kernel.org, Andreas.Fuchs@infineon.com, James Prestwood , David Woodhouse , Eric Biggers , James Bottomley , linux-crypto@vger.kernel.org, Stefan Berger , Lennart Poettering , Jarkko Sakkinen , "David S. Miller" , linux-kernel@vger.kernel.org (open list), James Bottomley , Mimi Zohar , David Howells , Paul Moore , James Morris , "Serge E. Hallyn" , linux-security-module@vger.kernel.org (open list:SECURITY SUBSYSTEM) Subject: [PATCH v7 2/5] KEYS: trusted: Change -EINVAL to -E2BIG Date: Wed, 29 May 2024 00:08:07 +0300 Message-ID: <20240528210823.28798-3-jarkko@kernel.org> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240528210823.28798-1-jarkko@kernel.org> References: <20240528210823.28798-1-jarkko@kernel.org> Precedence: bulk X-Mailing-List: linux-crypto@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Report -E2BIG instead of -EINVAL when too large size for the key blob is requested. Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger --- security/keys/trusted-keys/trusted_tpm2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index 8b7dd73d94c1..06c8fa7b21ae 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -122,7 +122,7 @@ static int tpm2_key_decode(struct trusted_key_payload *payload, return ret; if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE) - return -EINVAL; + return -E2BIG; blob = kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL); if (!blob) From patchwork Tue May 28 21:08:08 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 800278 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 EF040180A77; Tue, 28 May 2024 21:08:44 +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=1716930525; cv=none; b=Q/zKKMSIvucqu0m/uXcOwo48UPY0e2eW+jYeQlMXL3r8VTLjqfxFHuJSipACR4IMtqJ1z/Sse+H3CLBUIdNP+LnW9MM+d/SYXrLzlOysekV2Dc7Y1IJ/IY7YcFeveKLcEBSJa+uHMkRZtoEtD/G3uYRHjULfLbSH0VEsVrId6kI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716930525; c=relaxed/simple; bh=HjejRQyXQOkUessmoO6+Yc8j3jipeT/V6wTG5u+1Yho=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SD9NGyR7FaeXIgzTCZYeKWuf/M3PEWr+cvh/iH8aDPZZW0zVBQs1S5wPMGLet/Gxl0VUZ8H5pWwZbH7FV7kMqGoeWwcahu3yVfPEnJarY0Zb47eZgzeIGAHPCB7Ydv3mVQpHoHvvphE27B++9u+npdF6dTVdkivWz1KShPso9Aw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CBQjl9hE; 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="CBQjl9hE" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 27921C3277B; Tue, 28 May 2024 21:08:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1716930524; bh=HjejRQyXQOkUessmoO6+Yc8j3jipeT/V6wTG5u+1Yho=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CBQjl9hEk/VLwFseULqsMirEtppqiCtEmved6duidK/Yb25r6Bn4DK4e0Z3SjUphz cqRx2ty5nO35cABBhPVrjr7KIWY5ehxIxNRfUp8DYSk/8V2khgsCRrdKREWuh97HUE vop18eB3XSJcIVQ8WpxJBQd6OP62ByNLDJl4zNlKoWBxEj2X+4YcqAFaF0U7XBSbZn 2D4cQLK0yr6eIKqpptfl01cfvqoC8VTEN2KAmjcXibbQTuILWQyTnPTIYuyY7Mbu6n fidyQXXUKUUD4hrMlLqM9lc3hmo0Gm1oTxoFi42cyaWWMWsKB1KEw+RuXBWtM31zgn KOMgZTIUc1bBg== From: Jarkko Sakkinen To: Herbert Xu Cc: linux-integrity@vger.kernel.org, keyrings@vger.kernel.org, Andreas.Fuchs@infineon.com, James Prestwood , David Woodhouse , Eric Biggers , James Bottomley , linux-crypto@vger.kernel.org, Stefan Berger , Lennart Poettering , Jarkko Sakkinen , "David S. Miller" , linux-kernel@vger.kernel.org (open list), James Bottomley , Mimi Zohar , David Howells , Paul Moore , James Morris , "Serge E. Hallyn" , linux-security-module@vger.kernel.org (open list:SECURITY SUBSYSTEM) Subject: [PATCH v7 3/5] crypto: tpm2_key: Introduce a TPM2 key type Date: Wed, 29 May 2024 00:08:08 +0300 Message-ID: <20240528210823.28798-4-jarkko@kernel.org> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240528210823.28798-1-jarkko@kernel.org> References: <20240528210823.28798-1-jarkko@kernel.org> Precedence: bulk X-Mailing-List: linux-crypto@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 TPM2 ASN.1 format is required for trusted keys and asymmetric keys. Move it to crypto in order to make it available for both. Implement validation with coverage of all TPMT_PUBLIC shared fields. Key type specific fields must be covered by the different subsystems using this. A Kconfig option CRYPTO_TPM2_KEY can be used to select the feature, which depends only crypto subsystem itself and ASN.1 parser. Signed-off-by: Jarkko Sakkinen --- v6: * Relocate to crypto. Validate the shared part and provide accessor functions. Use a fixed buffer size. v2: * Do not allocate blob twice. Use the one inside struct tpm2_key. --- crypto/Kconfig | 7 ++ crypto/Makefile | 6 + crypto/tpm2_key.asn1 | 11 ++ crypto/tpm2_key.c | 134 ++++++++++++++++++++ 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 | 141 +++++----------------- 9 files changed, 235 insertions(+), 125 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 5688d42a59c2..c8989bc71f57 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -5,6 +5,13 @@ config XOR_BLOCKS tristate +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 edbbaa3ffef5..d932fdb72319 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -216,3 +216,9 @@ obj-$(CONFIG_CRYPTO_SIMD) += crypto_simd.o # Key derivation function # obj-$(CONFIG_CRYPTO_KDF800108_CTR) += kdf_sp800108.o + +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 += 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..b235d02ab78e --- /dev/null +++ b/crypto/tpm2_key.asn1 @@ -0,0 +1,11 @@ +--- +--- ASN.1 for TPM 2.0 keys +--- + +TPMKey ::= SEQUENCE { + type OBJECT IDENTIFIER ({tpm2_key_get_type}), + emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL, + 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..78f55478d046 --- /dev/null +++ b/crypto/tpm2_key.c @@ -0,0 +1,134 @@ +// 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; +}; + +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 = context; + const u8 *v = value; + int i; + + decoder->parent = 0; + for (i = 0; i < vlen; i++) { + decoder->parent <<= 8; + decoder->parent |= 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 = context; + + decoder->oid = look_up_OID(value, vlen); + 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) != 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 = context; + + if (!tpm2_key_is_valid(value, vlen)) + return -EBADMSG; + + if (sizeof(struct tpm2_key_desc) > vlen - 2) + return -EBADMSG; + + decoder->pub = value; + decoder->pub_len = 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 = context; + + if (!tpm2_key_is_valid(value, vlen)) + return -EBADMSG; + + decoder->priv = value; + decoder->priv_len = vlen; + return 0; +} + +/** + * tpm_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 all + * the shared fields of TPMT_PUBLIC. This is full coverage of the memory that + * 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 = asn1_ber_decoder(&tpm2_key_decoder, &decoder, src, src_len); + if (ret < 0) { + if (ret != -EBADMSG) + pr_info("Decoder error %d\n", ret); + + return ERR_PTR(-EBADMSG); + } + + key = kzalloc(sizeof(*key), GFP_KERNEL); + if (!key) + return ERR_PTR(-ENOMEM); + + data = &key->data[0]; + memcpy(&data[0], decoder.priv, decoder.priv_len); + memcpy(&data[decoder.priv_len], decoder.pub, decoder.pub_len); + + key->oid = decoder.oid; + key->priv_len = decoder.priv_len; + key->pub_len = decoder.pub_len; + key->parent = decoder.parent; + key->desc = (struct tpm2_key_desc *)&data[decoder.priv_len + 2]; + 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..74debaf707bf --- /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; + char oid_str[64]; +}; + +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-keys/Kconfig index 1fb8aa001995..00d9489384ac 100644 --- a/security/keys/trusted-keys/Kconfig +++ b/security/keys/trusted-keys/Kconfig @@ -9,9 +9,9 @@ config TRUSTED_KEYS_TPM select CRYPTO_HMAC select CRYPTO_SHA1 select CRYPTO_HASH_INFO + 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-keys/Makefile index f0f3b27f688b..2674d5c10fc9 100644 --- a/security/keys/trusted-keys/Makefile +++ b/security/keys/trusted-keys/Makefile @@ -7,9 +7,7 @@ obj-$(CONFIG_TRUSTED_KEYS) += trusted.o trusted-y += trusted_core.o trusted-$(CONFIG_TRUSTED_KEYS_TPM) += trusted_tpm1.o -$(obj)/trusted_tpm2.o: $(obj)/tpm2key.asn1.h trusted-$(CONFIG_TRUSTED_KEYS_TPM) += trusted_tpm2.o -trusted-$(CONFIG_TRUSTED_KEYS_TPM) += tpm2key.asn1.o trusted-$(CONFIG_TRUSTED_KEYS_TEE) += trusted_tee.o diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-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 ::= 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/trusted-keys/trusted_tpm2.c index 06c8fa7b21ae..b9e505e99e8c 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -13,11 +13,10 @@ #include #include +#include #include -#include "tpm2key.asn1.h" - static struct tpm2_hash tpm2_hash_map[] = { {HASH_ALGO_SHA1, TPM_ALG_SHA1}, {HASH_ALGO_SHA256, TPM_ALG_SHA256}, @@ -98,106 +97,6 @@ static int tpm2_key_encode(struct trusted_key_payload *payload, return ret; } -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 = 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 -E2BIG; - - blob = kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL); - if (!blob) - return -ENOMEM; - - *buf = blob; - options->keyhandle = ctx.parent; - - memcpy(blob, ctx.priv, ctx.priv_len); - blob += 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 = context; - const u8 *v = value; - int i; - - ctx->parent = 0; - for (i = 0; i < vlen; i++) { - ctx->parent <<= 8; - ctx->parent |= 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 = look_up_OID(value, vlen); - - if (oid != 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 = context; - - ctx->pub = value; - ctx->pub_len = 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 = context; - - ctx->priv = value; - ctx->priv_len = vlen; - - return 0; -} - /** * tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer. * @@ -387,22 +286,43 @@ static int tpm2_load_cmd(struct tpm_chip *chip, struct trusted_key_options *options, u32 *blob_handle) { - struct tpm_buf buf; unsigned int private_len; unsigned int public_len; unsigned int blob_len; - u8 *blob, *pub; - int rc; + struct tpm2_key *key; + const u8 *blob, *pub; + struct tpm_buf buf; u32 attrs; + int rc; - rc = tpm2_key_decode(payload, options, &blob); - if (rc) { - /* old form */ + key = tpm2_key_decode(payload->blob, payload->blob_len); + if (IS_ERR(key)) { + /* Get the error code and reset the pointer to the key: */ + rc = PTR_ERR(key); + key = NULL; + + if (rc == -ENOMEM) + return -ENOMEM; + + /* A sanity check, as only -EBADMSG or -ENOMEM are expected: */ + if (rc != -EBADMSG) + pr_err("tpm2_key_decode(): spurious error code %d\n", rc); + + /* Fallback to the legacy format: */ blob = payload->blob; payload->old_format = 1; + } else { + blob = tpm2_key_data(key); + if (key->oid != OID_TPMSealedData) { + kfree(key); + return -EBADMSG; + } } - /* new format carries keyhandle but old format doesn't */ + /* + * Must be non-zero here, either extracted from the ASN.1 for the new + * format or specified on the command line for the old. + */ if (!options->keyhandle) return -EINVAL; @@ -464,8 +384,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip, (__be32 *) &buf.data[TPM_HEADER_SIZE]); out: - if (blob != payload->blob) - kfree(blob); + kfree(key); tpm_buf_destroy(&buf); if (rc > 0) From patchwork Tue May 28 21:08:09 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 799612 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 B028117F391; Tue, 28 May 2024 21:08:52 +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=1716930532; cv=none; b=euaRFZAOY8zJ8ve83YVGXsSwDWx5M6tildPV2Bh1kCHdOzk4fLOWTbF2F+d4NZDR5apNyOKexmrWHxgef2T1J+Te5pgEMtoXbIG/+omqwOWDXqVfK+gdibLylVAYE7MgWG6uHfvTssVUgAyp7kuK2yBwyhWx7ziN0i7kqrnu/cA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716930532; c=relaxed/simple; bh=EWHB6qTYSbsxlpqEBR475FoQwTMhLEpw2cwYyM3OlHA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nv1V7Ffk6MeGEmA/je8DnUYUg6DWOffPG1HwBAILwP0/KydM0QOeeq5WJxULdt3SxjTvzdS7xKAuoLi/6w/WuWcRkdBsE/vK3WHYct15+2Q22QKw+bhNJu3upVDDK7NgSq/HrHiPSQPm/OotA+fu+D8ckCu+S0nvPvpIoIhAwHs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=OWh/c2GB; 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="OWh/c2GB" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 35ADFC3277B; Tue, 28 May 2024 21:08:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1716930532; bh=EWHB6qTYSbsxlpqEBR475FoQwTMhLEpw2cwYyM3OlHA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=OWh/c2GB5JK0y0fHHWP/fgvLY0LmqfUJK6qkWwcLgvTIwVgbgk1A/hA66wxUMPqUx fSgGsTiaa99NGF65O0j25xIAN2xof///+VEmPSMJVfblJ3nar8EvWVPl7VNvKveHc+ V2yt6fAW8rZqbah+pxKL2kdZjAFc0DifwR999xefPMguER3fsoPuTMcQk9qqp0bJls 3LDhNn9oZo9H9lg6IOJ0IgU2WAuneySrl7MyCQsG6naR/dQDORhQeje32vZVY6s7gj CSPdlG+LpumyQFTwcUNrpY9EF1aykdZpeYsdeOzcA1v8cJs6I0dIjtY0K1LxAxtIAI N5Z/0/Zo13aRQ== From: Jarkko Sakkinen To: Herbert Xu Cc: linux-integrity@vger.kernel.org, keyrings@vger.kernel.org, Andreas.Fuchs@infineon.com, James Prestwood , David Woodhouse , Eric Biggers , James Bottomley , linux-crypto@vger.kernel.org, Stefan Berger , Lennart Poettering , Jarkko Sakkinen , "David S. Miller" , linux-kernel@vger.kernel.org (open list), David Howells , James Bottomley , Ard Biesheuvel , Mario Limonciello Subject: [PATCH v7 4/5] keys: asymmetric: Add tpm2_key_rsa Date: Wed, 29 May 2024 00:08:09 +0300 Message-ID: <20240528210823.28798-5-jarkko@kernel.org> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240528210823.28798-1-jarkko@kernel.org> References: <20240528210823.28798-1-jarkko@kernel.org> Precedence: bulk X-Mailing-List: linux-crypto@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 * Asymmetric TPM2 RSA key with signing and verification. * Encryption and decryption when pcks1 encoding is used. * Enabled with CONFIG_ASYMMETRIC_TPM2_KEY_ECDSA_SUBTYPE. Signed-off-by: James Prestwood Co-developed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- v6: * Validate RSA parameters, and also that the blob has space for them. * Fix tpm2_key_rsa_destroy() memory corruption: cast to tpm2_key_rsa * Allocate temporary buffers from heap. * Rename tpm2_key_rsa_extract_pub to tpm2_key_rsa_probe. * While pre-parsing, return -EBADMSG when the probing fails. This translates to "not detected" for the framework, i.e. should not be considered as an error but instead "move on". E.g. TPM_ALG_RSA is checked and if it is instead TPM_ALG_ECDSA, then it is passed to that module. v5: * akcipher has two *undocumented* parameters. Document this clearly. * Remove unused variable. v4: * Just put the values to the buffer instead of encoding them. * Adjust buffer sizes. * Make tpm2_rsa_key_encode() not to allocate from heap and simplify the serialization. v3: * Drop the special case for null handle i.e. do not define policy. * Remove extra empty line. v2: * Remove two spurios pr_info() messsages that I forgot to remove. * Clean up padding functions and add additional checks for length also in tpm2_unpad_pcks1(). * Add the missing success check kzalloc() in tpm2_key_rsa_decrypt(). * Check that params->out_len for capacity before copying the result. --- crypto/asymmetric_keys/Kconfig | 15 + crypto/asymmetric_keys/Makefile | 1 + crypto/asymmetric_keys/tpm2_key_rsa.c | 678 ++++++++++++++++++++++++++ include/linux/tpm.h | 2 + 4 files changed, 696 insertions(+) create mode 100644 crypto/asymmetric_keys/tpm2_key_rsa.c diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index e1345b8f39f1..9d88c1190621 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,20 @@ 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. +config ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE + tristate "Asymmetric TPM2 RSA crypto algorithm subtype" + depends on TCG_TPM + select CRYPTO_RSA + select CRYPTO_SHA256 + select CRYPTO_HASH_INFO + select CRYPTO_TPM2_KEY + select ASN1 + help + This option provides support for asymmetric TPM2 key type handling. + If signature generation and/or verification are to be used, + appropriate hash algorithms (such as SHA-256) must be available. + 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/Makefile index bc65d3b98dcb..c6da84607824 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -11,6 +11,7 @@ asymmetric_keys-y := \ signature.o obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o +obj-$(CONFIG_ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE) += tpm2_key_rsa.o # # X.509 Certificate handling diff --git a/crypto/asymmetric_keys/tpm2_key_rsa.c b/crypto/asymmetric_keys/tpm2_key_rsa.c new file mode 100644 index 000000000000..4bc322580037 --- /dev/null +++ b/crypto/asymmetric_keys/tpm2_key_rsa.c @@ -0,0 +1,678 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* TPM2 asymmetric public-key crypto subtype + * + * Asymmetric TPM2 RSA key: + * - Decrypts RSA with TPM2_RSA_Decrypt. + * - Signs with PKCS#1 1.5 padding. Signing is implemented with + * TPM2_RSA_Decrypt operation. + * - Encrypts with the akcipher rsa-pcks1pad. + * + * See Documentation/crypto/asymmetric-keys.rst + * + * Copyright (c) 2020 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef pr_fmt +#define pr_fmt(fmt) "tpm2_key_rsa: "fmt + +#define PKCS1_PAD_MIN_SIZE 11 + +/* TPM2 Structures 12.2.3.5: TPMS_RSA_PARMS */ +struct tpm2_rsa_parms { + __be16 symmetric; + __be16 scheme; + __be16 key_bits; + __be32 exponent; + __be16 modulus_size; +} __packed; + +/* + * Fill the data with PKCS#1 v1.5 padding. + */ +static int tpm2_pad_pkcs1(const u8 *in, int in_len, u8 *out, int out_len) +{ + unsigned int prefix_len = out_len - in_len - 3; + + if (in_len > out_len - PKCS1_PAD_MIN_SIZE) + return -EBADMSG; + + /* prefix */ + out[0] = 0; + out[1] = 1; + memset(&out[2], 0xff, prefix_len); + out[2 + prefix_len] = 0; + /* payload */ + memcpy(&out[2 + prefix_len + 1], in, in_len); + + return 0; +} + +/* + * RFC 3447 - Section 7.2.2 + * Size of the input data should be checked against public key size by + * the caller. + */ +static const u8 *tpm2_unpad_pkcs1(const u8 *in, int in_len, int *out_len) +{ + int i; + + if (in[0] != 0 || in[1] != 2) + return NULL; + + i = 2; + while (in[i] != 0 && i < in_len) + i++; + + if (i == in_len || i < (PKCS1_PAD_MIN_SIZE - 1)) + return NULL; + + *out_len = in_len - i - 1; + return in + i + 1; +} + +/* + * Outputs the cipher algorithm name on success, and retuns -ENOPKG + * on failure. + */ +static int tpm2_key_get_akcipher(const char *encoding, const char *hash_algo, + char *cipher) +{ + ssize_t ret; + + if (strcmp(encoding, "pkcs1") == 0) { + if (!hash_algo) { + strcpy(cipher, "pkcs1pad(rsa)"); + return 0; + } + + ret = snprintf(cipher, CRYPTO_MAX_ALG_NAME, + "pkcs1pad(rsa,%s)", + hash_algo); + if (ret >= CRYPTO_MAX_ALG_NAME) + return -ENOPKG; + + return 0; + } + + if (strcmp(encoding, "raw") == 0) { + strcpy(cipher, "rsa"); + return 0; + } + + return -ENOPKG; +} + +static int tpm2_key_rsa_encode(const struct tpm2_key *key, u8 *buf) +{ + const off_t o = key->priv_len + 2 + sizeof(*key->desc); + const struct tpm2_rsa_parms *p = + (const struct tpm2_rsa_parms *)&key->data[o]; + const u16 mod_size = be16_to_cpu(p->modulus_size); + const void *mod = &key->data[o + sizeof(*p)]; + + u8 *start = &buf[4]; + u8 *work = &buf[4]; + u32 seq_len; + + work[0] = 0x02; /* INTEGER */ + work[1] = 0x82; /* u16 */ + work[2] = mod_size >> 8; + work[3] = mod_size & 0xff; + work = &work[4]; + memcpy(work, mod, mod_size); + work = &work[mod_size]; + work[0] = 0x02; /* INTEGER */ + work[1] = 3; /* < 128 */ + work[2] = 1; /* 65537 */ + work[3] = 0; + work[4] = 1; + work = &work[5]; + seq_len = work - start; + buf[0] = 0x30; /* SEQUENCE */ + buf[1] = 0x82; /* u16 */ + buf[2] = seq_len >> 8; + buf[3] = seq_len & 0xff; + + /* + * ABI requires this according include/crypto/akcipher.h, which says + * that there is epilogue with algorithm OID and parameters length. + * Neither size nor semantics is documented *anywhere*, and there's no + * struct to hold them. + * + * So zeroing out the last eight bytes after the key blob seems like the + * best bet, given no better (or any) information. The size of the + * parameters (two u32's) was found from crypto/asymmetric/public_key.c. + */ + memset(work, 0, 8); + + return seq_len + 4; +} + +/* + * Encryption operation is performed with the public key. Hence it is done + * in software + */ +static int tpm2_key_rsa_encrypt(struct tpm2_key *key, + struct kernel_pkey_params *params, + const void *in, void *out) +{ + char cipher[CRYPTO_MAX_ALG_NAME]; + struct scatterlist in_sg, out_sg; + struct akcipher_request *req; + struct crypto_akcipher *tfm; + struct crypto_wait cwait; + u8 *buf; + int ret; + + buf = kzalloc(TPM2_KEY_BYTES_MAX, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = tpm2_key_get_akcipher(params->encoding, params->hash_algo, cipher); + if (ret < 0) + goto err_buf; + + tfm = crypto_alloc_akcipher(cipher, 0, 0); + if (IS_ERR(tfm)) { + ret = PTR_ERR(tfm); + goto err_buf; + } + + ret = tpm2_key_rsa_encode(key, buf); + if (ret < 0) + goto err_tfm; + + ret = crypto_akcipher_set_pub_key(tfm, buf, ret); + if (ret < 0) + goto err_tfm; + + req = akcipher_request_alloc(tfm, GFP_KERNEL); + if (!req) { + ret = -ENOMEM; + goto err_tfm; + } + + sg_init_one(&in_sg, in, params->in_len); + sg_init_one(&out_sg, out, params->out_len); + akcipher_request_set_crypt(req, &in_sg, &out_sg, params->in_len, + params->out_len); + + crypto_init_wait(&cwait); + akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_req_done, &cwait); + + ret = crypto_akcipher_encrypt(req); + if (ret) + goto err_tfm; + + ret = crypto_wait_req(ret, &cwait); + if (!ret) + ret = req->dst_len; + + akcipher_request_free(req); + +err_tfm: + crypto_free_akcipher(tfm); + +err_buf: + kfree(buf); + return ret; +} + +static int __tpm2_key_rsa_decrypt(struct tpm_chip *chip, + struct tpm2_key *key, + struct kernel_pkey_params *params, + const void *in, int in_len, void *out) +{ + u32 key_handle = 0; + struct tpm_buf buf; + u16 decrypted_len; + u8 *pos; + int ret; + + ret = tpm_try_get_ops(chip); + if (ret) + return ret; + + ret = tpm2_start_auth_session(chip); + if (ret) + goto err_ops; + + ret = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD); + if (ret < 0) + goto err_auth; + + tpm_buf_append_name(chip, &buf, key->parent, NULL); + 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 = -E2BIG; + goto err_buf; + } + tpm_buf_fill_hmac_session(chip, &buf); + ret = tpm_transmit_cmd(chip, &buf, 4, "TPM2_CC_LOAD"); + ret = tpm_buf_check_hmac_response(chip, &buf, ret); + if (ret) { + ret = -EIO; + goto err_buf; + } + key_handle = be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE]); + + tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_RSA_DECRYPT); + tpm_buf_append_name(chip, &buf, key_handle, NULL); + tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT, NULL, 0); + tpm_buf_append_u16(&buf, in_len); + tpm_buf_append(&buf, in, in_len); + tpm_buf_append_u16(&buf, TPM_ALG_NULL); + tpm_buf_append_u16(&buf, 0); + tpm_buf_fill_hmac_session(chip, &buf); + ret = tpm_transmit_cmd(chip, &buf, 4, "TPM2_RSA_DECRYPT"); + ret = tpm_buf_check_hmac_response(chip, &buf, ret); + if (ret) { + ret = -EIO; + goto err_blob; + } + + pos = buf.data + TPM_HEADER_SIZE + 4; + decrypted_len = be16_to_cpup((__be16 *)pos); + pos += 2; + + if (params->out_len < decrypted_len) { + ret = -EMSGSIZE; + goto err_blob; + } + + memcpy(out, pos, decrypted_len); + ret = decrypted_len; + +err_blob: + tpm2_flush_context(chip, key_handle); + +err_buf: + tpm_buf_destroy(&buf); + +err_auth: + if (ret < 0) + tpm2_end_auth_session(chip); + +err_ops: + tpm_put_ops(chip); + return ret; +} + +static int tpm2_key_rsa_decrypt(struct tpm_chip *chip, struct tpm2_key *key, + struct kernel_pkey_params *params, + const void *in, void *out) +{ + const u8 *ptr; + int out_len; + u8 *work; + int ret; + + work = kzalloc(TPM2_KEY_BYTES_MAX, GFP_KERNEL); + if (!work) + return -ENOMEM; + + ret = __tpm2_key_rsa_decrypt(chip, key, params, in, params->in_len, + work); + if (ret < 0) + goto err; + + ptr = tpm2_unpad_pkcs1(work, ret, &out_len); + if (!ptr) { + ret = -EINVAL; + goto err; + } + + if (out_len > params->out_len) { + ret = -EMSGSIZE; + goto err; + } + + memcpy(out, ptr, out_len); + kfree(work); + return out_len; + +err: + kfree(work); + return ret; +} + +/* + * Sign operation is an encryption using the TPM's private key. With RSA the + * only difference between encryption and decryption is where the padding goes. + * Since own padding can be used, TPM2_RSA_Decrypt can be repurposed to do + * encryption. + */ +static int tpm2_key_rsa_sign(struct tpm_chip *chip, struct tpm2_key *key, + struct kernel_pkey_params *params, + const void *in, void *out) +{ + const off_t o = key->priv_len + 2 + sizeof(*key->desc); + const struct tpm2_rsa_parms *p = + (const struct tpm2_rsa_parms *)&key->data[o]; + const u16 mod_size = be16_to_cpu(p->modulus_size); + const struct rsa_asn1_template *asn1; + u32 in_len = params->in_len; + void *asn1_wrapped = NULL; + u8 *padded; + int ret; + + if (strcmp(params->encoding, "pkcs1") != 0) { + ret = -ENOPKG; + goto err; + } + + if (params->hash_algo) { + asn1 = rsa_lookup_asn1(params->hash_algo); + if (!asn1) { + ret = -ENOPKG; + goto err; + } + + /* Request enough space for the ASN.1 template + input hash */ + asn1_wrapped = kzalloc(in_len + asn1->size, GFP_KERNEL); + if (!asn1_wrapped) { + ret = -ENOMEM; + goto err; + } + + /* Copy ASN.1 template, then the input */ + memcpy(asn1_wrapped, asn1->data, asn1->size); + memcpy(asn1_wrapped + asn1->size, in, in_len); + + in = asn1_wrapped; + in_len += asn1->size; + } + + /* with padding: */ + padded = kmalloc(mod_size, GFP_KERNEL); + tpm2_pad_pkcs1(in, in_len, padded, mod_size); + ret = __tpm2_key_rsa_decrypt(chip, key, params, padded, mod_size, out); + kfree(padded); + +err: + kfree(asn1_wrapped); + return ret; +} + +static void tpm2_key_rsa_describe(const struct key *asymmetric_key, + struct seq_file *m) +{ + struct tpm2_key *key = asymmetric_key->payload.data[asym_crypto]; + + if (!key) { + pr_err("key blob missing"); + return; + } + + seq_puts(m, "TPM2/RSA"); +} + +static void tpm2_key_rsa_destroy(void *payload0, void *payload3) +{ + struct tpm2_key *key = payload0; + + if (!key) + return; + + kfree(key); +} + +static int tpm2_key_rsa_eds_op(struct kernel_pkey_params *params, + const void *in, void *out) +{ + struct tpm2_key *key = params->key->payload.data[asym_crypto]; + struct tpm_chip *chip = tpm_default_chip(); + + if (!chip) + return -ENODEV; + + switch (params->op) { + case kernel_pkey_encrypt: + return tpm2_key_rsa_encrypt(key, params, in, out); + case kernel_pkey_decrypt: + return tpm2_key_rsa_decrypt(chip, key, params, in, out); + case kernel_pkey_sign: + return tpm2_key_rsa_sign(chip, key, params, in, out); + default: + return -EOPNOTSUPP; + } +} + +static int tpm2_key_rsa_verify(const struct key *key, + const struct public_key_signature *sig) +{ + const struct tpm2_key *tpm2_key = key->payload.data[asym_crypto]; + char alg_name[CRYPTO_MAX_ALG_NAME]; + struct akcipher_request *req; + struct scatterlist src_sg[2]; + struct crypto_akcipher *tfm; + struct crypto_wait cwait; + u8 *buf; + int ret; + + if (!sig->digest) + return -ENOPKG; + + ret = tpm2_key_get_akcipher(sig->encoding, sig->hash_algo, alg_name); + if (ret < 0) + return ret; + + buf = kzalloc(TPM2_KEY_BYTES_MAX, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + tfm = crypto_alloc_akcipher(alg_name, 0, 0); + if (IS_ERR(tfm)) { + ret = PTR_ERR(tfm); + goto err_buf; + } + + ret = tpm2_key_rsa_encode(tpm2_key, buf); + if (ret < 0) + goto err_tfm; + + ret = crypto_akcipher_set_pub_key(tfm, buf, ret); + if (ret < 0) + goto err_tfm; + + ret = -ENOMEM; + req = akcipher_request_alloc(tfm, GFP_KERNEL); + if (!req) + goto err_tfm; + + sg_init_table(src_sg, 2); + sg_set_buf(&src_sg[0], sig->s, sig->s_size); + sg_set_buf(&src_sg[1], sig->digest, sig->digest_size); + akcipher_request_set_crypt(req, src_sg, NULL, sig->s_size, + sig->digest_size); + crypto_init_wait(&cwait); + akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_req_done, &cwait); + ret = crypto_wait_req(crypto_akcipher_verify(req), &cwait); + + akcipher_request_free(req); + +err_tfm: + crypto_free_akcipher(tfm); + +err_buf: + kfree(buf); + return ret; +} + +static int tpm2_key_rsa_query(const struct kernel_pkey_params *params, + struct kernel_pkey_query *info) +{ + const struct tpm2_key *key = params->key->payload.data[asym_crypto]; + const off_t o = key->priv_len + 2 + sizeof(*key->desc); + const struct tpm2_rsa_parms *p = + (const struct tpm2_rsa_parms *)&key->data[o]; + const u16 mod_size = be16_to_cpu(p->modulus_size); + char alg_name[CRYPTO_MAX_ALG_NAME]; + struct crypto_akcipher *tfm; + unsigned int len; + u8 *buf; + int ret; + + ret = tpm2_key_get_akcipher(params->encoding, params->hash_algo, alg_name); + if (ret < 0) + return ret; + + buf = kzalloc(TPM2_KEY_BYTES_MAX, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + tfm = crypto_alloc_akcipher(alg_name, 0, 0); + if (IS_ERR(tfm)) { + ret = PTR_ERR(tfm); + goto err_buf; + } + + ret = tpm2_key_rsa_encode(key, buf); + if (ret < 0) + goto err_tfm; + + ret = crypto_akcipher_set_pub_key(tfm, buf, ret); + if (ret < 0) + goto err_tfm; + + len = crypto_akcipher_maxsize(tfm); + + info->key_size = mod_size * 8; + info->max_data_size = mod_size; + info->max_sig_size = len; + info->max_enc_size = len; + info->max_dec_size = mod_size; + + info->supported_ops = KEYCTL_SUPPORTS_SIGN | KEYCTL_SUPPORTS_VERIFY; + + if (!strcmp(params->encoding, "pkcs1")) { + pr_info("pkcs1\n"); + info->supported_ops = + KEYCTL_SUPPORTS_ENCRYPT | KEYCTL_SUPPORTS_DECRYPT; + } + +err_tfm: + crypto_free_akcipher(tfm); + return ret; + +err_buf: + kfree(buf); + return ret; +} + +struct asymmetric_key_subtype tpm2_key_rsa_subtype = { + .owner = THIS_MODULE, + .name = "tpm2_key_rsa", + .name_len = sizeof("tpm2_key_rsa") - 1, + .describe = tpm2_key_rsa_describe, + .destroy = tpm2_key_rsa_destroy, + .query = tpm2_key_rsa_query, + .eds_op = tpm2_key_rsa_eds_op, + .verify_signature = tpm2_key_rsa_verify, +}; +EXPORT_SYMBOL_GPL(tpm2_key_rsa_subtype); + +static int __tpm2_key_rsa_preparse(struct tpm2_key *key) +{ + const off_t o = key->priv_len + 2 + sizeof(*key->desc); + const struct tpm2_rsa_parms *p = + (const struct tpm2_rsa_parms *)&key->data[o]; + + if (tpm2_key_type(key) != TPM_ALG_RSA) + return -EBADMSG; + + if (tpm2_key_policy_size(key) != 0) + return -EBADMSG; + + if (be16_to_cpu(p->symmetric) != TPM_ALG_NULL) + return -EBADMSG; + + if (be16_to_cpu(p->scheme) != TPM_ALG_NULL) + return -EBADMSG; + + if (be16_to_cpu(p->key_bits) != 2048 && + be16_to_cpu(p->key_bits) != 3072 && + be16_to_cpu(p->key_bits) != 4096) + return -EBADMSG; + + if (be32_to_cpu(p->exponent) != 0x00000000 && + be32_to_cpu(p->exponent) != 0x00010001) + return -EBADMSG; + + pr_debug("modulus_size=%u\n", be16_to_cpu(p->modulus_size)); + return 0; +} + +/* + * Attempt to parse a data blob for a key as a TPM private key blob. + */ +static int tpm2_key_rsa_preparse(struct key_preparsed_payload *prep) +{ + struct tpm2_key *key; + int ret; + + key = tpm2_key_decode(prep->data, prep->datalen); + if (IS_ERR(key)) + return ret; + + if (key->oid != OID_TPMLoadableKey) { + kfree(key); + return -EBADMSG; + } + + ret = __tpm2_key_rsa_preparse(key); + if (ret < 0) { + kfree(key); + return ret; + } + + prep->payload.data[asym_subtype] = &tpm2_key_rsa_subtype; + prep->payload.data[asym_key_ids] = NULL; + prep->payload.data[asym_crypto] = key; + prep->payload.data[asym_auth] = NULL; + prep->quotalen = 100; + + return 0; +} + +static struct asymmetric_key_parser tpm2_key_rsa_parser = { + .owner = THIS_MODULE, + .name = "tpm2_key_rsa_parser", + .parse = tpm2_key_rsa_preparse, +}; + +static int __init tpm2_key_rsa_init(void) +{ + return register_asymmetric_key_parser(&tpm2_key_rsa_parser); +} + +static void __exit tpm2_key_rsa_exit(void) +{ + unregister_asymmetric_key_parser(&tpm2_key_rsa_parser); +} + +module_init(tpm2_key_rsa_init); +module_exit(tpm2_key_rsa_exit); + +MODULE_DESCRIPTION("Asymmetric TPM2 RSA key"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 21a67dc9efe8..d0860af7a56d 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -43,6 +43,7 @@ enum tpm2_session_types { /* if you add a new hash to this, increment TPM_MAX_HASHES below */ enum tpm_algorithms { TPM_ALG_ERROR = 0x0000, + TPM_ALG_RSA = 0x0001, TPM_ALG_SHA1 = 0x0004, TPM_ALG_AES = 0x0006, TPM_ALG_KEYEDHASH = 0x0008, @@ -271,6 +272,7 @@ enum tpm2_command_codes { TPM2_CC_NV_READ = 0x014E, TPM2_CC_CREATE = 0x0153, TPM2_CC_LOAD = 0x0157, + TPM2_CC_RSA_DECRYPT = 0x0159, TPM2_CC_SEQUENCE_UPDATE = 0x015C, TPM2_CC_UNSEAL = 0x015E, TPM2_CC_CONTEXT_LOAD = 0x0161, From patchwork Tue May 28 21:08:10 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 800277 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 20E3E181330; Tue, 28 May 2024 21:09:00 +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=1716930541; cv=none; b=VyK2ZNhHoaSNzROO2lOgj2g0SZHuHJ5405c035QciWH0MNyOZbtveHDNO47YarxqQvaak6f7Sk/pjxC8iDIU7aozyeEFuELG4krbB4ei5SxBGW9foMp8D0PP7mW2K8vbh75yPg0zfU+driny1cJvB5sgmCHahNXN0eh7JbMWG8o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716930541; c=relaxed/simple; bh=OHaLvd3u2PG4gh4eSpL4tDMpwijWMcMJukxRPnN7rXY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=WdTaTYlanoYZe29EZOfGmCRKuj1o0bY/crAwSkWvcDzFvNFPDmWE1TcWoJffasnNSXe/L3mEwSHUVduTKKZfGAOMnWRQhrz9yjPN1DmByH5Fi8YOeKUUPuBtFm+Q0t60MqpZvJR2mZu07H1VwAiXQzQrdqri1uxr6HCTMKjyq1E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=K+RhHfLZ; 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="K+RhHfLZ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6157CC3277B; Tue, 28 May 2024 21:09:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1716930540; bh=OHaLvd3u2PG4gh4eSpL4tDMpwijWMcMJukxRPnN7rXY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=K+RhHfLZObmYkj+ji3lYtT6HTS2Qt4AzvTv+tnJ5LMkTPKd3sX9Ei8b9KGhOfKJsw NI+YB8u8R5tul7dFswMUXVGDMq5qaWwSGOs/bbd+5q9XX4gCpDMzDTbdBOj+oeoClZ OeQ+5KiuiKBAti/8Et+DfgKSx9qhw+VeMySIPzdS3ufyijbmRKblM0ccucl0+vk5A3 YLWjh+rXXPEpacuVzZgMDtu3b+/2KqMV1lpGb4rPdELfmlx2vhBbxAGt/nSNyYIja2 8YJof2vWl3ZYybiUu3sbIb+x1/2WYioQUmZZsK1CtAJnlgnz/3TfvWUBTaH17ZSag+ yPylck74PVRjg== From: Jarkko Sakkinen To: Herbert Xu Cc: linux-integrity@vger.kernel.org, keyrings@vger.kernel.org, Andreas.Fuchs@infineon.com, James Prestwood , David Woodhouse , Eric Biggers , James Bottomley , linux-crypto@vger.kernel.org, Stefan Berger , Lennart Poettering , Jarkko Sakkinen , "David S. Miller" , linux-kernel@vger.kernel.org (open list), David Howells , Peter Huewe , Jason Gunthorpe , James Bottomley , Ard Biesheuvel , Mario Limonciello Subject: [PATCH v7 5/5] keys: asymmetric: Add tpm2_key_ecdsa Date: Wed, 29 May 2024 00:08:10 +0300 Message-ID: <20240528210823.28798-6-jarkko@kernel.org> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240528210823.28798-1-jarkko@kernel.org> References: <20240528210823.28798-1-jarkko@kernel.org> Precedence: bulk X-Mailing-List: linux-crypto@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 * Asymmetric TPM2 ECDSA key with signing and verification. * Enabled with CONFIG_ASYMMETRIC_TPM2_KEY_ECDSA_SUBTYPE. Cc: Stefan Berger Signed-off-by: Jarkko Sakkinen --- v7: * Rewrote the signature encoder. * Added the missing sha256() call to the signature verifier. v6: * The very first version. * Stefan: any idea why the signature give -EKEYREJECTED? --- crypto/asymmetric_keys/Kconfig | 15 + crypto/asymmetric_keys/Makefile | 1 + crypto/asymmetric_keys/tpm2_key_ecdsa.c | 462 ++++++++++++++++++++++++ crypto/ecdsa.c | 1 - drivers/char/tpm/tpm-buf.c | 2 +- include/linux/tpm.h | 7 + 6 files changed, 486 insertions(+), 2 deletions(-) create mode 100644 crypto/asymmetric_keys/tpm2_key_ecdsa.c diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 9d88c1190621..c97f11e0340c 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -24,6 +24,21 @@ 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. +config ASYMMETRIC_TPM2_KEY_ECDSA_SUBTYPE + tristate "Asymmetric TPM2 ECDSA crypto algorithm subtype" + depends on TCG_TPM + select CRYPTO_ECDSA + 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. + If signature generation and/or verification are to be used, + appropriate hash algorithms (such as SHA-256) must be available. + ENOPKG will be reported if the requisite algorithm is unavailable. + config ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE tristate "Asymmetric TPM2 RSA crypto algorithm subtype" depends on TCG_TPM diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index c6da84607824..0843d2268a69 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -11,6 +11,7 @@ asymmetric_keys-y := \ signature.o obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o +obj-$(CONFIG_ASYMMETRIC_TPM2_KEY_ECDSA_SUBTYPE) += tpm2_key_ecdsa.o obj-$(CONFIG_ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE) += tpm2_key_rsa.o # diff --git a/crypto/asymmetric_keys/tpm2_key_ecdsa.c b/crypto/asymmetric_keys/tpm2_key_ecdsa.c new file mode 100644 index 000000000000..e2f599a0ffe0 --- /dev/null +++ b/crypto/asymmetric_keys/tpm2_key_ecdsa.c @@ -0,0 +1,462 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Asymmetric TPM2 ECDSA key subtype. + * + * See Documentation/crypto/asymmetric-keys.rst + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef pr_fmt +#define pr_fmt(fmt) "tpm2_key_ecdsa: "fmt + +struct tpm2_ecc_parms { + __be16 symmetric; + __be16 scheme; + __be16 ecc; + __be16 kdf; +}; + +static const u8 *tpm2_key_ecdsa_ecc_x(const struct tpm2_key *key) +{ + const off_t o = key->priv_len + 2 + sizeof(*key->desc); + + return &key->data[o + sizeof(struct tpm2_ecc_parms)]; +} + +static const u8 *tpm2_key_ecdsa_ecc_y(const struct tpm2_key *key) +{ + const u8 *x = tpm2_key_ecdsa_ecc_x(key); + u16 x_size = get_unaligned_be16(&x[0]); + + /* +2 from the size field: */ + return &x[2 + x_size]; +} + +static void tpm2_key_ecdsa_describe(const struct key *asymmetric_key, + struct seq_file *m) +{ + struct tpm2_key *key = asymmetric_key->payload.data[asym_crypto]; + + if (!key) { + pr_err("key missing"); + return; + } + + seq_puts(m, "TPM2/ECDSA"); +} + +static void tpm2_key_ecdsa_destroy(void *payload0, void *payload3) +{ + struct tpm2_key *key = payload0; + + if (!key) + return; + + kfree(key); +} + +static const char *tpm2_ecc_name(u16 ecc) +{ + const char *name; + + switch (ecc) { + case TPM2_ECC_NIST_P521: + name = "ecdsa-nist-p521"; + break; + case TPM2_ECC_NIST_P384: + name = "ecdsa-nist-p384"; + break; + default: + name = "ecdsa-nist-p256"; + break; + } + + return name; +} + +static int tpm2_key_ecdsa_query(const struct kernel_pkey_params *params, + struct kernel_pkey_query *info) +{ + const struct tpm2_key *key = params->key->payload.data[asym_crypto]; + const off_t o = key->priv_len + 2 + sizeof(*key->desc); + const struct tpm2_ecc_parms *p = + (const struct tpm2_ecc_parms *)&key->data[o]; + u16 ecc = be16_to_cpu(p->ecc); + const char *ecc_name = tpm2_ecc_name(ecc); + const u8 *x = tpm2_key_ecdsa_ecc_x(key); + u16 x_size = get_unaligned_be16(&x[0]); + struct crypto_akcipher *tfm; + char data[256]; + u8 *ptr; + int ret; + + memset(data, 0, sizeof(data)); + + tfm = crypto_alloc_akcipher(ecc_name, 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + /* Probe for ecdsa_set_pub_key(): */ + ptr = &data[0]; + *ptr++ = 0x04; /* uncompressed */ + memcpy(&ptr[0], &x[2], x_size); + memcpy(&ptr[x_size], &x[2 + x_size + 2], x_size); + ret = crypto_akcipher_set_pub_key(tfm, data, 2 * x_size + 1); + crypto_free_akcipher(tfm); + if (ret < 0) + return ret; + + info->max_sig_size = 256; + info->key_size = 256; + info->max_data_size = 256; + info->supported_ops = KEYCTL_SUPPORTS_SIGN | KEYCTL_SUPPORTS_VERIFY; + return ret; +} + +static int tpm2_key_ecdsa_sign(struct tpm_chip *chip, struct tpm2_key *key, + struct kernel_pkey_params *params, + const void *in, void *out) +{ + u8 r[SHA256_DIGEST_SIZE], s[SHA256_DIGEST_SIZE]; + u32 in_len = params->in_len; + bool r_0, s_0; + struct tpm_header *head; + struct tpm_buf buf; + u32 key_handle; + u8 *ptr = out; + off_t offset; + int ret; + + + /* Require explicit hash algorithm: */ + if (!params->hash_algo) + return -EINVAL; + + /* Currently only support SHA256: */ + if (!!strcmp(params->hash_algo, "sha256")) + return -EINVAL; + + ret = tpm_try_get_ops(chip); + if (ret) + return ret; + + ret = tpm2_start_auth_session(chip); + if (ret) + goto err_ops; + + ret = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD); + if (ret < 0) { + tpm2_end_auth_session(chip); + goto err_ops; + } + + tpm_buf_append_name(chip, &buf, key->parent, NULL); + 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) { + tpm2_end_auth_session(chip); + ret = -E2BIG; + goto err_buf; + } + tpm_buf_fill_hmac_session(chip, &buf); + ret = tpm_transmit_cmd(chip, &buf, 4, "ECDSA loading"); + ret = tpm_buf_check_hmac_response(chip, &buf, ret); + if (ret) { + tpm2_end_auth_session(chip); + ret = -EIO; + goto err_buf; + } + + key_handle = be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE]); + + tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_SIGN); + tpm_buf_append_name(chip, &buf, key_handle, NULL); + tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT, NULL, 0); + + sha256(in, in_len, r); + tpm_buf_append_u16(&buf, SHA256_DIGEST_SIZE); + tpm_buf_append(&buf, r, SHA256_DIGEST_SIZE); + tpm_buf_append_u16(&buf, TPM_ALG_ECDSA); + tpm_buf_append_u16(&buf, TPM_ALG_SHA256); + + /* 10.7.2 A NULL Ticket */ + tpm_buf_append_u16(&buf, TPM2_ST_HASHCHECK); + tpm_buf_append_u32(&buf, TPM2_RH_NULL); + tpm_buf_append_u16(&buf, 0); + + tpm_buf_fill_hmac_session(chip, &buf); + ret = tpm_transmit_cmd(chip, &buf, 4, "ECDSA signing"); + ret = tpm_buf_check_hmac_response(chip, &buf, ret); + if (ret) { + tpm2_end_auth_session(chip); + ret = -EIO; + goto err_key_handle; + } + + /* Move to parameters: */ + head = (struct tpm_header *)buf.data; + offset = sizeof(*head); + if (be16_to_cpu(head->tag) == TPM2_ST_SESSIONS) + offset += 4; + + ret = -EIO; + + /* Copy R: */ + if (tpm_buf_read_u16(&buf, &offset) != TPM_ALG_ECDSA || + tpm_buf_read_u16(&buf, &offset) != TPM_ALG_SHA256 || + tpm_buf_read_u16(&buf, &offset) != SHA256_DIGEST_SIZE) { + pr_warn("offset=%u\n", offset); + goto err_key_handle; + } + + tpm_buf_read(&buf, &offset, SHA256_DIGEST_SIZE, r); + r_0 = (r[0] & 0x80) != 0; + pr_info("r_0=%d\n", r_0); + + /* Copy S: */ + if (tpm_buf_read_u16(&buf, &offset) != SHA256_DIGEST_SIZE) { + pr_warn("offset=%u\n", offset); + goto err_key_handle; + } + + tpm_buf_read(&buf, &offset, SHA256_DIGEST_SIZE, s); + s_0 = (r[0] & 0x80) != 0; + pr_info("s_0=%d\n", r_0); + + /* Encode the ASN.1 signature: */ +#define TPM2_KEY_ECDSA_SIG_SIZE (2 + 2 * (2 + SHA256_DIGEST_SIZE) + r_0 + s_0) + pr_info("sig_size=%d\n", TPM2_KEY_ECDSA_SIG_SIZE); + ptr[0] = 0x30; /* SEQUENCE */ + ptr[1] = TPM2_KEY_ECDSA_SIG_SIZE - 2; +#define TPM2_KEY_ECDSA_SIG_R_TAG 2 +#define TPM2_KEY_ECDSA_SIG_R_SIZE 3 +#define TPM2_KEY_ECDSA_SIG_R_BODY 4 + ptr[TPM2_KEY_ECDSA_SIG_R_TAG] = 0x02; /* INTEGER */ + ptr[TPM2_KEY_ECDSA_SIG_R_SIZE] = SHA256_DIGEST_SIZE + r_0; + ptr[TPM2_KEY_ECDSA_SIG_R_BODY] = 0x00; /* maybe dummy write */ + memcpy(&ptr[TPM2_KEY_ECDSA_SIG_R_BODY + r_0], r, SHA256_DIGEST_SIZE); +#define TPM2_KEY_ECDSA_SIG_S_TAG (4 + r_0 + SHA256_DIGEST_SIZE) +#define TPM2_KEY_ECDSA_SIG_S_SIZE (5 + r_0 + SHA256_DIGEST_SIZE) +#define TPM2_KEY_ECDSA_SIG_S_BODY (6 + r_0 + SHA256_DIGEST_SIZE) + ptr[TPM2_KEY_ECDSA_SIG_S_TAG] = 0x02; /* INTEGER */ + ptr[TPM2_KEY_ECDSA_SIG_S_SIZE] = SHA256_DIGEST_SIZE + s_0; + ptr[TPM2_KEY_ECDSA_SIG_S_BODY] = 0x00; /* maybe dummy write */ + memcpy(&ptr[TPM2_KEY_ECDSA_SIG_S_BODY + s_0], s, SHA256_DIGEST_SIZE); + ret = TPM2_KEY_ECDSA_SIG_SIZE; + +err_key_handle: + tpm2_flush_context(chip, key_handle); + +err_buf: + tpm_buf_destroy(&buf); + +err_ops: + tpm_put_ops(chip); + return ret; +} + +static int tpm2_key_ecdsa_eds_op(struct kernel_pkey_params *params, + const void *in, void *out) +{ + struct tpm2_key *key = params->key->payload.data[asym_crypto]; + struct tpm_chip *chip = tpm_default_chip(); + + if (!chip) + return -ENODEV; + + switch (params->op) { + case kernel_pkey_sign: + return tpm2_key_ecdsa_sign(chip, key, params, in, out); + default: + return -EOPNOTSUPP; + } +} + +static int tpm2_key_ecdsa_verify_signature(const struct key *key, + const struct public_key_signature *sig) +{ + const struct tpm2_key *tpm2_key = key->payload.data[asym_crypto]; + const off_t o = tpm2_key->priv_len + 2 + sizeof(*tpm2_key->desc); + const struct tpm2_ecc_parms *p = + (const struct tpm2_ecc_parms *)&tpm2_key->data[o]; + u16 ecc = be16_to_cpu(p->ecc); + const char *ecc_name = tpm2_ecc_name(ecc); + const u8 *x = tpm2_key_ecdsa_ecc_x(tpm2_key); + u16 x_size = get_unaligned_be16(&x[0]); + struct akcipher_request *req; + struct scatterlist src_sg[2]; + struct crypto_akcipher *tfm; + struct crypto_wait cwait; + char data[256]; + u8 *ptr; + int ret; + + tfm = crypto_alloc_akcipher(ecc_name, 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + ptr = &data[0]; + *ptr++ = 0x04; /* uncompressed */ + memcpy(&ptr[0], &x[2], x_size); + memcpy(&ptr[x_size], &x[2 + x_size + 2], x_size); + ret = crypto_akcipher_set_pub_key(tfm, data, 2 * x_size + 1); + if (ret) + goto err_tfm; + + sha256(sig->digest, sig->digest_size, data); + + ret = -ENOMEM; + req = akcipher_request_alloc(tfm, GFP_KERNEL); + if (!req) + goto err_tfm; + + sg_init_table(src_sg, 2); + sg_set_buf(&src_sg[0], sig->s, sig->s_size); + sg_set_buf(&src_sg[1], data, SHA256_DIGEST_SIZE); + + akcipher_request_set_crypt(req, src_sg, NULL, sig->s_size, + SHA256_DIGEST_SIZE); + + crypto_init_wait(&cwait); + akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_req_done, &cwait); + + ret = crypto_wait_req(crypto_akcipher_verify(req), &cwait); + if (ret) { + pr_warn("crypto_wait_req() failed with %d\n", ret); + goto err_tfm; + } + + akcipher_request_free(req); + +err_tfm: + crypto_free_akcipher(tfm); + return ret; +} + +/* + * Asymmetric TPM2 ECDSA key. Signs and decrypts with TPM. + */ +struct asymmetric_key_subtype tpm2_key_ecdsa_subtype = { + .owner = THIS_MODULE, + .name = "tpm2_key_ecdsa", + .name_len = sizeof("tpm2_key_ecdsa") - 1, + .describe = tpm2_key_ecdsa_describe, + .destroy = tpm2_key_ecdsa_destroy, + .query = tpm2_key_ecdsa_query, + .eds_op = tpm2_key_ecdsa_eds_op, + .verify_signature = tpm2_key_ecdsa_verify_signature, +}; +EXPORT_SYMBOL_GPL(tpm2_key_ecdsa_subtype); + +static int __tpm2_key_ecdsa_preparse(struct tpm2_key *key) +{ + const off_t o = key->priv_len + 2 + sizeof(*key->desc); + const struct tpm2_ecc_parms *p = + (const struct tpm2_ecc_parms *)&key->data[o]; + u16 x_size, y_size; + const u8 *x, *y; + + if (tpm2_key_type(key) != TPM_ALG_ECC) + return -EBADMSG; + + if (tpm2_key_policy_size(key) != 0) + return -EBADMSG; + + if (be16_to_cpu(p->symmetric) != TPM_ALG_NULL) + return -EBADMSG; + + if (be16_to_cpu(p->scheme) != TPM_ALG_NULL) + return -EBADMSG; + + if (be16_to_cpu(p->ecc) != TPM2_ECC_NIST_P256 && + be16_to_cpu(p->ecc) != TPM2_ECC_NIST_P384 && + be16_to_cpu(p->ecc) != TPM2_ECC_NIST_P521) + return -EBADMSG; + + if (be16_to_cpu(p->kdf) != TPM_ALG_NULL) + return -EBADMSG; + + x = tpm2_key_ecdsa_ecc_x(key); + x_size = get_unaligned_be16(&x[0]); + if (x_size > ECC_MAX_BYTES) + return -EBADMSG; + + y = tpm2_key_ecdsa_ecc_y(key); + y_size = get_unaligned_be16(&y[0]); + if (y_size > ECC_MAX_BYTES) + return -EBADMSG; + + if (x_size != y_size) + return -EBADMSG; + + return 0; +} + +/* + * Attempt to parse a data blob for a key as a TPM private key blob. + */ +static int tpm2_key_ecdsa_preparse(struct key_preparsed_payload *prep) +{ + struct tpm2_key *key; + int ret; + + key = tpm2_key_decode(prep->data, prep->datalen); + if (IS_ERR(key)) + return ret; + + if (key->oid != OID_TPMLoadableKey) { + kfree(key); + return -EBADMSG; + } + + ret = __tpm2_key_ecdsa_preparse(key); + if (ret < 0) { + kfree(key); + return ret; + } + + prep->payload.data[asym_subtype] = &tpm2_key_ecdsa_subtype; + prep->payload.data[asym_key_ids] = NULL; + prep->payload.data[asym_crypto] = key; + prep->payload.data[asym_auth] = NULL; + prep->quotalen = 100; + return 0; +} + +static struct asymmetric_key_parser tpm2_key_ecdsa_parser = { + .owner = THIS_MODULE, + .name = "tpm2_key_ecdsa_parser", + .parse = tpm2_key_ecdsa_preparse, +}; + +static int __init tpm2_key_ecdsa_init(void) +{ + return register_asymmetric_key_parser(&tpm2_key_ecdsa_parser); +} + +static void __exit tpm2_key_ecdsa_exit(void) +{ + unregister_asymmetric_key_parser(&tpm2_key_ecdsa_parser); +} + +module_init(tpm2_key_ecdsa_init); +module_exit(tpm2_key_ecdsa_exit); + +MODULE_DESCRIPTION("Asymmetric TPM2 ECDSA key"); +MODULE_LICENSE("GPL"); diff --git a/crypto/ecdsa.c b/crypto/ecdsa.c index 258fffbf623d..cf7d630c6593 100644 --- a/crypto/ecdsa.c +++ b/crypto/ecdsa.c @@ -236,7 +236,6 @@ static int ecdsa_set_pub_key(struct crypto_akcipher *tfm, const void *key, unsig if (d[0] != 4) return -EINVAL; - keylen--; digitlen = keylen >> 1; ndigits = DIV_ROUND_UP(digitlen, sizeof(u64)); diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c index cad0048bcc3c..980113482546 100644 --- a/drivers/char/tpm/tpm-buf.c +++ b/drivers/char/tpm/tpm-buf.c @@ -153,7 +153,7 @@ EXPORT_SYMBOL_GPL(tpm_buf_append_u32); * @count: the number of bytes to read * @output: the output buffer */ -static void tpm_buf_read(struct tpm_buf *buf, off_t *offset, size_t count, void *output) +void tpm_buf_read(struct tpm_buf *buf, off_t *offset, size_t count, void *output) { off_t next_offset; diff --git a/include/linux/tpm.h b/include/linux/tpm.h index d0860af7a56d..b89a8fe9049c 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -45,6 +45,7 @@ enum tpm_algorithms { TPM_ALG_ERROR = 0x0000, TPM_ALG_RSA = 0x0001, TPM_ALG_SHA1 = 0x0004, + TPM_ALG_HMAC = 0x0004, TPM_ALG_AES = 0x0006, TPM_ALG_KEYEDHASH = 0x0008, TPM_ALG_SHA256 = 0x000B, @@ -52,6 +53,7 @@ enum tpm_algorithms { TPM_ALG_SHA512 = 0x000D, TPM_ALG_NULL = 0x0010, TPM_ALG_SM3_256 = 0x0012, + TPM_ALG_ECDSA = 0x0018, TPM_ALG_ECC = 0x0023, TPM_ALG_CFB = 0x0043, }; @@ -65,6 +67,8 @@ enum tpm_algorithms { enum tpm2_curves { TPM2_ECC_NONE = 0x0000, TPM2_ECC_NIST_P256 = 0x0003, + TPM2_ECC_NIST_P384 = 0x0004, + TPM2_ECC_NIST_P521 = 0x0005, }; struct tpm_digest { @@ -239,6 +243,7 @@ enum tpm2_structures { TPM2_ST_NO_SESSIONS = 0x8001, TPM2_ST_SESSIONS = 0x8002, TPM2_ST_CREATION = 0x8021, + TPM2_ST_HASHCHECK = 0x8024, }; /* Indicates from what layer of the software stack the error comes from */ @@ -274,6 +279,7 @@ enum tpm2_command_codes { TPM2_CC_LOAD = 0x0157, TPM2_CC_RSA_DECRYPT = 0x0159, TPM2_CC_SEQUENCE_UPDATE = 0x015C, + TPM2_CC_SIGN = 0x015D, TPM2_CC_UNSEAL = 0x015E, TPM2_CC_CONTEXT_LOAD = 0x0161, TPM2_CC_CONTEXT_SAVE = 0x0162, @@ -423,6 +429,7 @@ void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value); u8 tpm_buf_read_u8(struct tpm_buf *buf, off_t *offset); u16 tpm_buf_read_u16(struct tpm_buf *buf, off_t *offset); u32 tpm_buf_read_u32(struct tpm_buf *buf, off_t *offset); +void tpm_buf_read(struct tpm_buf *buf, off_t *offset, size_t count, void *output); /* * Check if TPM device is in the firmware upgrade mode.