* [PATCH v7 0/5] KEYS: asymmetric: tpm2_key_{rsa,ecdsa}
@ 2024-05-28 21:08 Jarkko Sakkinen
2024-05-28 21:08 ` [PATCH v7 1/5] crypto: rsa-pkcs1pad: export rsa1_asn_lookup() Jarkko Sakkinen
` (4 more replies)
0 siblings, 5 replies; 24+ messages in thread
From: Jarkko Sakkinen @ 2024-05-28 21:08 UTC (permalink / raw)
To: Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Stefan Berger, Lennart Poettering, Jarkko Sakkinen,
David S. Miller, open list
Testing
=======
RSA
---
tpm2_createprimary --hierarchy o -G rsa2048 -c owner.txt
tpm2_evictcontrol -c owner.txt 0x81000001
tpm2_getcap handles-persistent
openssl genrsa -out private.pem 2048
tpm2_import -C 0x81000001 -G rsa -i private.pem -u key.pub -r key.priv
tpm2_encodeobject -C 0x81000001 -u key.pub -r key.priv -o key.priv.pem
openssl asn1parse -inform pem -in key.priv.pem -noout -out key.priv.der
serial=`cat key.priv.der | keyctl padd asymmetric tpm @u`
echo "abcdefg" > plaintext.txt
keyctl pkey_encrypt $serial 0 plaintext.txt enc=pkcs1 > encrypted.dat
keyctl pkey_decrypt $serial 0 encrypted.dat enc=pkcs1 > decrypted.dat
keyctl pkey_sign $serial 0 plaintext.txt enc=pkcs1 hash=sha256 > signed.dat
keyctl pkey_verify $serial 0 plaintext.txt signed.dat enc=pkcs1 hash=sha256
ECDSA
-----
tpm2_createprimary --hierarchy o -G ecc -c owner.txt
tpm2_evictcontrol -c owner.txt 0x81000001
openssl ecparam -name prime256v1 -genkey -noout -out private.pem
tpm2_import -C 0x81000001 -G ecc -i private.pem -u key.pub -r key.priv
tpm2_encodeobject -C 0x81000001 -u key.pub -r key.priv -o key.priv.pem
openssl asn1parse -inform pem -in key.priv.pem -noout -out key.priv.der
serial=`cat key.priv.der | keyctl padd asymmetric tpm @u`
echo "abcdefg" > plaintext.txt
keyctl pkey_sign $serial 0 plaintext.txt hash=sha256 > signed.dat
keyctl pkey_verify $serial 0 plaintext.txt signed.dat hash=sha256
Closed Issues
=============
* When verifying ECDSA signature, _ecdsa_verify() returns -EKEYREJECTED.
* v7: rewrote the signature encoder with a more structured layout.
References
==========
* v6: https://lore.kernel.org/linux-integrity/20240528035136.11464-1-jarkko@kernel.org/
* v5: https://lore.kernel.org/linux-integrity/20240523212515.4875-1-jarkko@kernel.org/
* v4: https://lore.kernel.org/linux-integrity/20240522005252.17841-1-jarkko@kernel.org/
* v3: https://lore.kernel.org/linux-integrity/20240521152659.26438-1-jarkko@kernel.org/
* v2: https://lore.kernel.org/linux-integrity/336755.1716327854@warthog.procyon.org.uk/
* v1: https://lore.kernel.org/linux-integrity/20240520184727.22038-1-jarkko@kernel.org/
* Derived from https://lore.kernel.org/all/20200518172704.29608-1-prestwoj@gmail.com/
Jarkko Sakkinen (5):
crypto: rsa-pkcs1pad: export rsa1_asn_lookup()
KEYS: trusted: Change -EINVAL to -E2BIG
crypto: tpm2_key: Introduce a TPM2 key type
keys: asymmetric: Add tpm2_key_rsa
keys: asymmetric: Add tpm2_key_ecdsa
crypto/Kconfig | 7 +
crypto/Makefile | 6 +
crypto/asymmetric_keys/Kconfig | 30 +
crypto/asymmetric_keys/Makefile | 2 +
crypto/asymmetric_keys/tpm2_key_ecdsa.c | 462 +++++++++++++++
crypto/asymmetric_keys/tpm2_key_rsa.c | 678 ++++++++++++++++++++++
crypto/ecdsa.c | 1 -
crypto/rsa-pkcs1pad.c | 16 +-
crypto/tpm2_key.asn1 | 11 +
crypto/tpm2_key.c | 134 +++++
drivers/char/tpm/tpm-buf.c | 2 +-
include/crypto/rsa-pkcs1pad.h | 20 +
include/crypto/tpm2_key.h | 46 ++
include/linux/tpm.h | 9 +
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 +----
18 files changed, 1447 insertions(+), 133 deletions(-)
create mode 100644 crypto/asymmetric_keys/tpm2_key_ecdsa.c
create mode 100644 crypto/asymmetric_keys/tpm2_key_rsa.c
create mode 100644 crypto/tpm2_key.asn1
create mode 100644 crypto/tpm2_key.c
create mode 100644 include/crypto/rsa-pkcs1pad.h
create mode 100644 include/crypto/tpm2_key.h
delete mode 100644 security/keys/trusted-keys/tpm2key.asn1
--
2.45.1
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v7 1/5] crypto: rsa-pkcs1pad: export rsa1_asn_lookup()
2024-05-28 21:08 [PATCH v7 0/5] KEYS: asymmetric: tpm2_key_{rsa,ecdsa} Jarkko Sakkinen
@ 2024-05-28 21:08 ` Jarkko Sakkinen
2024-05-28 23:20 ` Stefan Berger
2024-05-28 21:08 ` [PATCH v7 2/5] KEYS: trusted: Change -EINVAL to -E2BIG Jarkko Sakkinen
` (3 subsequent siblings)
4 siblings, 1 reply; 24+ messages in thread
From: Jarkko Sakkinen @ 2024-05-28 21:08 UTC (permalink / raw)
To: Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Stefan Berger, Lennart Poettering, Jarkko Sakkinen,
David S. Miller, open list
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 <jarkko@kernel.org>
---
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 <crypto/algapi.h>
#include <crypto/akcipher.h>
+#include <crypto/rsa-pkcs1pad.h>
#include <crypto/internal/akcipher.h>
#include <crypto/internal/rsa.h>
#include <linux/err.h>
@@ -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 */
--
2.45.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v7 2/5] KEYS: trusted: Change -EINVAL to -E2BIG
2024-05-28 21:08 [PATCH v7 0/5] KEYS: asymmetric: tpm2_key_{rsa,ecdsa} Jarkko Sakkinen
2024-05-28 21:08 ` [PATCH v7 1/5] crypto: rsa-pkcs1pad: export rsa1_asn_lookup() Jarkko Sakkinen
@ 2024-05-28 21:08 ` Jarkko Sakkinen
2024-05-29 1:50 ` Stefan Berger
2024-05-28 21:08 ` [PATCH v7 3/5] crypto: tpm2_key: Introduce a TPM2 key type Jarkko Sakkinen
` (2 subsequent siblings)
4 siblings, 1 reply; 24+ messages in thread
From: Jarkko Sakkinen @ 2024-05-28 21:08 UTC (permalink / raw)
To: Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Stefan Berger, Lennart Poettering, Jarkko Sakkinen,
David S. Miller, open list, James Bottomley, Mimi Zohar,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
open list:SECURITY SUBSYSTEM
Report -E2BIG instead of -EINVAL when too large size for the key blob is
requested.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
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)
--
2.45.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v7 3/5] crypto: tpm2_key: Introduce a TPM2 key type
2024-05-28 21:08 [PATCH v7 0/5] KEYS: asymmetric: tpm2_key_{rsa,ecdsa} Jarkko Sakkinen
2024-05-28 21:08 ` [PATCH v7 1/5] crypto: rsa-pkcs1pad: export rsa1_asn_lookup() Jarkko Sakkinen
2024-05-28 21:08 ` [PATCH v7 2/5] KEYS: trusted: Change -EINVAL to -E2BIG Jarkko Sakkinen
@ 2024-05-28 21:08 ` Jarkko Sakkinen
2024-05-31 0:35 ` Stefan Berger
2024-05-28 21:08 ` [PATCH v7 4/5] keys: asymmetric: Add tpm2_key_rsa Jarkko Sakkinen
2024-05-28 21:08 ` [PATCH v7 5/5] keys: asymmetric: Add tpm2_key_ecdsa Jarkko Sakkinen
4 siblings, 1 reply; 24+ messages in thread
From: Jarkko Sakkinen @ 2024-05-28 21:08 UTC (permalink / raw)
To: Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Stefan Berger, Lennart Poettering, Jarkko Sakkinen,
David S. Miller, open list, James Bottomley, Mimi Zohar,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
open list:SECURITY SUBSYSTEM
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 <jarkko@kernel.org>
---
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 <crypto/tpm2_key.h>
+#include <linux/oid_registry.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <asm/unaligned.h>
+#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 <linux/oid_registry.h>
+#include <linux/slab.h>
+
+#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 <keys/trusted-type.h>
#include <keys/trusted_tpm.h>
+#include <crypto/tpm2_key.h>
#include <asm/unaligned.h>
-#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)
--
2.45.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v7 4/5] keys: asymmetric: Add tpm2_key_rsa
2024-05-28 21:08 [PATCH v7 0/5] KEYS: asymmetric: tpm2_key_{rsa,ecdsa} Jarkko Sakkinen
` (2 preceding siblings ...)
2024-05-28 21:08 ` [PATCH v7 3/5] crypto: tpm2_key: Introduce a TPM2 key type Jarkko Sakkinen
@ 2024-05-28 21:08 ` Jarkko Sakkinen
2024-05-29 16:07 ` Jarkko Sakkinen
` (2 more replies)
2024-05-28 21:08 ` [PATCH v7 5/5] keys: asymmetric: Add tpm2_key_ecdsa Jarkko Sakkinen
4 siblings, 3 replies; 24+ messages in thread
From: Jarkko Sakkinen @ 2024-05-28 21:08 UTC (permalink / raw)
To: Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Stefan Berger, Lennart Poettering, Jarkko Sakkinen,
David S. Miller, open list, David Howells, James Bottomley,
Ard Biesheuvel, Mario Limonciello
* 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 <prestwoj@gmail.com>
Co-developed-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
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 <asm/unaligned.h>
+#include <crypto/akcipher.h>
+#include <crypto/public_key.h>
+#include <crypto/rsa-pkcs1pad.h>
+#include <crypto/tpm2_key.h>
+#include <keys/asymmetric-parser.h>
+#include <keys/asymmetric-subtype.h>
+#include <keys/trusted-type.h>
+#include <linux/keyctl.h>
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/tpm.h>
+
+#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,
--
2.45.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v7 5/5] keys: asymmetric: Add tpm2_key_ecdsa
2024-05-28 21:08 [PATCH v7 0/5] KEYS: asymmetric: tpm2_key_{rsa,ecdsa} Jarkko Sakkinen
` (3 preceding siblings ...)
2024-05-28 21:08 ` [PATCH v7 4/5] keys: asymmetric: Add tpm2_key_rsa Jarkko Sakkinen
@ 2024-05-28 21:08 ` Jarkko Sakkinen
2024-05-28 21:42 ` Jarkko Sakkinen
2024-05-28 23:15 ` Stefan Berger
4 siblings, 2 replies; 24+ messages in thread
From: Jarkko Sakkinen @ 2024-05-28 21:08 UTC (permalink / raw)
To: Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Stefan Berger, Lennart Poettering, Jarkko Sakkinen,
David S. Miller, open list, David Howells, Peter Huewe,
Jason Gunthorpe, James Bottomley, Ard Biesheuvel,
Mario Limonciello
* Asymmetric TPM2 ECDSA key with signing and verification.
* Enabled with CONFIG_ASYMMETRIC_TPM2_KEY_ECDSA_SUBTYPE.
Cc: Stefan Berger <stefanb@linux.ibm.com>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
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 <asm/unaligned.h>
+#include <crypto/internal/ecc.h>
+#include <crypto/akcipher.h>
+#include <crypto/sha2.h>
+#include <crypto/public_key.h>
+#include <crypto/tpm2_key.h>
+#include <keys/asymmetric-parser.h>
+#include <keys/asymmetric-subtype.h>
+#include <linux/asn1_encoder.h>
+#include <linux/keyctl.h>
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/tpm.h>
+
+#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.
--
2.45.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH v7 5/5] keys: asymmetric: Add tpm2_key_ecdsa
2024-05-28 21:08 ` [PATCH v7 5/5] keys: asymmetric: Add tpm2_key_ecdsa Jarkko Sakkinen
@ 2024-05-28 21:42 ` Jarkko Sakkinen
2024-05-28 23:09 ` Jarkko Sakkinen
2024-05-28 23:15 ` Stefan Berger
1 sibling, 1 reply; 24+ messages in thread
From: Jarkko Sakkinen @ 2024-05-28 21:42 UTC (permalink / raw)
To: Jarkko Sakkinen, Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Stefan Berger, Lennart Poettering, David S. Miller, open list,
David Howells, Peter Huewe, Jason Gunthorpe, James Bottomley,
Ard Biesheuvel, Mario Limonciello
On Wed May 29, 2024 at 12:08 AM EEST, Jarkko Sakkinen wrote:
> + /* 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;
Stefan, so this how I realized the signature encoding, thanks to
your earlier remarks [1]! I found out based on that a few glitches
and ended up with this better structured ECDSA signature encoder,
so thank you for doing that.
[1] https://lore.kernel.org/linux-crypto/b5ff9003-065f-437f-bf6b-7f1ae0a0364a@linux.ibm.com/
BR, Jarkko
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v7 5/5] keys: asymmetric: Add tpm2_key_ecdsa
2024-05-28 21:42 ` Jarkko Sakkinen
@ 2024-05-28 23:09 ` Jarkko Sakkinen
0 siblings, 0 replies; 24+ messages in thread
From: Jarkko Sakkinen @ 2024-05-28 23:09 UTC (permalink / raw)
To: Jarkko Sakkinen, Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Stefan Berger, Lennart Poettering, David S. Miller, open list,
David Howells, Peter Huewe, Jason Gunthorpe, James Bottomley,
Ard Biesheuvel, Mario Limonciello
On Wed May 29, 2024 at 12:42 AM EEST, Jarkko Sakkinen wrote:
> On Wed May 29, 2024 at 12:08 AM EEST, Jarkko Sakkinen wrote:
> > + /* 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;
>
> Stefan, so this how I realized the signature encoding, thanks to
> your earlier remarks [1]! I found out based on that a few glitches
> and ended up with this better structured ECDSA signature encoder,
> so thank you for doing that.
1. SHA384 would fit without any significant changes.
2. SHA512 will require a single additional byte, i.e. prefix byte 0x81
for the sequence (not for coeffients).
3. SM3 similarly is also trivial to add.
Both will be also easy to add later on. I would not enlarge this patch
set from what it is now.
So I think this is pretty well along the lines of:
https://datatracker.ietf.org/doc/draft-woodhouse-cert-best-practice/
BR, Jarkko
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v7 5/5] keys: asymmetric: Add tpm2_key_ecdsa
2024-05-28 21:08 ` [PATCH v7 5/5] keys: asymmetric: Add tpm2_key_ecdsa Jarkko Sakkinen
2024-05-28 21:42 ` Jarkko Sakkinen
@ 2024-05-28 23:15 ` Stefan Berger
2024-05-29 1:14 ` Jarkko Sakkinen
1 sibling, 1 reply; 24+ messages in thread
From: Stefan Berger @ 2024-05-28 23:15 UTC (permalink / raw)
To: Jarkko Sakkinen, Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Lennart Poettering, David S. Miller, open list, David Howells,
Peter Huewe, Jason Gunthorpe, Ard Biesheuvel, Mario Limonciello
On 5/28/24 17:08, Jarkko Sakkinen wrote:
> * Asymmetric TPM2 ECDSA key with signing and verification.
> * Enabled with CONFIG_ASYMMETRIC_TPM2_KEY_ECDSA_SUBTYPE.
>
> Cc: Stefan Berger <stefanb@linux.ibm.com>
> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> ---
> 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 <asm/unaligned.h>
> +#include <crypto/internal/ecc.h>
> +#include <crypto/akcipher.h>
> +#include <crypto/sha2.h>
> +#include <crypto/public_key.h>
> +#include <crypto/tpm2_key.h>
> +#include <keys/asymmetric-parser.h>
> +#include <keys/asymmetric-subtype.h>
> +#include <linux/asn1_encoder.h>
> +#include <linux/keyctl.h>
> +#include <linux/module.h>
> +#include <linux/scatterlist.h>
> +#include <linux/slab.h>
> +#include <linux/tpm.h>
> +
> +#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];
Due to NIST p521 1 + 2 * 66 = 133 should be enough.
If x_size exceeeds 66 then something is wrong.
> + 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;
The size of the signature has nothing to do with the size of the hash.
SHA256_DIGEST_SIZE (32) happens to match the number of bytes of a
coordinate of prime256v1 / NIST p256 but should fail when you use
secp521r1 / NIST p521 since then r or s may then be 66 or 67 bytes (if
most sign. bit is set) long.
> + 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;
> +}
> +
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v7 1/5] crypto: rsa-pkcs1pad: export rsa1_asn_lookup()
2024-05-28 21:08 ` [PATCH v7 1/5] crypto: rsa-pkcs1pad: export rsa1_asn_lookup() Jarkko Sakkinen
@ 2024-05-28 23:20 ` Stefan Berger
2024-05-29 1:25 ` Jarkko Sakkinen
0 siblings, 1 reply; 24+ messages in thread
From: Stefan Berger @ 2024-05-28 23:20 UTC (permalink / raw)
To: Jarkko Sakkinen, Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Lennart Poettering, David S. Miller, open list
On 5/28/24 17:08, Jarkko Sakkinen wrote:
> 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
piggy-backed
> patch prepares crypto subsystem for the addition of those keys.
>
> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
> 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 <crypto/algapi.h>
> #include <crypto/akcipher.h>
> +#include <crypto/rsa-pkcs1pad.h>
> #include <crypto/internal/akcipher.h>
> #include <crypto/internal/rsa.h>
> #include <linux/err.h>
> @@ -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 */
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v7 5/5] keys: asymmetric: Add tpm2_key_ecdsa
2024-05-28 23:15 ` Stefan Berger
@ 2024-05-29 1:14 ` Jarkko Sakkinen
0 siblings, 0 replies; 24+ messages in thread
From: Jarkko Sakkinen @ 2024-05-29 1:14 UTC (permalink / raw)
To: Stefan Berger, Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Lennart Poettering, David S. Miller, open list, David Howells,
Peter Huewe, Jason Gunthorpe, Ard Biesheuvel, Mario Limonciello
On Wed May 29, 2024 at 2:15 AM EEST, Stefan Berger wrote:
> > + ptr[TPM2_KEY_ECDSA_SIG_R_TAG] = 0x02; /* INTEGER */
> > + ptr[TPM2_KEY_ECDSA_SIG_R_SIZE] = SHA256_DIGEST_SIZE + r_0;
>
> The size of the signature has nothing to do with the size of the hash.
> SHA256_DIGEST_SIZE (32) happens to match the number of bytes of a
> coordinate of prime256v1 / NIST p256 but should fail when you use
> secp521r1 / NIST p521 since then r or s may then be 66 or 67 bytes (if
> most sign. bit is set) long.
First remark did not go unnoticed, so thanks for both. There was not
just much to comment on it :-)
I could just replace the constant with a (range checked) variable
read from the response and overall structure woud be the same.
This will also mean that in the case of P521 also prefix byte (0x81) is
required but just for the sequence I think, not for the integers.
Finally, I need to implement p521 smoke test for testing this patch set.
One big letdown that I only now have consciously realized, is that TCG
does not have p256k1 in their algorithm repository. It is the basis for
quite a few blockchain technologies. I wonder why...
BR, Jarkko
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v7 1/5] crypto: rsa-pkcs1pad: export rsa1_asn_lookup()
2024-05-28 23:20 ` Stefan Berger
@ 2024-05-29 1:25 ` Jarkko Sakkinen
0 siblings, 0 replies; 24+ messages in thread
From: Jarkko Sakkinen @ 2024-05-29 1:25 UTC (permalink / raw)
To: Stefan Berger, Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Lennart Poettering, David S. Miller, open list
On Wed May 29, 2024 at 2:20 AM EEST, Stefan Berger wrote:
>
>
> On 5/28/24 17:08, Jarkko Sakkinen wrote:
> > 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
>
> piggy-backed
Right! I consciously wrote it that way, i.e. have used wrong spelling
up to this day :-)
Thanks for the review! This is not likely to change that much. Would
not tag any other patches tho before up to p521 have been tested...
BR, Jarkko
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v7 2/5] KEYS: trusted: Change -EINVAL to -E2BIG
2024-05-28 21:08 ` [PATCH v7 2/5] KEYS: trusted: Change -EINVAL to -E2BIG Jarkko Sakkinen
@ 2024-05-29 1:50 ` Stefan Berger
2024-05-29 12:20 ` Jarkko Sakkinen
0 siblings, 1 reply; 24+ messages in thread
From: Stefan Berger @ 2024-05-29 1:50 UTC (permalink / raw)
To: Jarkko Sakkinen, Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Lennart Poettering, David S. Miller, open list, Mimi Zohar,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
open list:SECURITY SUBSYSTEM
On 5/28/24 17:08, Jarkko Sakkinen wrote:
> Report -E2BIG instead of -EINVAL when too large size for the key blob is
> requested.
>
> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
> 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)
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v7 2/5] KEYS: trusted: Change -EINVAL to -E2BIG
2024-05-29 1:50 ` Stefan Berger
@ 2024-05-29 12:20 ` Jarkko Sakkinen
0 siblings, 0 replies; 24+ messages in thread
From: Jarkko Sakkinen @ 2024-05-29 12:20 UTC (permalink / raw)
To: Stefan Berger, Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Lennart Poettering, David S. Miller, open list, Mimi Zohar,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
open list:SECURITY SUBSYSTEM
On Wed May 29, 2024 at 4:50 AM EEST, Stefan Berger wrote:
>
>
> On 5/28/24 17:08, Jarkko Sakkinen wrote:
> > Report -E2BIG instead of -EINVAL when too large size for the key blob is
> > requested.
> >
> > Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
>
> Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Thank you.
Hmm... I'd like to add even:
Cc: stable@vger.kernel.org # v5.13+
Fixes: f2219745250f ("security: keys: trusted: use ASN.1 TPM2 key format for the blobs")
It turned out to be useful error message and would be useful also for
stable kernels. So if no decent counter-arguments, I'll just pick it
to my master branch.
BR, Jarkko
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v7 4/5] keys: asymmetric: Add tpm2_key_rsa
2024-05-28 21:08 ` [PATCH v7 4/5] keys: asymmetric: Add tpm2_key_rsa Jarkko Sakkinen
@ 2024-05-29 16:07 ` Jarkko Sakkinen
2024-05-31 1:10 ` Stefan Berger
2024-06-07 10:58 ` Herbert Xu
2 siblings, 0 replies; 24+ messages in thread
From: Jarkko Sakkinen @ 2024-05-29 16:07 UTC (permalink / raw)
To: Jarkko Sakkinen, Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Stefan Berger, Lennart Poettering, David S. Miller, open list,
David Howells, James Bottomley, Ard Biesheuvel, Mario Limonciello,
Dan Carpenter
On Wed May 29, 2024 at 12:08 AM EEST, Jarkko Sakkinen wrote:
> * 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 <prestwoj@gmail.com>
> Co-developed-by: Jarkko Sakkinen <jarkko@kernel.org>
> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
To be addressed in v8:
"
4970b8d723c3af Jarkko Sakkinen 2024-05-26 634 key = tpm2_key_decode(prep->data, prep->datalen);
4970b8d723c3af Jarkko Sakkinen 2024-05-26 635 if (IS_ERR(key))
4970b8d723c3af Jarkko Sakkinen 2024-05-26 @636 return ret;
^^^^^^^^^^
Same. return PTR_ERR(key);
" - https://lore.kernel.org/all/cbae0ed0-e0a6-41ba-9671-a9f48e8f07f3@moroto.mountain/
BR, Jarkko
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v7 3/5] crypto: tpm2_key: Introduce a TPM2 key type
2024-05-28 21:08 ` [PATCH v7 3/5] crypto: tpm2_key: Introduce a TPM2 key type Jarkko Sakkinen
@ 2024-05-31 0:35 ` Stefan Berger
2024-06-04 17:23 ` Jarkko Sakkinen
0 siblings, 1 reply; 24+ messages in thread
From: Stefan Berger @ 2024-05-31 0:35 UTC (permalink / raw)
To: Jarkko Sakkinen, Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Lennart Poettering, David S. Miller, open list, Mimi Zohar,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
open list:SECURITY SUBSYSTEM
On 5/28/24 17:08, Jarkko Sakkinen wrote:
> 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 <jarkko@kernel.org>
> ---
> 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 <crypto/tpm2_key.h>
> +#include <linux/oid_registry.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +#include <asm/unaligned.h>
> +#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.
I am not sure what the last sentence means.
> + *
> + * 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 <linux/oid_registry.h>
> +#include <linux/slab.h>
> +
> +#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 <keys/trusted-type.h>
> #include <keys/trusted_tpm.h>
> +#include <crypto/tpm2_key.h>
>
> #include <asm/unaligned.h>
>
> -#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);
tpm2_key_decode seems simple enough that it only returns key, -ENOMEM or
EBADMSG.
> +
> + /* 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.
sentence seems incomplete: ... for the old one. OR ... for the old format.
> + */
> 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)
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v7 4/5] keys: asymmetric: Add tpm2_key_rsa
2024-05-28 21:08 ` [PATCH v7 4/5] keys: asymmetric: Add tpm2_key_rsa Jarkko Sakkinen
2024-05-29 16:07 ` Jarkko Sakkinen
@ 2024-05-31 1:10 ` Stefan Berger
2024-06-04 20:29 ` Jarkko Sakkinen
2024-06-07 10:58 ` Herbert Xu
2 siblings, 1 reply; 24+ messages in thread
From: Stefan Berger @ 2024-05-31 1:10 UTC (permalink / raw)
To: Jarkko Sakkinen, Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Lennart Poettering, David S. Miller, open list, David Howells,
Ard Biesheuvel, Mario Limonciello
On 5/28/24 17:08, Jarkko Sakkinen wrote:
> * Asymmetric TPM2 RSA key with signing and verification.
> * Encryption and decryption when pcks1 encoding is used.
> * Enabled with CONFIG_ASYMMETRIC_TPM2_KEY_ECDSA_SUBTYPE.
s/ECDSA/RSA !
>
> Signed-off-by: James Prestwood <prestwoj@gmail.com>
> Co-developed-by: Jarkko Sakkinen <jarkko@kernel.org>
> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> ---
> 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.
> +
s/requisite/required ?
> 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.
s/pcks1pad/pkcs1pad !
> + *
> + * See Documentation/crypto/asymmetric-keys.rst
> + *
> + * Copyright (c) 2020 Intel Corporation
> + */
> +
> +#include <asm/unaligned.h>
> +#include <crypto/akcipher.h>
> +#include <crypto/public_key.h>
> +#include <crypto/rsa-pkcs1pad.h>
> +#include <crypto/tpm2_key.h>
> +#include <keys/asymmetric-parser.h>
> +#include <keys/asymmetric-subtype.h>
> +#include <keys/trusted-type.h>
> +#include <linux/keyctl.h>
> +#include <linux/module.h>
> +#include <linux/scatterlist.h>
> +#include <linux/slab.h>
> +#include <linux/tpm.h>
> +
> +#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
according to
> + * that there is epilogue with algorithm OID and parameters length.
is an epilogue
> + * 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)
if (ret < 0)
> + return ret;
> +
> + ret = tpm2_start_auth_session(chip);
> + if (ret)
Uh, this one can return TPM error codes it seems from
tpm_transmit_cmd()? You probably have to do something with ret here in
case it's positive because I saw a caller of __tpm2_key_rsa_decrypt
relying on ret < 0 as error.
> + 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) {
I suppose params->out_len describes the size of void *out buffer..
> + 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);
check NULL pointer?
> + 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;
This seems unnecessary.
> +
> + 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);
Missing verify here?
> + 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,
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v7 3/5] crypto: tpm2_key: Introduce a TPM2 key type
2024-05-31 0:35 ` Stefan Berger
@ 2024-06-04 17:23 ` Jarkko Sakkinen
2024-06-04 18:41 ` Stefan Berger
0 siblings, 1 reply; 24+ messages in thread
From: Jarkko Sakkinen @ 2024-06-04 17:23 UTC (permalink / raw)
To: Stefan Berger, Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Lennart Poettering, David S. Miller, open list, Mimi Zohar,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
open list:SECURITY SUBSYSTEM
On Fri May 31, 2024 at 3:35 AM EEST, Stefan Berger wrote:
>
>
> On 5/28/24 17:08, Jarkko Sakkinen wrote:
> > 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 <jarkko@kernel.org>
> > ---
> > 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 <crypto/tpm2_key.h>
> > +#include <linux/oid_registry.h>
> > +#include <linux/slab.h>
> > +#include <linux/types.h>
> > +#include <asm/unaligned.h>
> > +#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.
>
> I am not sure what the last sentence means.
I think the whole paragraph should be rewritten.
So what it does is that it takes the private and public parts and
concanates them together so maybe just write:
"Load TPMT_PUBLIC and TPMT_PRIVATE from ASN.1 file, and concatenate the
blobs together as a single blob, as this is expected format for the TPM2
commands. In addition, validate TPMT_PUBLIC fields so that they make
sense for trusted and asymmetric keys."
What you think of this?
>
> > + *
> > + * 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 <linux/oid_registry.h>
> > +#include <linux/slab.h>
> > +
> > +#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 <keys/trusted-type.h>
> > #include <keys/trusted_tpm.h>
> > +#include <crypto/tpm2_key.h>
> >
> > #include <asm/unaligned.h>
> >
> > -#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);
>
> tpm2_key_decode seems simple enough that it only returns key, -ENOMEM or
> EBADMSG.
So what is your suggestion here?
The reasoning here is that asymmetric keys use -EBADMSG not only as
error but also iterator, when probing which can load a specific key.
>
> > +
> > + /* 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.
>
> sentence seems incomplete: ... for the old one. OR ... for the old format.
Yep, I think it is a plain mistake.
>
> > + */
> > 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)
Thanks for the feedback.
BR, Jarkko
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v7 3/5] crypto: tpm2_key: Introduce a TPM2 key type
2024-06-04 17:23 ` Jarkko Sakkinen
@ 2024-06-04 18:41 ` Stefan Berger
2024-06-04 22:33 ` Jarkko Sakkinen
0 siblings, 1 reply; 24+ messages in thread
From: Stefan Berger @ 2024-06-04 18:41 UTC (permalink / raw)
To: Jarkko Sakkinen, Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Lennart Poettering, David S. Miller, open list, Mimi Zohar,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
open list:SECURITY SUBSYSTEM
On 6/4/24 13:23, Jarkko Sakkinen wrote:
> On Fri May 31, 2024 at 3:35 AM EEST, Stefan Berger wrote:
>>
>>>
>>> - 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);
>>
>> tpm2_key_decode seems simple enough that it only returns key, -ENOMEM or
>> EBADMSG.
>
> So what is your suggestion here?
You can remove the check resuling in pr_err().
>
> The reasoning here is that asymmetric keys use -EBADMSG not only as
> error but also iterator, when probing which can load a specific key.
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v7 4/5] keys: asymmetric: Add tpm2_key_rsa
2024-05-31 1:10 ` Stefan Berger
@ 2024-06-04 20:29 ` Jarkko Sakkinen
0 siblings, 0 replies; 24+ messages in thread
From: Jarkko Sakkinen @ 2024-06-04 20:29 UTC (permalink / raw)
To: Stefan Berger, Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Lennart Poettering, David S. Miller, open list, David Howells,
Ard Biesheuvel, Mario Limonciello
On Fri May 31, 2024 at 4:10 AM EEST, Stefan Berger wrote:
>
>
> On 5/28/24 17:08, Jarkko Sakkinen wrote:
> > * Asymmetric TPM2 RSA key with signing and verification.
> > * Encryption and decryption when pcks1 encoding is used.
> > * Enabled with CONFIG_ASYMMETRIC_TPM2_KEY_ECDSA_SUBTYPE.
>
> s/ECDSA/RSA !
Thanks, note taken.
>
> >
> > Signed-off-by: James Prestwood <prestwoj@gmail.com>
> > Co-developed-by: Jarkko Sakkinen <jarkko@kernel.org>
> > Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> > ---
> > 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.
> > +
>
> s/requisite/required ?
Ack.
>
> > 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.
>
> s/pcks1pad/pkcs1pad !
+1
>
>
> > + *
> > + * See Documentation/crypto/asymmetric-keys.rst
> > + *
> > + * Copyright (c) 2020 Intel Corporation
> > + */
> > +
> > +#include <asm/unaligned.h>
> > +#include <crypto/akcipher.h>
> > +#include <crypto/public_key.h>
> > +#include <crypto/rsa-pkcs1pad.h>
> > +#include <crypto/tpm2_key.h>
> > +#include <keys/asymmetric-parser.h>
> > +#include <keys/asymmetric-subtype.h>
> > +#include <keys/trusted-type.h>
> > +#include <linux/keyctl.h>
> > +#include <linux/module.h>
> > +#include <linux/scatterlist.h>
> > +#include <linux/slab.h>
> > +#include <linux/tpm.h>
> > +
> > +#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
>
> according to
>
> > + * that there is epilogue with algorithm OID and parameters length.
>
> is an epilogue
+1
>
> > + * 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)
>
> if (ret < 0)
+1
>
> > + return ret;
> > +
> > + ret = tpm2_start_auth_session(chip);
> > + if (ret)
>
> Uh, this one can return TPM error codes it seems from
> tpm_transmit_cmd()? You probably have to do something with ret here in
> case it's positive because I saw a caller of __tpm2_key_rsa_decrypt
> relying on ret < 0 as error.
Good catch, thanks.
>
> > + 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) {
>
> I suppose params->out_len describes the size of void *out buffer..
>
> > + 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);
>
> check NULL pointer?
>
> > + 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;
>
> This seems unnecessary.
>
> > +
> > + 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);
>
> Missing verify here?
>
> > + 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,
Yeah, all remarks make total sense to me, thank you.
BR, Jarkko
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v7 3/5] crypto: tpm2_key: Introduce a TPM2 key type
2024-06-04 18:41 ` Stefan Berger
@ 2024-06-04 22:33 ` Jarkko Sakkinen
0 siblings, 0 replies; 24+ messages in thread
From: Jarkko Sakkinen @ 2024-06-04 22:33 UTC (permalink / raw)
To: Stefan Berger, Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Lennart Poettering, David S. Miller, open list, Mimi Zohar,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
open list:SECURITY SUBSYSTEM
On Tue Jun 4, 2024 at 9:41 PM EEST, Stefan Berger wrote:
>
>
> On 6/4/24 13:23, Jarkko Sakkinen wrote:
> > On Fri May 31, 2024 at 3:35 AM EEST, Stefan Berger wrote:
> >>
>
> >>>
> >>> - 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);
> >>
> >> tpm2_key_decode seems simple enough that it only returns key, -ENOMEM or
> >> EBADMSG.
> >
> > So what is your suggestion here?
>
> You can remove the check resuling in pr_err().
OK, I think so too. Just had to (sanity) check.
>
> >
> > The reasoning here is that asymmetric keys use -EBADMSG not only as
> > error but also iterator, when probing which can load a specific key.
> >
BR, Jarkko
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v7 4/5] keys: asymmetric: Add tpm2_key_rsa
2024-05-28 21:08 ` [PATCH v7 4/5] keys: asymmetric: Add tpm2_key_rsa Jarkko Sakkinen
2024-05-29 16:07 ` Jarkko Sakkinen
2024-05-31 1:10 ` Stefan Berger
@ 2024-06-07 10:58 ` Herbert Xu
2024-06-20 0:23 ` Jarkko Sakkinen
2 siblings, 1 reply; 24+ messages in thread
From: Herbert Xu @ 2024-06-07 10:58 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Stefan Berger, Lennart Poettering, David S. Miller, open list,
David Howells, Ard Biesheuvel, Mario Limonciello
On Wed, May 29, 2024 at 12:08:09AM +0300, Jarkko Sakkinen wrote:
>
> +/*
> + * 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);
Could you please explain why this can't be done through pkcs1pad
instead of going to raw RSA?
Thanks,
--
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v7 4/5] keys: asymmetric: Add tpm2_key_rsa
2024-06-07 10:58 ` Herbert Xu
@ 2024-06-20 0:23 ` Jarkko Sakkinen
2025-08-26 8:25 ` Jarkko Sakkinen
0 siblings, 1 reply; 24+ messages in thread
From: Jarkko Sakkinen @ 2024-06-20 0:23 UTC (permalink / raw)
To: Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Stefan Berger, Lennart Poettering, David S. Miller, open list,
David Howells, Ard Biesheuvel, Mario Limonciello
On Fri Jun 7, 2024 at 1:58 PM EEST, Herbert Xu wrote:
> On Wed, May 29, 2024 at 12:08:09AM +0300, Jarkko Sakkinen wrote:
> >
> > +/*
> > + * 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);
>
> Could you please explain why this can't be done through pkcs1pad
> instead of going to raw RSA?
Sorry was away couple of weeks from here. I replace this with TPM2_Sign
as is done already in the ECDSA module, so I guess that is a "yes".
BR, Jarkko
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v7 4/5] keys: asymmetric: Add tpm2_key_rsa
2024-06-20 0:23 ` Jarkko Sakkinen
@ 2025-08-26 8:25 ` Jarkko Sakkinen
0 siblings, 0 replies; 24+ messages in thread
From: Jarkko Sakkinen @ 2025-08-26 8:25 UTC (permalink / raw)
To: Herbert Xu
Cc: linux-integrity, keyrings, Andreas.Fuchs, James Prestwood,
David Woodhouse, Eric Biggers, James Bottomley, linux-crypto,
Stefan Berger, Lennart Poettering, David S. Miller, open list,
David Howells, Ard Biesheuvel, Mario Limonciello
On Thu, Jun 20, 2024 at 03:23:20AM +0300, Jarkko Sakkinen wrote:
> On Fri Jun 7, 2024 at 1:58 PM EEST, Herbert Xu wrote:
> > On Wed, May 29, 2024 at 12:08:09AM +0300, Jarkko Sakkinen wrote:
> > >
> > > +/*
> > > + * 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);
> >
> > Could you please explain why this can't be done through pkcs1pad
> > instead of going to raw RSA?
>
> Sorry was away couple of weeks from here. I replace this with TPM2_Sign
> as is done already in the ECDSA module, so I guess that is a "yes".
Time travelling back to 2024 ;-)
I can't recall what I was thining here butI'm glad that I did not put
the patch set further as now I have much more sane angle to this.
I realized while working on [1] that I'm better of making this to work
as API on rsapubkey.asn1 and rsaprivkey.asn1 and matching files for
ECC and do all steps inside kernel from this:
tpm2_createprimary --hierarchy o -G rsa2048 -c owner.txt
tpm2_evictcontrol -c owner.txt 0x81000001
tpm2_getcap handles-persistent
openssl genrsa -out private.pem 2048
tpm2_import -C 0x81000001 -G rsa -i private.pem -u key.pub -r key.priv
tpm2_encodeobject -C 0x81000001 -u key.pub -r key.priv -o key.priv.pem
openssl asn1parse -inform pem -in key.priv.pem -noout -out key.priv.der
I.e. my test tool does everything else except
openssl genrsa -out private.pem 2048
Im now pretty familiar with import procedure and how to prepare data
for TPM2_Import and is like the "spirit" of it i.e., take external
key and store it inside TPM2. That as side effect removes all the
use of tpm2key.asn1 from the patch set and simplifies flows
greatly.
And my Rust works help to get the preparation procedure exactly
right and none of those crazy tools and commands will be needed.
The matching C code following TCG Architecture spec I'll first write in
user space and then port that kernel crypto APIs
That spans a question tho: should it be its own key type (as it is
right now or would it be better idea to have parameter/option for
hardware pre-existing RSA key types, or what would be the best
direction API wise to approach this?
[1] https://lore.kernel.org/tpm2/aKzaTYCI2GO_UPRB@kernel.org/T/#u
BR, Jarkko
^ permalink raw reply [flat|nested] 24+ messages in thread
end of thread, other threads:[~2025-08-26 8:25 UTC | newest]
Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-05-28 21:08 [PATCH v7 0/5] KEYS: asymmetric: tpm2_key_{rsa,ecdsa} Jarkko Sakkinen
2024-05-28 21:08 ` [PATCH v7 1/5] crypto: rsa-pkcs1pad: export rsa1_asn_lookup() Jarkko Sakkinen
2024-05-28 23:20 ` Stefan Berger
2024-05-29 1:25 ` Jarkko Sakkinen
2024-05-28 21:08 ` [PATCH v7 2/5] KEYS: trusted: Change -EINVAL to -E2BIG Jarkko Sakkinen
2024-05-29 1:50 ` Stefan Berger
2024-05-29 12:20 ` Jarkko Sakkinen
2024-05-28 21:08 ` [PATCH v7 3/5] crypto: tpm2_key: Introduce a TPM2 key type Jarkko Sakkinen
2024-05-31 0:35 ` Stefan Berger
2024-06-04 17:23 ` Jarkko Sakkinen
2024-06-04 18:41 ` Stefan Berger
2024-06-04 22:33 ` Jarkko Sakkinen
2024-05-28 21:08 ` [PATCH v7 4/5] keys: asymmetric: Add tpm2_key_rsa Jarkko Sakkinen
2024-05-29 16:07 ` Jarkko Sakkinen
2024-05-31 1:10 ` Stefan Berger
2024-06-04 20:29 ` Jarkko Sakkinen
2024-06-07 10:58 ` Herbert Xu
2024-06-20 0:23 ` Jarkko Sakkinen
2025-08-26 8:25 ` Jarkko Sakkinen
2024-05-28 21:08 ` [PATCH v7 5/5] keys: asymmetric: Add tpm2_key_ecdsa Jarkko Sakkinen
2024-05-28 21:42 ` Jarkko Sakkinen
2024-05-28 23:09 ` Jarkko Sakkinen
2024-05-28 23:15 ` Stefan Berger
2024-05-29 1:14 ` Jarkko Sakkinen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).