From patchwork Tue May 21 03:16:26 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 13668872 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 9226C1CA96; Tue, 21 May 2024 03:16:56 +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=1716261416; cv=none; b=W3KwtafyaxERliLikTrT4QX/oijaIlT28P5U/nR8xJgCbMgQNWq39QyusPwyurfUGt3CUhjeGHWyyEqd962sv2WxVR/cxHuWOdj7gzKjctbCpFKboUnDZIkBwXlrc3CFro8u6PqK6lst2DET7EigWa5raYqIKqH9YwkFmecKPlA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716261416; c=relaxed/simple; bh=rQTPLrmfpCPpQ7Zz53PlLkcw1m7mUahOVVqZUGpxmKU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=pUFw6HZJ8HBz4h9idqVZgVRNvz5SbsMhnCkd7INS5xmxKRUZ0qvPTthg2bcoD/WdK/wXUpgypldBuDVvqH8Wok4QMLQ7yPbO0PUYFQtqVrr3LUi+B4uSNsdSZpMpLh39GaxNeostgmJa7pgUNgtrYEDGM3RB5KI4Yw7CvEVpfXE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=h+0MQXA/; 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="h+0MQXA/" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B7301C2BD10; Tue, 21 May 2024 03:16:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1716261416; bh=rQTPLrmfpCPpQ7Zz53PlLkcw1m7mUahOVVqZUGpxmKU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=h+0MQXA/XTB6gpsQWr1qrNEZjYmyKXViiajnoJS5ynYpfF6Qm3+nweNQu7LSmnPDX hK8v4DATVsLFdPDNZgTTMcKcofHl1Sp7PZhPaHlWlnD48TOzYrEqje//ZUgHYsWCok 2gVB/GmAJMBdvCYWwu9Mzv+Y6JXodYXmv3WzYuIe/kWMXfRkRUHgiKJVIplPSisXbV LEmgZwBUUdvqM7Q0Lat5byfn06kh8+G0OtTvsSoh1yeUGWWlE37kvU3WOuicdgbbLa wjxVH0DVU+v1mGF3zzXvD7KiT0H0fD6LzCOoRt6KSmx2/heMV3dBgfqoBRTRI0E1zV o8q+O8bt3eD8A== 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 , Jarkko Sakkinen , "David S. Miller" , linux-crypto@vger.kernel.org (open list:CRYPTO API), linux-kernel@vger.kernel.org (open list) Subject: [PATCH v2 1/6] crypto: rsa-pkcs1pad: export rsa1_asn_lookup() Date: Tue, 21 May 2024 06:16:26 +0300 Message-ID: <20240521031645.17008-2-jarkko@kernel.org> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240521031645.17008-1-jarkko@kernel.org> References: <20240521031645.17008-1-jarkko@kernel.org> Precedence: bulk X-Mailing-List: keyrings@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 21 03:16:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 13668873 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 A369F219FD; Tue, 21 May 2024 03:17:02 +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=1716261422; cv=none; b=bmHqUSjMHDWLuMuIuECYAUyxEttDeCAiiJnyqTq4d66ADQ6OZ67YzC60CAPL43EfD8q/sGKa8QT1Hl8VPNXRa9jEihFSrz1H16gOwY3a3f/4QxoJ+0UuBlZY9qbamIfPQhEcz7WMBJiwH3a1biN7tBAZLmyaQdIs5WRzy1tzpWI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716261422; c=relaxed/simple; bh=xy+V7XL2mntIN1Y8Vq4nuU/5Bbmt5vykHQ/6P1bi5xo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=hcAjztWaOVDWrtWqciVWhhTmx5iJw+sEMFH2gFRkGlfdvNiAomEihJnQ/IsDBKhqS860DwrXbD+gJcS/juhPEQIcAja4vlqGijmWLJwXc1kyjtEIOmBgK7dtbzKu+3ctHEZdMQjyS8r+8XipjsKK6tQw95CY9IXKYV/eGMiNRBg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=pdqLKWAk; 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="pdqLKWAk" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C8A3BC2BD10; Tue, 21 May 2024 03:17:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1716261422; bh=xy+V7XL2mntIN1Y8Vq4nuU/5Bbmt5vykHQ/6P1bi5xo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pdqLKWAkqn4rCqLGe5WuL12DGRwSUXOoyzGknryitZVpWDUZdVhacnjUdwNTaBVew jUg/PiWNgtS0EFLAXtIVj8jnBLSJML94vMHpuMSb3c9T8sLaHGQys525YZTX4msxm1 c392dPotvPEtL4k3IgcHMKDTEFYaJA6r7fOd5lCsQ8vEaghtUDnLxfo0xEdlCCm5QH wbRHR5WXRAY98p3wju2PyTv4nY+f12nKbO0+MHgRFM1neRr5Op1S3GmmLkjWo7I/iG VTkSgn8RJZpWAOK+skEPGxZSC2GCRE/TU1ZAj2j4WqxIMQpfM/1tFAnjoSe0dfrCJH EK+kir9Llqy/Q== 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 , Jarkko Sakkinen , "David S. Miller" , linux-crypto@vger.kernel.org (open list:CRYPTO API), linux-kernel@vger.kernel.org (open list), Andrew Morton , 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 v2 2/6] lib: Expand asn1_encode_integer() to variable size integers Date: Tue, 21 May 2024 06:16:27 +0300 Message-ID: <20240521031645.17008-3-jarkko@kernel.org> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240521031645.17008-1-jarkko@kernel.org> References: <20240521031645.17008-1-jarkko@kernel.org> Precedence: bulk X-Mailing-List: keyrings@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Expand asn1_encode_integer() to variable size integers, meaning that it will get a blob in big-endian format as integer and length of the blob as parameters. This is required in order to encode RSA public key modulus. Signed-off-by: Jarkko Sakkinen --- include/linux/asn1_encoder.h | 3 +- lib/asn1_encoder.c | 185 ++++++++++++---------- security/keys/trusted-keys/trusted_tpm2.c | 4 +- 3 files changed, 103 insertions(+), 89 deletions(-) diff --git a/include/linux/asn1_encoder.h b/include/linux/asn1_encoder.h index 08cd0c2ad34f..ad5fb18db9e2 100644 --- a/include/linux/asn1_encoder.h +++ b/include/linux/asn1_encoder.h @@ -9,9 +9,10 @@ #include #define asn1_oid_len(oid) (sizeof(oid)/sizeof(u32)) + unsigned char * asn1_encode_integer(unsigned char *data, const unsigned char *end_data, - s64 integer); + const u8 *integer, int integer_len); unsigned char * asn1_encode_oid(unsigned char *data, const unsigned char *end_data, u32 oid[], int oid_len); diff --git a/lib/asn1_encoder.c b/lib/asn1_encoder.c index 0fd3c454a468..51a2d7010a67 100644 --- a/lib/asn1_encoder.c +++ b/lib/asn1_encoder.c @@ -9,12 +9,78 @@ #include #include #include +#include + +/** + * asn1_encode_length() - encode a length to follow an ASN.1 tag + * @data: pointer to encode at + * @data_len: pointer to remaining length (adjusted by routine) + * @len: length to encode + * + * This routine can encode lengths up to 65535 using the ASN.1 rules. + * It will accept a negative length and place a zero length tag + * instead (to keep the ASN.1 valid). This convention allows other + * encoder primitives to accept negative lengths as singalling the + * sequence will be re-encoded when the length is known. + */ +static int asn1_encode_length(unsigned char **data, int *data_len, int len) +{ + if (*data_len < 1) + return -EINVAL; + + if (len < 0) { + *((*data)++) = 0; + (*data_len)--; + return 0; + } + + if (len <= 0x7f) { + *((*data)++) = len; + (*data_len)--; + return 0; + } + + if (*data_len < 2) + return -EINVAL; + + if (len <= 0xff) { + *((*data)++) = 0x81; + *((*data)++) = len & 0xff; + *data_len -= 2; + return 0; + } + + if (*data_len < 3) + return -EINVAL; + + if (len <= 0xffff) { + *((*data)++) = 0x82; + *((*data)++) = (len >> 8) & 0xff; + *((*data)++) = len & 0xff; + *data_len -= 3; + return 0; + } + + if (WARN(len > 0xffffff, "ASN.1 length can't be > 0xffffff")) + return -EINVAL; + + if (*data_len < 4) + return -EINVAL; + *((*data)++) = 0x83; + *((*data)++) = (len >> 16) & 0xff; + *((*data)++) = (len >> 8) & 0xff; + *((*data)++) = len & 0xff; + *data_len -= 4; + + return 0; +} /** * asn1_encode_integer() - encode positive integer 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 - * @integer: integer to be encoded + * @data: pointer to the pointer to the data + * @end_data: end of data pointer, points one beyond last usable byte in @data + * @integer: integer to be encoded + * @integer_len: length in bytes of the integer blob * * This is a simplified encoder: it only currently does * positive integers, but it should be simple enough to add the @@ -22,15 +88,17 @@ */ unsigned char * asn1_encode_integer(unsigned char *data, const unsigned char *end_data, - s64 integer) + const u8 *integer, int integer_len) { int data_len = end_data - data; - unsigned char *d = &data[2]; bool found = false; + unsigned char *d; + int encoded_len; + u8 *encoded; + int ret; int i; - if (WARN(integer < 0, - "BUG: integer encode only supports positive integers")) + if (WARN(!integer, "BUG: integer is null")) return ERR_PTR(-EINVAL); if (IS_ERR(data)) @@ -40,17 +108,22 @@ asn1_encode_integer(unsigned char *data, const unsigned char *end_data, if (data_len < 3) return ERR_PTR(-EINVAL); - /* remaining length where at d (the start of the integer encoding) */ - data_len -= 2; + (*data++) = _tag(UNIV, PRIM, INT); + data_len--; - data[0] = _tag(UNIV, PRIM, INT); - if (integer == 0) { - *d++ = 0; - goto out; + if (!memchr_inv(integer, 0, integer_len)) { + data[1] = 1; + data[2] = 0; + return &data[2]; } - for (i = sizeof(integer); i > 0 ; i--) { - int byte = integer >> (8 * (i - 1)); + encoded = kzalloc(integer_len, GFP_KERNEL); + if (!encoded) + return ERR_PTR(-ENOMEM); + d = encoded; + + for (i = 0; i < integer_len; i++) { + int byte = integer[i]; if (!found && byte == 0) continue; @@ -67,21 +140,23 @@ asn1_encode_integer(unsigned char *data, const unsigned char *end_data, * have len >= 1 */ *d++ = 0; - data_len--; } found = true; - if (data_len == 0) - return ERR_PTR(-EINVAL); - *d++ = byte; - data_len--; } - out: - data[1] = d - data - 2; + encoded_len = d - encoded; - return d; + ret = asn1_encode_length(&data, &data_len, encoded_len); + if (ret) { + kfree(encoded); + return ERR_PTR(ret); + } + + memcpy(data, encoded, encoded_len); + kfree(encoded); + return data + encoded_len; } EXPORT_SYMBOL_GPL(asn1_encode_integer); @@ -176,70 +251,6 @@ asn1_encode_oid(unsigned char *data, const unsigned char *end_data, } EXPORT_SYMBOL_GPL(asn1_encode_oid); -/** - * asn1_encode_length() - encode a length to follow an ASN.1 tag - * @data: pointer to encode at - * @data_len: pointer to remaining length (adjusted by routine) - * @len: length to encode - * - * This routine can encode lengths up to 65535 using the ASN.1 rules. - * It will accept a negative length and place a zero length tag - * instead (to keep the ASN.1 valid). This convention allows other - * encoder primitives to accept negative lengths as singalling the - * sequence will be re-encoded when the length is known. - */ -static int asn1_encode_length(unsigned char **data, int *data_len, int len) -{ - if (*data_len < 1) - return -EINVAL; - - if (len < 0) { - *((*data)++) = 0; - (*data_len)--; - return 0; - } - - if (len <= 0x7f) { - *((*data)++) = len; - (*data_len)--; - return 0; - } - - if (*data_len < 2) - return -EINVAL; - - if (len <= 0xff) { - *((*data)++) = 0x81; - *((*data)++) = len & 0xff; - *data_len -= 2; - return 0; - } - - if (*data_len < 3) - return -EINVAL; - - if (len <= 0xffff) { - *((*data)++) = 0x82; - *((*data)++) = (len >> 8) & 0xff; - *((*data)++) = len & 0xff; - *data_len -= 3; - return 0; - } - - if (WARN(len > 0xffffff, "ASN.1 length can't be > 0xffffff")) - return -EINVAL; - - if (*data_len < 4) - return -EINVAL; - *((*data)++) = 0x83; - *((*data)++) = (len >> 16) & 0xff; - *((*data)++) = (len >> 8) & 0xff; - *((*data)++) = len & 0xff; - *data_len -= 4; - - return 0; -} - /** * asn1_encode_tag() - add a tag for optional or explicit value * @data: pointer to place tag at diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index 8b7dd73d94c1..ec59f9389a2d 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -38,6 +38,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload, u8 *end_work = scratch + SCRATCH_SIZE; u8 *priv, *pub; u16 priv_len, pub_len; + u32 key_handle; int ret; priv_len = get_unaligned_be16(src) + 2; @@ -77,7 +78,8 @@ static int tpm2_key_encode(struct trusted_key_payload *payload, goto err; } - work = asn1_encode_integer(work, end_work, options->keyhandle); + key_handle = cpu_to_be32(options->keyhandle); + work = asn1_encode_integer(work, end_work, (u8 *)&key_handle, 4); work = asn1_encode_octet_string(work, end_work, pub, pub_len); work = asn1_encode_octet_string(work, end_work, priv, priv_len); From patchwork Tue May 21 03:16:28 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 13668874 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 5B86E25634; Tue, 21 May 2024 03:17:09 +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=1716261429; cv=none; b=eOB/G1kr7OO3ZTcLE0zineeWHuMBbMGdNCSXFX15YvpH80niVYqzSuO+BGaAraseKjLhdJdQal60IcGDtJV/+xaupUjyK5q5DpMMuegb+bYohdClCgY5+ykCym+65pQG3b9cygJE/iil4ScES/Y1mHegeUoY0F6yH90+BuMrE+c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716261429; c=relaxed/simple; bh=jec1TbJ+akbpJqkAQc6Azv309XlSmxotrVc7+fbbuXQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=t16V5YeOS8cg/yWdNBRQmqxJp8qQdGXi3X8U8jlq+dvd0qX205WFThE/r/80OVkInrU9CdRQ8Dwhu/ZuReYojlMritPDWZcBxLAqFcTwaELOnV1DCGMVS5pfuKLlnrSgZw+1laWdIh4qkXkpUho2wATQnTIDu6XY4bpI5aRPXHw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=oilhY33F; 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="oilhY33F" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 519BDC2BD10; Tue, 21 May 2024 03:17:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1716261428; bh=jec1TbJ+akbpJqkAQc6Azv309XlSmxotrVc7+fbbuXQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=oilhY33FFXb+C+i5c31ceEevAOIRz1Cqa1PM2if0um6uCG+Z7Q6RO0I7lk7YH7lfL dJkaKM39368DN8fkkMzGZqFaSX2c84Mi71/kar5txrHYvoCYYKLEz/bYXvIBaZbQwU lh4/WY7NcXIffTudKvX5yW/DoesW4adc5rLQ6sfNHIp8DWCQtezuz9Op0rpZNLXDRQ hSDjYVYA0eBIYUlcxzdx75xQrs6lx6FP1UR3vZGSqR08UMBHKsg0w3E18dG547uhBY FyqeF99PhyLZrdMBm+aSfeAZa6I7m+v+WhxWPw664G0ytAfwvC3D3Fep1OmprfTnTO gsxskQWCkZd1g== 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 , Jarkko Sakkinen , "David S. Miller" , linux-crypto@vger.kernel.org (open list:CRYPTO API), linux-kernel@vger.kernel.org (open list), Peter Huewe , Jason Gunthorpe , James Bottomley , Stefan Berger , Ard Biesheuvel , Mario Limonciello Subject: [PATCH v2 3/6] tpm: Export tpm2_load_context() Date: Tue, 21 May 2024 06:16:28 +0300 Message-ID: <20240521031645.17008-4-jarkko@kernel.org> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240521031645.17008-1-jarkko@kernel.org> References: <20240521031645.17008-1-jarkko@kernel.org> Precedence: bulk X-Mailing-List: keyrings@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Export tpm2_load_context() so that the null key can be loaded as the parent of a asymmetric TPM2 key. Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm.h | 2 - drivers/char/tpm/tpm2-cmd.c | 77 +++++++++++++++++++++++++++++++++++ drivers/char/tpm/tpm2-space.c | 61 --------------------------- include/linux/tpm.h | 2 + 4 files changed, 79 insertions(+), 63 deletions(-) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 6b8b9956ba69..c9c67fe84f33 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -314,8 +314,6 @@ int tpm_devs_add(struct tpm_chip *chip); void tpm_devs_remove(struct tpm_chip *chip); int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf, unsigned int buf_size, unsigned int *offset); -int tpm2_load_context(struct tpm_chip *chip, u8 *buf, - unsigned int *offset, u32 *handle); void tpm_bios_log_setup(struct tpm_chip *chip); void tpm_bios_log_teardown(struct tpm_chip *chip); diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 0cdf892ec2a7..eb07a109e2ba 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -370,6 +370,83 @@ void tpm2_flush_context(struct tpm_chip *chip, u32 handle) } EXPORT_SYMBOL_GPL(tpm2_flush_context); +struct tpm2_context { + __be64 sequence; + __be32 saved_handle; + __be32 hierarchy; + __be16 blob_size; +} __packed; + +/** + * tpm2_load_context() - Load TPM2 object to the TPM memory + * @chip: TPM chip to use + * @buf: Blob containing TPM2 object. + * @offset: Output variable for the offset in @buf reached. + * @handle: Output variable for the handle of the object in TPM memory. + * + * Load a blob encrypted with TPM from the memory to the TPM chip. + * + * Return: + * - 0 when the blob is successfully loaded to the TPM. + * - -EFAULT if the TPM chip itself fails. + * - -ENOENT if the TPM object is replayed. + * - -EINVAL if the TPM object is corrupted. + */ +int tpm2_load_context(struct tpm_chip *chip, const u8 *buf, + unsigned int *offset, u32 *handle) +{ + struct tpm_buf tbuf; + struct tpm2_context *ctx; + unsigned int body_size; + int rc; + + rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD); + if (rc) + return rc; + + ctx = (struct tpm2_context *)&buf[*offset]; + body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size); + tpm_buf_append(&tbuf, &buf[*offset], body_size); + + rc = tpm_transmit_cmd(chip, &tbuf, 4, NULL); + if (rc < 0) { + dev_warn(&chip->dev, "%s: failed with a system error %d\n", + __func__, rc); + tpm_buf_destroy(&tbuf); + return -EFAULT; + } else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE || + rc == TPM2_RC_REFERENCE_H0) { + /* + * TPM_RC_HANDLE means that the session context can't + * be loaded because of an internal counter mismatch + * that makes the TPM think there might have been a + * replay. This might happen if the context was saved + * and loaded outside the space. + * + * TPM_RC_REFERENCE_H0 means the session has been + * flushed outside the space + */ + *handle = 0; + tpm_buf_destroy(&tbuf); + return -ENOENT; + } else if (tpm2_rc_value(rc) == TPM2_RC_INTEGRITY) { + tpm_buf_destroy(&tbuf); + return -EINVAL; + } else if (rc > 0) { + dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n", + __func__, rc); + tpm_buf_destroy(&tbuf); + return -EFAULT; + } + + *handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]); + *offset += body_size; + + tpm_buf_destroy(&tbuf); + return 0; +} +EXPORT_SYMBOL_GPL(tpm2_load_context); + struct tpm2_get_cap_out { u8 more_data; __be32 subcap_id; diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index 4892d491da8d..708c6e4d64cd 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -21,13 +21,6 @@ enum tpm2_handle_types { TPM2_HT_TRANSIENT = 0x80000000, }; -struct tpm2_context { - __be64 sequence; - __be32 saved_handle; - __be32 hierarchy; - __be16 blob_size; -} __packed; - static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space) { int i; @@ -68,60 +61,6 @@ void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space) kfree(space->session_buf); } -int tpm2_load_context(struct tpm_chip *chip, u8 *buf, - unsigned int *offset, u32 *handle) -{ - struct tpm_buf tbuf; - struct tpm2_context *ctx; - unsigned int body_size; - int rc; - - rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD); - if (rc) - return rc; - - ctx = (struct tpm2_context *)&buf[*offset]; - body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size); - tpm_buf_append(&tbuf, &buf[*offset], body_size); - - rc = tpm_transmit_cmd(chip, &tbuf, 4, NULL); - if (rc < 0) { - dev_warn(&chip->dev, "%s: failed with a system error %d\n", - __func__, rc); - tpm_buf_destroy(&tbuf); - return -EFAULT; - } else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE || - rc == TPM2_RC_REFERENCE_H0) { - /* - * TPM_RC_HANDLE means that the session context can't - * be loaded because of an internal counter mismatch - * that makes the TPM think there might have been a - * replay. This might happen if the context was saved - * and loaded outside the space. - * - * TPM_RC_REFERENCE_H0 means the session has been - * flushed outside the space - */ - *handle = 0; - tpm_buf_destroy(&tbuf); - return -ENOENT; - } else if (tpm2_rc_value(rc) == TPM2_RC_INTEGRITY) { - tpm_buf_destroy(&tbuf); - return -EINVAL; - } else if (rc > 0) { - dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n", - __func__, rc); - tpm_buf_destroy(&tbuf); - return -EFAULT; - } - - *handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]); - *offset += body_size; - - tpm_buf_destroy(&tbuf); - return 0; -} - int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf, unsigned int buf_size, unsigned int *offset) { diff --git a/include/linux/tpm.h b/include/linux/tpm.h index c17e4efbb2e5..2f25ca07127b 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -466,6 +466,8 @@ extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max); extern struct tpm_chip *tpm_default_chip(void); void tpm2_flush_context(struct tpm_chip *chip, u32 handle); +int tpm2_load_context(struct tpm_chip *chip, const u8 *buf, + unsigned int *offset, u32 *handle); static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle) { From patchwork Tue May 21 03:16:29 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 13668875 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 20C3D2556E; Tue, 21 May 2024 03:17:15 +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=1716261435; cv=none; b=DNylbWQGKZ39S6F8P53TM9lLwUkUFfHLWAKGjaJox+77xDElfjmTYaCQCIPHgPwpAItOuPtTK7k99qlD4elui8cLtXRG8nCagCqFaZv5ewtQzlWLDUiuQBRArVF09OEVeYNBsgilpkRaKib48sQ+ced1TSliQUW2+pAOT1ZPD3w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716261435; c=relaxed/simple; bh=n3cIMYf7xZGCzgAkDDAlFx85xEycCwosKadBNAn25Vw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=upIG38E15cNJsKqcXF41qu/Ikb+2iz6yQQvt6zIxXQkvQSdxxHoT2f02h/vCbg4FKfdWk540c+NwSeKhBwvtvUXJoPn4YdMa8yE08RDv26EF67b74mvgIfkieGLgNhHCHWIm/9rnAKHLznBqj4szjokvoyhl8GNSyDEMWhjfjo4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=QwIIXzE1; 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="QwIIXzE1" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 95E77C2BD10; Tue, 21 May 2024 03:17:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1716261435; bh=n3cIMYf7xZGCzgAkDDAlFx85xEycCwosKadBNAn25Vw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QwIIXzE1xw5aYEGI78qrhpjosf1rBczq8Z/7pUaMROmisAKG9/MJj421TtlZ5Bn/P zfxKyz7zZZNsl8zn9gOkPxbD06DDf03tCGO+BY+CkkZdIaOzXvzFhD61cJ2iKjtpuW yVubyvSOFRwgkFz9VkOFkzKY751RblgTQBrmf8m6W9AGFl32fDL7Ql64T/fG0u/ZwW YCrHrU7Vgy2NkhOLOzBcqKrMVklc6J4p9jbWcaOWp0JEhKfMOi4/e8yJHQR4/qUXym VrsQdAHkgONXPc+Ad6eSdDr7dE+iAhgvjwfsPhAfKDKw1dEKTZsnF4hxDpNOqiE9oy e/Ho10PyQdazw== 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 , Jarkko Sakkinen , "David S. Miller" , linux-crypto@vger.kernel.org (open list:CRYPTO API), linux-kernel@vger.kernel.org (open list), Peter Huewe , Jason Gunthorpe , 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 v2 4/6] KEYS: trusted: Move tpm2_key_decode() to the TPM driver Date: Tue, 21 May 2024 06:16:29 +0300 Message-ID: <20240521031645.17008-5-jarkko@kernel.org> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240521031645.17008-1-jarkko@kernel.org> References: <20240521031645.17008-1-jarkko@kernel.org> Precedence: bulk X-Mailing-List: keyrings@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Move tpm2_key_decode() to the TPM driver and export the symbols to make them callable from trusted keys. It can re-used for asymmetric keys. Signed-off-by: Jarkko Sakkinen --- v2: Do not allocate blob twice. Use the one inside struct tpm2_key. --- drivers/char/tpm/Kconfig | 1 + drivers/char/tpm/Makefile | 5 + drivers/char/tpm/tpm2_key.c | 111 +++++++++++++++ .../char/tpm}/tpm2key.asn1 | 0 include/crypto/tpm2_key.h | 33 +++++ security/keys/trusted-keys/Makefile | 2 - security/keys/trusted-keys/trusted_tpm2.c | 127 +++--------------- 7 files changed, 167 insertions(+), 112 deletions(-) create mode 100644 drivers/char/tpm/tpm2_key.c rename {security/keys/trusted-keys => drivers/char/tpm}/tpm2key.asn1 (100%) create mode 100644 include/crypto/tpm2_key.h diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index e63a6a17793c..de2f4093c939 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -7,6 +7,7 @@ menuconfig TCG_TPM tristate "TPM Hardware Support" depends on HAS_IOMEM imply SECURITYFS + select ASN1 select CRYPTO select CRYPTO_HASH_INFO help diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 4c695b0388f3..071437058ef6 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -17,6 +17,11 @@ tpm-y += eventlog/tpm1.o tpm-y += eventlog/tpm2.o tpm-y += tpm-buf.o +# TPM2 Asymmetric Key +$(obj)/trusted_tpm2.o: $(obj)/tpm2key.asn1.h +tpm-y += tpm2key.asn1.o +tpm-y += tpm2_key.o + tpm-$(CONFIG_TCG_TPM2_HMAC) += tpm2-sessions.o tpm-$(CONFIG_ACPI) += tpm_ppi.o eventlog/acpi.o tpm-$(CONFIG_EFI) += eventlog/efi.o diff --git a/drivers/char/tpm/tpm2_key.c b/drivers/char/tpm/tpm2_key.c new file mode 100644 index 000000000000..0112362e432e --- /dev/null +++ b/drivers/char/tpm/tpm2_key.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include "tpm2key.asn1.h" + +#undef pr_fmt +#define pr_fmt(fmt) "tpm2_key: "fmt + +int tpm2_key_parent(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct tpm2_key *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 *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 *ctx = context; + + ctx->priv = value; + ctx->priv_len = vlen; + + return 0; +} + +/** + * tpm_key_decode() - Decode TPM2 ASN.1 key. + * @src: ASN.1 source. + * @src_len: ASN.1 source length. + * @key: TPM2 asymmetric key. + * @max_key_len: Maximum length of the TPM2 asymmetric key. + * + * Decodes TPM2 ASN.1 key on success. Returns POSIX error code on failure. + */ +int tpm2_key_decode(const u8 *src, u32 src_len, struct tpm2_key *key, + u32 max_key_len) +{ + struct tpm2_key ctx; + u32 blob_len; + int ret; + + memset(&ctx, 0, sizeof(ctx)); + + ret = asn1_ber_decoder(&tpm2key_decoder, &ctx, src, src_len); + if (ret < 0) + return ret; + + blob_len = ctx.priv_len + ctx.pub_len; + if (blob_len > max_key_len) + return -E2BIG; + + ctx.blob_len = blob_len; + ctx.blob = kmalloc(blob_len, GFP_KERNEL); + if (!ctx.blob) + return -ENOMEM; + + memcpy((void *)ctx.blob, ctx.priv, ctx.priv_len); + memcpy((void *)ctx.blob + ctx.priv_len, ctx.pub, ctx.pub_len); + ctx.priv = ctx.blob; + ctx.pub = ctx.blob + ctx.priv_len; + + memcpy(key, &ctx, sizeof(ctx)); + return 0; +} +EXPORT_SYMBOL_GPL(tpm2_key_decode); diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/drivers/char/tpm/tpm2key.asn1 similarity index 100% rename from security/keys/trusted-keys/tpm2key.asn1 rename to drivers/char/tpm/tpm2key.asn1 diff --git a/include/crypto/tpm2_key.h b/include/crypto/tpm2_key.h new file mode 100644 index 000000000000..acf41b2e0c92 --- /dev/null +++ b/include/crypto/tpm2_key.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __LINUX_TPM2_KEY_H__ +#define __LINUX_TPM2_KEY_H__ + +#include + +/* + * TPM2 ASN.1 key + */ +struct tpm2_key { + u32 parent; + const u8 *blob; + u32 blob_len; + const u8 *pub; + u32 pub_len; + const u8 *priv; + u32 priv_len; +}; + +int tpm2_key_decode(const u8 *src, u32 src_len, struct tpm2_key *key, + u32 max_key_len); + +/** + * tpm2_key_free() - Release TPM2 asymmetric key resources and reset values + * @key: TPM2 asymmetric key. + */ +static inline void tpm2_key_destroy(struct tpm2_key *key) +{ + kfree(key->blob); + memset(key, 0, sizeof(*key)); +} + +#endif /* __LINUX_TPM2_KEY_H__ */ 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/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index ec59f9389a2d..f255388d32b8 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}, @@ -28,9 +27,9 @@ static struct tpm2_hash tpm2_hash_map[] = { static u32 tpm2key_oid[] = { 2, 23, 133, 10, 1, 5 }; -static int tpm2_key_encode(struct trusted_key_payload *payload, - struct trusted_key_options *options, - u8 *src, u32 len) +static int tpm2_trusted_key_encode(struct trusted_key_payload *payload, + struct trusted_key_options *options, + u8 *src, u32 len) { const int SCRATCH_SIZE = PAGE_SIZE; u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL); @@ -100,106 +99,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 -EINVAL; - - 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. * @@ -349,7 +248,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip, goto out; } - blob_len = tpm2_key_encode(payload, options, &buf.data[offset], blob_len); + blob_len = tpm2_trusted_key_encode(payload, options, &buf.data[offset], + blob_len); out: tpm_buf_destroy(&sized); @@ -389,21 +289,27 @@ 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; + struct tpm2_key key; + struct tpm_buf buf; + const u8 *blob, *pub; int rc; u32 attrs; - rc = tpm2_key_decode(payload, options, &blob); + rc = tpm2_key_decode(payload->blob, payload->blob_len, &key, PAGE_SIZE); if (rc) { /* old form */ blob = payload->blob; payload->old_format = 1; + } else { + blob = key.blob; } + if (!blob) + return -ENOMEM; + /* new format carries keyhandle but old format doesn't */ if (!options->keyhandle) return -EINVAL; @@ -467,7 +373,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip, out: if (blob != payload->blob) - kfree(blob); + tpm2_key_destroy(&key); + tpm_buf_destroy(&buf); if (rc > 0) From patchwork Tue May 21 03:16:30 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 13668876 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 C09093BB32; Tue, 21 May 2024 03:17:19 +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=1716261439; cv=none; b=lyDGkfJpJJmHeLiVzYurx4DTT5kOx9Xc5zji2NVUh0rvG3Kaa33EO6rP1SvYIcQTksFv0s81hTLNkkeSTG+TpvwPTv5hXnzmWfcRKmwv+CkO/3c1Js6pXB9ZAMn8AnReGZWXNGmLZkwNApan6FvFxW3c6Tyamjg0eaE63eGOd28= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716261439; c=relaxed/simple; bh=UAboRLGS7mPC957H6sOCjxTXDHuNsnoUR6E4ZLlnQfU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RSaxncjtJ8W9xFc8MYYnP3hix6ngPqVENTuc4FHdalqXqXzdU93vvz25ZS3NxyPK3BRRv5RCs9HxQ0UEtIyzvZYFjyuIjaLz7viY5gEqod5iUVw5eUMOXNq0jRpbiUL2lJh8PtjWEYJYRwCOuNrMb2n216FJK/eIskG5APaGspM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=BE+6tN+m; 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="BE+6tN+m" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D663EC2BD10; Tue, 21 May 2024 03:17:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1716261439; bh=UAboRLGS7mPC957H6sOCjxTXDHuNsnoUR6E4ZLlnQfU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=BE+6tN+m6fx9OgZToaDacpbDWg+pJj59hvxFVCDQEd+zb5xAE344zx6ST7EMNHWsB so/X2hKPVn3FuA0kkSjC0jZjpND0sNc6ne2LAtIOgkAKzfycTyiLSmglDudknOizHY If14J9oWy00jpVeGnKzaOAqQFyn1yHlueM0RwJJSsCTyKys0ZbunfqptVIMvdTnoY3 Kgv16jgwi/KoB1jlWHOuIsUKU9ugR4YeH8b7+VCWOFQczT3oQDDf817ATCeSu68Rfc /7hQqB9zS9bT8SMWo1Jv2jjVc/sCPGkvZjFXaOsJoE+esLGSQAJgxzZepkX8QqMpUs uPSu9a0mFz5jQ== 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 , Jarkko Sakkinen , "David S. Miller" , linux-crypto@vger.kernel.org (open list:CRYPTO API), linux-kernel@vger.kernel.org (open list), Peter Huewe , Jason Gunthorpe , 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 v2 5/6] tpm: tpm2_key: Extend parser to TPM_LoadableKey Date: Tue, 21 May 2024 06:16:30 +0300 Message-ID: <20240521031645.17008-6-jarkko@kernel.org> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240521031645.17008-1-jarkko@kernel.org> References: <20240521031645.17008-1-jarkko@kernel.org> Precedence: bulk X-Mailing-List: keyrings@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Extend parser to TPM_LoadableKey. Add field for oid to struct tpm2_key so that callers can differentiate different key types. Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm2_key.c | 14 +++++++++++--- include/crypto/tpm2_key.h | 2 ++ security/keys/trusted-keys/trusted_tpm2.c | 4 ++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/char/tpm/tpm2_key.c b/drivers/char/tpm/tpm2_key.c index 0112362e432e..59797dc232f1 100644 --- a/drivers/char/tpm/tpm2_key.c +++ b/drivers/char/tpm/tpm2_key.c @@ -32,16 +32,24 @@ int tpm2_key_type(void *context, size_t hdrlen, const void *value, size_t vlen) { enum OID oid = look_up_OID(value, vlen); - - if (oid != OID_TPMSealedData) { + struct tpm2_key *key = context; + + switch (oid) { + case OID_TPMSealedData: + pr_info("TPMSealedData\n"); + break; + case OID_TPMLoadableKey: + pr_info("TPMLodableKey\n"); + break; + default: char buffer[50]; - sprint_oid(value, vlen, buffer, sizeof(buffer)); pr_debug("OID is \"%s\" which is not TPMSealedData\n", buffer); return -EINVAL; } + key->oid = oid; return 0; } diff --git a/include/crypto/tpm2_key.h b/include/crypto/tpm2_key.h index acf41b2e0c92..2d2434233000 100644 --- a/include/crypto/tpm2_key.h +++ b/include/crypto/tpm2_key.h @@ -2,12 +2,14 @@ #ifndef __LINUX_TPM2_KEY_H__ #define __LINUX_TPM2_KEY_H__ +#include #include /* * TPM2 ASN.1 key */ struct tpm2_key { + enum OID oid; u32 parent; const u8 *blob; u32 blob_len; diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index f255388d32b8..ce4c667c3ee3 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -305,6 +305,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip, payload->old_format = 1; } else { blob = key.blob; + if (key.oid != OID_TPMSealedData) { + tpm2_key_destroy(&key); + return -EINVAL; + } } if (!blob) From patchwork Tue May 21 03:16:31 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 13668877 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 AA40F4778C; Tue, 21 May 2024 03:17:27 +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=1716261447; cv=none; b=VOC1FVicFYzE3DgV1d93Kc9G2ee89SZF8fNIoDXvpDEuxFaPbzR/gd1e7pJxVphM0Vbs0Ie8RvLjmOW5HYUUJGLtk3eMevo8/mPa7zkQJZQuF9lzZoTFUXlYgGTXH0X2B59JRUMMjEdhSob3PsYXKuxdtHME3EfoCe4Vo1aTPS0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716261447; c=relaxed/simple; bh=hL4NU79dwSPxBcJfy9fvF5qBi4urlXwoW9kEtncl2Gg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eq+OY1aXWTvcqxxBjL7pxoWL32WFkPQfyGL7k/9x9oFBpqo6zv6DvoKbShAgeiCanijT46a90ww6l4DSm6mXycfTcHJxHOKCZ8o3CTOwqHvdS2jOPg0dsC5venRCDNTlBUzBte4tCz+zk74BBWyvGLAcCpL9bshBl+lPyXDHGt4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=hvcwd8Qy; 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="hvcwd8Qy" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D3EC4C4AF0F; Tue, 21 May 2024 03:17:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1716261447; bh=hL4NU79dwSPxBcJfy9fvF5qBi4urlXwoW9kEtncl2Gg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hvcwd8Qy7jfSxXMjyodOkt0Ku/d1zvvmj6pw5LnSbTsOdQ3sdd+nz5zd/I4nSfEB7 NbQMFP2H91rJWDkYsrtGX48OOVz0Xqc9kCieanYvMxGPsiqfcLP4RPwVZcN8YxOGam VsJkCaVJqRFjfZnFLcOg+Vp6VeKP6HbVyomOwNzi4VnweUwFtqrgkwzyl33K2khrXl op/tGE2UdW4m0qchIsUfPaGQVzYZrWQAlf3vi9Ux5F2pk5RnMdijq9YygCAzjqMwum iXeKhaSdKsGQ58IqtDn0WvraTLcdzZH9trqUX4tZ+/YL+xwP5P6pg9QwJko81Jf4Jt rsufWuWoDaH6A== 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 , Jarkko Sakkinen , "David S. Miller" , linux-crypto@vger.kernel.org (open list:CRYPTO API), linux-kernel@vger.kernel.org (open list), David Howells , James Bottomley , Stefan Berger , Ard Biesheuvel , Mario Limonciello Subject: [PATCH v2 6/6] keys: asymmetric: ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE Date: Tue, 21 May 2024 06:16:31 +0300 Message-ID: <20240521031645.17008-7-jarkko@kernel.org> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240521031645.17008-1-jarkko@kernel.org> References: <20240521031645.17008-1-jarkko@kernel.org> Precedence: bulk X-Mailing-List: keyrings@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: James Prestwood Based on earlier work by James Prestwood. Add ASN.1 compatible asymmetric TPM2 RSA key subtype: 1. Signing and decryption (with the private key) is handled by TPM2_RSA_Decrypt. 2. Encryption (with the public key) is handled by the kernel RSA implementation. Link: https://lore.kernel.org/all/20200518172704.29608-1-prestwoj@gmail.com/ Signed-off-by: James Prestwood Co-developed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- 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 | 16 + crypto/asymmetric_keys/Makefile | 1 + crypto/asymmetric_keys/tpm2_key_rsa.c | 698 ++++++++++++++++++++++++++ include/linux/tpm.h | 2 + 4 files changed, 717 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..4d14bb0c346e 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,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_RSA_SUBTYPE + tristate "Asymmetric TPM2 RSA crypto algorithm subtype" + depends on TCG_TPM + select TCG_TPM2_HMAC + select CRYPTO_RSA + select CRYPTO_SHA256 + select CRYPTO_HASH_INFO + 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 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..32250ff38268 --- /dev/null +++ b/crypto/asymmetric_keys/tpm2_key_rsa.c @@ -0,0 +1,698 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* TPM2 asymmetric public-key crypto subtype + * + * See Documentation/crypto/asymmetric-keys.rst + * + * Copyright (c) 2020 Intel Corporation + */ + +#include +#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 PUB_KEY_BUF_SIZE 512 + +struct tpm2_key_rsa { + struct tpm2_key key; + const u8 *pub; + int pub_len; +}; + +static const int PKCS1_PAD_MIN_SIZE = 11; + +/* + * 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_extract_pub(struct tpm2_key_rsa *key_rsa) +{ + struct tpm2_key *key = &key_rsa->key; + struct tpm_buf buf; + off_t offset = 2; + u16 policy_len; + u32 attr; + u16 bits; + u16 type; + u16 len; + u16 alg; + u32 exp; + + buf.flags = TPM_BUF_TPM2B; + buf.length = key->pub_len; + buf.data = (void *)key->pub; + + if (get_unaligned_be16(key->pub) != buf.length - 2) + return -EINVAL; + + type = tpm_buf_read_u16(&buf, &offset); + pr_debug("pub type: 0x%04x\n", type); + if (type != TPM_ALG_RSA) + return -EINVAL; + + alg = tpm_buf_read_u16(&buf, &offset); + pr_debug("pub name alg: 0x%04x\n", alg); + attr = tpm_buf_read_u32(&buf, &offset); + pr_debug("pub attributes: 0x%08x\n", attr); + policy_len = tpm_buf_read_u16(&buf, &offset); + pr_debug("pub policy length: %u bytes\n", policy_len); + offset += policy_len; + + alg = tpm_buf_read_u16(&buf, &offset); + pr_debug("pub symmetric: 0x%04x\n", alg); + if (alg != TPM_ALG_NULL) + return -EINVAL; + + alg = tpm_buf_read_u16(&buf, &offset); + pr_debug("pub symmetric scheme: 0x%04x\n", alg); + if (alg != TPM_ALG_NULL) + return -EINVAL; + + bits = tpm_buf_read_u16(&buf, &offset); + pr_debug("pub bits: %u\n", bits); + + exp = tpm_buf_read_u32(&buf, &offset); + pr_debug("pub exponent: 0x%08x\n", exp); + if (exp != 0x00000000 && exp != 0x00010001) + return -EINVAL; + + len = tpm_buf_read_u16(&buf, &offset); + pr_debug("pub modulus: %u bytes\n", len); + key_rsa->pub = key->pub + offset; + key_rsa->pub_len = len; + + return buf.flags & TPM_BUF_BOUNDARY_ERROR ? -EIO : 0; +} + +static int tpm2_key_rsa_encode(const struct tpm2_key_rsa *key, u8 *buf) +{ + const int SCRATCH_SIZE = PAGE_SIZE; + const u8 exp[3] = {1, 0, 1}; + u8 *scratch, *work, *work1, *end_work; + int pub_len = key->pub_len; + const u8 *pub = key->pub; + int ret; + + scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL); + if (!scratch) + return -ENOMEM; + + work = scratch; + end_work = &scratch[SCRATCH_SIZE]; + + work = asn1_encode_integer(work, end_work, pub, pub_len); + if (IS_ERR(work)) { + ret = PTR_ERR(work); + goto err; + } + + work = asn1_encode_integer(work, end_work, exp, 3); + if (IS_ERR(work)) { + ret = PTR_ERR(work); + goto err; + } + + work1 = buf; + work1 = asn1_encode_sequence(work1, &work1[PUB_KEY_BUF_SIZE], + scratch, work - scratch); + if (IS_ERR(work1)) { + ret = PTR_ERR(work1); + goto err; + } + + memset(work1, 0, 8); + + kfree(scratch); + return work1 - buf; + +err: + kfree(scratch); + return ret; +} + +/* + * Encryption operation is performed with the public key. Hence it is done + * in software + */ +static int tpm2_key_rsa_encrypt(struct tpm2_key_rsa *key, + struct kernel_pkey_params *params, + const void *in, void *out) +{ + char cipher[CRYPTO_MAX_ALG_NAME]; + struct scatterlist in_sg, out_sg; + u8 enc_pub_key[PUB_KEY_BUF_SIZE]; + struct akcipher_request *req; + struct crypto_akcipher *tfm; + struct crypto_wait cwait; + int rc; + + rc = tpm2_key_get_akcipher(params->encoding, params->hash_algo, cipher); + if (rc < 0) + return rc; + + tfm = crypto_alloc_akcipher(cipher, 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + rc = tpm2_key_rsa_encode(key, enc_pub_key); + if (rc < 0) + goto err_tfm; + + rc = crypto_akcipher_set_pub_key(tfm, enc_pub_key, rc); + if (rc < 0) + goto err_tfm; + + req = akcipher_request_alloc(tfm, GFP_KERNEL); + if (!req) { + rc = -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); + + rc = crypto_akcipher_encrypt(req); + rc = crypto_wait_req(rc, &cwait); + + if (!rc) + rc = req->dst_len; + + akcipher_request_free(req); + +err_tfm: + crypto_free_akcipher(tfm); + + return rc; +} + +static int __tpm2_key_rsa_decrypt(struct tpm_chip *chip, + struct tpm2_key_rsa *key, + struct kernel_pkey_params *params, + const void *in, int in_len, void *out) +{ + unsigned int offset = 0; + u32 key_handle = 0; + struct tpm_buf buf; + u16 decrypted_len; + u32 parent; + 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; + + if (key->key.parent == TPM2_RH_NULL) { + ret = tpm2_load_context(chip, chip->null_key_context, &offset, + &parent); + if (ret) { + ret = -EIO; + goto err_auth; + } + } else { + parent = key->key.parent; + } + + ret = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD); + if (ret < 0) + goto err_parent; + + tpm_buf_append_name(chip, &buf, parent, NULL); + tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_CONTINUE_SESSION | + TPM2_SA_ENCRYPT, NULL, 0); + tpm_buf_append(&buf, key->key.blob, key->key.blob_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, "RSA key loading"); + 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, "RSA key decrypting"); + 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_parent: + if (key->key.parent == TPM2_RH_NULL) + tpm2_flush_context(chip, parent); + +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_rsa *key, + struct kernel_pkey_params *params, + const void *in, void *out) +{ + const u8 *ptr; + int out_len; + u8 *work; + int ret; + + work = kzalloc(params->out_len, 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_rsa *key, + struct kernel_pkey_params *params, + const void *in, void *out) +{ + const struct rsa_asn1_template *asn1; + u32 in_len = params->in_len; + void *asn1_wrapped = NULL; + int pub_len = key->pub_len; + 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(pub_len, GFP_KERNEL); + tpm2_pad_pkcs1(in, in_len, padded, pub_len); + ret = __tpm2_key_rsa_decrypt(chip, key, params, padded, pub_len, 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_rsa *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; + + tpm2_key_destroy(key); + kfree(key); +} + +static int tpm2_key_rsa_eds_op(struct kernel_pkey_params *params, + const void *in, void *out) +{ + struct tpm2_key_rsa *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_rsa *tpm2_key = key->payload.data[asym_crypto]; + char alg_name[CRYPTO_MAX_ALG_NAME]; + u8 enc_pub_key[PUB_KEY_BUF_SIZE]; + struct akcipher_request *req; + struct scatterlist src_sg[2]; + struct crypto_akcipher *tfm; + struct crypto_wait cwait; + int rc; + + if (!sig->digest) + return -ENOPKG; + + rc = tpm2_key_get_akcipher(sig->encoding, sig->hash_algo, alg_name); + if (rc < 0) + return rc; + + tfm = crypto_alloc_akcipher(alg_name, 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + rc = tpm2_key_rsa_encode(tpm2_key, enc_pub_key); + if (rc < 0) + goto err_tfm; + + rc = crypto_akcipher_set_pub_key(tfm, enc_pub_key, rc); + if (rc < 0) + goto err_tfm; + + rc = -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); + rc = crypto_wait_req(crypto_akcipher_verify(req), &cwait); + + akcipher_request_free(req); + +err_tfm: + crypto_free_akcipher(tfm); + return rc; +} + +static int tpm2_key_rsa_query(const struct kernel_pkey_params *params, + struct kernel_pkey_query *info) +{ + struct tpm2_key_rsa *tk = params->key->payload.data[asym_crypto]; + char alg_name[CRYPTO_MAX_ALG_NAME]; + u8 enc_pub_key[PUB_KEY_BUF_SIZE]; + struct crypto_akcipher *tfm; + unsigned int len; + int ret; + + ret = tpm2_key_get_akcipher(params->encoding, params->hash_algo, alg_name); + if (ret < 0) + return ret; + + tfm = crypto_alloc_akcipher(alg_name, 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + ret = tpm2_key_rsa_encode(tk, enc_pub_key); + if (ret < 0) + goto err_tfm; + + ret = crypto_akcipher_set_pub_key(tfm, enc_pub_key, ret); + if (ret < 0) + goto err_tfm; + + len = crypto_akcipher_maxsize(tfm); + + info->key_size = tk->pub_len * 8; + info->max_data_size = tk->pub_len; + info->max_sig_size = len; + info->max_enc_size = len; + info->max_dec_size = tk->pub_len; + + info->supported_ops = KEYCTL_SUPPORTS_ENCRYPT | + KEYCTL_SUPPORTS_DECRYPT | + KEYCTL_SUPPORTS_VERIFY | + KEYCTL_SUPPORTS_SIGN; + +err_tfm: + crypto_free_akcipher(tfm); + return ret; +} + +/* + * Asymmetric TPM2 RSA key. Signs and decrypts with TPM. + */ +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); + +/* + * Attempt to parse a data blob for a key as a TPM private key blob. + */ +static int tpm2_key_preparse(struct key_preparsed_payload *prep) +{ + struct tpm2_key_rsa *key; + int ret; + + key = kzalloc(sizeof(*key), GFP_KERNEL); + if (!key) + return -ENOMEM; + + /* + * TPM 2.0 RSA keys are recommended to be 2048 bits long. Assume the + * blob is no more than 4x that. + */ + if (prep->datalen > 256 * 4) { + kfree(key); + return -EMSGSIZE; + } + + ret = tpm2_key_decode(prep->data, prep->datalen, &key->key, PAGE_SIZE); + if (ret) { + kfree(key); + return ret; + } + + if (key->key.oid != OID_TPMLoadableKey) { + tpm2_key_destroy(&key->key); + kfree(key); + return -EINVAL; + } + + ret = tpm2_key_rsa_extract_pub(key); + if (ret < 0) { + tpm2_key_destroy(&key->key); + 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_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 2f25ca07127b..8161758da19a 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,