From nobody Wed Feb 11 05:40:54 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F2C7112AAC9; Thu, 23 May 2024 21:25:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716499551; cv=none; b=G8xpoDUHx6fMXEObq13XLDyuJkWcNyP0SLj6uPe1JdWmHbrbVsLBPIkBkt2HG6xV5QoXpUYsBAROAEh7wiHSkeWKLJZOAEeIMfT8t+PK/WyNiu+ntWcqo45t5E899uVlEDWbiqKE+UnGgeIU9QJFXukn31K7YzvTVGmwsDmxpZA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716499551; c=relaxed/simple; bh=NdagsQDESvEw86U+SE9PoSnY3YI58xiUWidZNr3CLA0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=oCQQginezYLvhCbUIK7Y6x+yvqTRKy5fR6hjYAJ+U/+X+5NREfS1/9zUuEttT0RElTuyPftoA7Txw+WmScsOHYhKGmrk+RtE7oN8yqrmLCqZpYadKC3J87WVrB2Wj+0KlLaZG6u8DhlyCl/rmmf38c1tuJAGiB+qEmzdyBomuDY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FsB5ShcU; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="FsB5ShcU" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2B1CAC2BD10; Thu, 23 May 2024 21:25:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1716499550; bh=NdagsQDESvEw86U+SE9PoSnY3YI58xiUWidZNr3CLA0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FsB5ShcUXHEtJfvQyTm2M/F7cgSkrKHgsXUf0mT3HHME6S2Hf0F2n95hlPAg8BFAZ ZC/UDg3evcc+/ZKA4r9ynk2yUChOZqtCNyYIcfgtEa09v+V9jMvMBU9vEV1dGkpa4L jr+dKPVqCR44hRx7wabStk05AZhFbqpELwOlgFs6PlTAkL+bzzI3TYvSAArmyUmtmn DCuVTYfzEHiNL1jVtHX0bQquqcICV6a1g+r6dYJZttYmW9XlUY3lYe7btaVNTJ8QmQ BAWvjiCSmDFzSERZ6fkJSY7Ns2yRR0/HaiPMCaadM6tsyy6DDSQ9wqQm3e2SaM7aro Av6jv/wmJpj1g== From: Jarkko Sakkinen To: Herbert Xu Cc: linux-integrity@vger.kernel.org, keyrings@vger.kernel.org, Andreas.Fuchs@infineon.com, James Prestwood , David Woodhouse , Eric Biggers , James Bottomley , linux-crypto@vger.kernel.org, Jarkko Sakkinen , "David S. Miller" , linux-kernel@vger.kernel.org (open list), David Howells , James Bottomley , Stefan Berger , Ard Biesheuvel , Mario Limonciello Subject: [PATCH v5 5/5] keys: asymmetric: ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE Date: Fri, 24 May 2024 00:25:06 +0300 Message-ID: <20240523212515.4875-6-jarkko@kernel.org> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20240523212515.4875-1-jarkko@kernel.org> References: <20240523212515.4875-1-jarkko@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: James Prestwood Based on earlier work by James Prestwood. Add ASN.1 compatible asymmetric TPM2 RSA key subtype: 1. Signing and decryption (with the private key) is handled by TPM2_RSA_Decrypt. 2. Encryption (with the public key) is handled by the kernel RSA implementation. Signed-off-by: James Prestwood Co-developed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- 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 | 680 ++++++++++++++++++++++++++ include/linux/tpm.h | 2 + 4 files changed, 698 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..71448c2f0a8f 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. =20 +config ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE + tristate "Asymmetric TPM2 RSA crypto algorithm subtype" + depends on TCG_TPM + select TCG_TPM2_HMAC + select CRYPTO_RSA + select CRYPTO_SHA256 + select CRYPTO_HASH_INFO + select ASN1 + 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/Makef= ile index bc65d3b98dcb..c6da84607824 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -11,6 +11,7 @@ asymmetric_keys-y :=3D \ signature.o =20 obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) +=3D public_key.o +obj-$(CONFIG_ASYMMETRIC_TPM2_KEY_RSA_SUBTYPE) +=3D tpm2_key_rsa.o =20 # # 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..22e7cc0217d3 --- /dev/null +++ b/crypto/asymmetric_keys/tpm2_key_rsa.c @@ -0,0 +1,680 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* TPM2 asymmetric public-key crypto subtype + * + * See Documentation/crypto/asymmetric-keys.rst + * + * Copyright (c) 2020 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef pr_fmt +#define pr_fmt(fmt) "tpm2_key_rsa: "fmt + +/* 4096 bits */ +#define TPM2_KEY_RSA_PUB_SIZE 512 +#define TPM2_KEY_RSA_PUB_ENC_SIZE (TPM2_KEY_RSA_PUB_SIZE + 16) +/* 4x 4096bits */ +#define TPM2_KEY_RSA_BUFFER_SIZE (TPM2_KEY_RSA_PUB_SIZE * 4) + +#define PKCS1_PAD_MIN_SIZE 11 + +struct tpm2_key_rsa { + struct tpm2_key key; + const u8 *pub; + int pub_len; +}; + +/* + * 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 =3D out_len - in_len - 3; + + if (in_len > out_len - PKCS1_PAD_MIN_SIZE) + return -EBADMSG; + + /* prefix */ + out[0] =3D 0; + out[1] =3D 1; + memset(&out[2], 0xff, prefix_len); + out[2 + prefix_len] =3D 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] !=3D 0 || in[1] !=3D 2) + return NULL; + + i =3D 2; + while (in[i] !=3D 0 && i < in_len) + i++; + + if (i =3D=3D in_len || i < (PKCS1_PAD_MIN_SIZE - 1)) + return NULL; + + *out_len =3D 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_al= go, + char *cipher) +{ + ssize_t ret; + + if (strcmp(encoding, "pkcs1") =3D=3D 0) { + if (!hash_algo) { + strcpy(cipher, "pkcs1pad(rsa)"); + return 0; + } + + ret =3D snprintf(cipher, CRYPTO_MAX_ALG_NAME, + "pkcs1pad(rsa,%s)", + hash_algo); + if (ret >=3D CRYPTO_MAX_ALG_NAME) + return -ENOPKG; + + return 0; + } + + if (strcmp(encoding, "raw") =3D=3D 0) { + strcpy(cipher, "rsa"); + return 0; + } + + return -ENOPKG; +} + +static int tpm2_key_rsa_extract_pub(struct tpm2_key_rsa *key_rsa) +{ + struct tpm2_key *key =3D &key_rsa->key; + struct tpm_buf buf; + off_t offset =3D 2; + u16 policy_len; + u32 attr; + u16 bits; + u16 type; + u16 len; + u16 alg; + u32 exp; + + buf.flags =3D TPM_BUF_TPM2B; + buf.length =3D key->pub_len; + buf.data =3D (void *)key->pub; + + if (get_unaligned_be16(key->pub) !=3D buf.length - 2) + return -EINVAL; + + type =3D tpm_buf_read_u16(&buf, &offset); + pr_debug("pub type: 0x%04x\n", type); + if (type !=3D TPM_ALG_RSA) + return -EINVAL; + + alg =3D tpm_buf_read_u16(&buf, &offset); + pr_debug("pub name alg: 0x%04x\n", alg); + attr =3D tpm_buf_read_u32(&buf, &offset); + pr_debug("pub attributes: 0x%08x\n", attr); + policy_len =3D tpm_buf_read_u16(&buf, &offset); + pr_debug("pub policy length: %u bytes\n", policy_len); + offset +=3D policy_len; + + alg =3D tpm_buf_read_u16(&buf, &offset); + pr_debug("pub symmetric: 0x%04x\n", alg); + if (alg !=3D TPM_ALG_NULL) + return -EINVAL; + + alg =3D tpm_buf_read_u16(&buf, &offset); + pr_debug("pub symmetric scheme: 0x%04x\n", alg); + if (alg !=3D TPM_ALG_NULL) + return -EINVAL; + + bits =3D tpm_buf_read_u16(&buf, &offset); + pr_debug("pub bits: %u\n", bits); + + exp =3D tpm_buf_read_u32(&buf, &offset); + pr_debug("pub exponent: 0x%08x\n", exp); + if (exp !=3D 0x00000000 && exp !=3D 0x00010001) + return -EINVAL; + + len =3D tpm_buf_read_u16(&buf, &offset); + pr_debug("pub modulus: %u bytes\n", len); + key_rsa->pub =3D key->pub + offset; + key_rsa->pub_len =3D len; + + return buf.flags & TPM_BUF_BOUNDARY_ERROR ? -EIO : 0; +} + +static int tpm2_key_rsa_encode(const struct tpm2_key_rsa *key, u8 *buf) +{ + int pub_len =3D key->pub_len; + const u8 *pub =3D key->pub; + u8 *start =3D &buf[4]; + u8 *work =3D &buf[4]; + u32 seq_len; + + work[0] =3D 0x02; /* INTEGER */ + work[1] =3D 0x82; /* u16 */ + work[2] =3D pub_len >> 8; + work[3] =3D pub_len & 0xff; + work =3D &work[4]; + memcpy(work, pub, pub_len); + work =3D &work[pub_len]; + work[0] =3D 0x02; /* INTEGER */ + work[1] =3D 3; /* < 128 */ + work[2] =3D 1; /* 65537 */ + work[3] =3D 0; + work[4] =3D 1; + work =3D &work[5]; + seq_len =3D work - start; + buf[0] =3D 0x30; /* SEQUENCE */ + buf[1] =3D 0x82; /* u16 */ + buf[2] =3D seq_len >> 8; + buf[3] =3D 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_rsa *key, + struct kernel_pkey_params *params, + const void *in, void *out) +{ + u8 enc_pub_key[TPM2_KEY_RSA_PUB_ENC_SIZE]; + char cipher[CRYPTO_MAX_ALG_NAME]; + struct scatterlist in_sg, out_sg; + struct akcipher_request *req; + struct crypto_akcipher *tfm; + struct crypto_wait cwait; + int rc; + + rc =3D tpm2_key_get_akcipher(params->encoding, params->hash_algo, cipher); + if (rc < 0) + return rc; + + tfm =3D crypto_alloc_akcipher(cipher, 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + rc =3D tpm2_key_rsa_encode(key, enc_pub_key); + if (rc < 0) + goto err_tfm; + + rc =3D crypto_akcipher_set_pub_key(tfm, enc_pub_key, rc); + if (rc < 0) + goto err_tfm; + + req =3D akcipher_request_alloc(tfm, GFP_KERNEL); + if (!req) { + rc =3D -ENOMEM; + goto err_tfm; + } + + sg_init_one(&in_sg, in, params->in_len); + sg_init_one(&out_sg, out, params->out_len); + akcipher_request_set_crypt(req, &in_sg, &out_sg, params->in_len, + params->out_len); + + crypto_init_wait(&cwait); + akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_req_done, &cwait); + + rc =3D crypto_akcipher_encrypt(req); + rc =3D crypto_wait_req(rc, &cwait); + if (!rc) + rc =3D req->dst_len; + + akcipher_request_free(req); + +err_tfm: + crypto_free_akcipher(tfm); + + return rc; +} + +static int __tpm2_key_rsa_decrypt(struct tpm_chip *chip, + struct tpm2_key_rsa *key, + struct kernel_pkey_params *params, + const void *in, int in_len, void *out) +{ + u32 key_handle =3D 0; + struct tpm_buf buf; + u16 decrypted_len; + u32 parent; + u8 *pos; + int ret; + + ret =3D tpm_try_get_ops(chip); + if (ret) + return ret; + + ret =3D tpm2_start_auth_session(chip); + if (ret) + goto err_ops; + + parent =3D key->key.parent; + + ret =3D tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD); + if (ret < 0) + goto err_auth; + + tpm_buf_append_name(chip, &buf, parent, NULL); + tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_CONTINUE_SESSION | + TPM2_SA_ENCRYPT, NULL, 0); + tpm_buf_append(&buf, key->key.blob, key->key.blob_len); + if (buf.flags & TPM_BUF_OVERFLOW) { + ret =3D -E2BIG; + goto err_buf; + } + tpm_buf_fill_hmac_session(chip, &buf); + ret =3D tpm_transmit_cmd(chip, &buf, 4, "RSA key loading"); + ret =3D tpm_buf_check_hmac_response(chip, &buf, ret); + if (ret) { + ret =3D -EIO; + goto err_buf; + } + key_handle =3D 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 =3D tpm_transmit_cmd(chip, &buf, 4, "RSA key decrypting"); + ret =3D tpm_buf_check_hmac_response(chip, &buf, ret); + if (ret) { + ret =3D -EIO; + goto err_blob; + } + + pos =3D buf.data + TPM_HEADER_SIZE + 4; + decrypted_len =3D be16_to_cpup((__be16 *)pos); + pos +=3D 2; + + if (params->out_len < decrypted_len) { + ret =3D -EMSGSIZE; + goto err_blob; + } + + memcpy(out, pos, decrypted_len); + ret =3D 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_rsa= *key, + struct kernel_pkey_params *params, + const void *in, void *out) +{ + const u8 *ptr; + int out_len; + u8 *work; + int ret; + + work =3D kzalloc(params->out_len, GFP_KERNEL); + if (!work) + return -ENOMEM; + + ret =3D __tpm2_key_rsa_decrypt(chip, key, params, in, params->in_len, + work); + if (ret < 0) + goto err; + + ptr =3D tpm2_unpad_pkcs1(work, ret, &out_len); + if (!ptr) { + ret =3D -EINVAL; + goto err; + } + + if (out_len > params->out_len) { + ret =3D -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 t= he + * only difference between encryption and decryption is where the padding = goes. + * Since own padding can be used, TPM2_RSA_Decrypt can be repurposed to do + * encryption. + */ +static int tpm2_key_rsa_sign(struct tpm_chip *chip, struct tpm2_key_rsa *k= ey, + struct kernel_pkey_params *params, + const void *in, void *out) +{ + const struct rsa_asn1_template *asn1; + u32 in_len =3D params->in_len; + void *asn1_wrapped =3D NULL; + int pub_len =3D key->pub_len; + u8 *padded; + int ret; + + if (strcmp(params->encoding, "pkcs1") !=3D 0) { + ret =3D -ENOPKG; + goto err; + } + + if (params->hash_algo) { + asn1 =3D rsa_lookup_asn1(params->hash_algo); + if (!asn1) { + ret =3D -ENOPKG; + goto err; + } + + /* Request enough space for the ASN.1 template + input hash */ + asn1_wrapped =3D kzalloc(in_len + asn1->size, GFP_KERNEL); + if (!asn1_wrapped) { + ret =3D -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 =3D asn1_wrapped; + in_len +=3D asn1->size; + } + + /* with padding: */ + padded =3D kmalloc(pub_len, GFP_KERNEL); + tpm2_pad_pkcs1(in, in_len, padded, pub_len); + ret =3D __tpm2_key_rsa_decrypt(chip, key, params, padded, pub_len, out); + kfree(padded); + +err: + kfree(asn1_wrapped); + return ret; +} + +static void tpm2_key_rsa_describe(const struct key *asymmetric_key, + struct seq_file *m) +{ + struct tpm2_key_rsa *key =3D 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 =3D payload0; + + if (!key) + return; + + tpm2_key_destroy(key); + kfree(key); +} + +static int tpm2_key_rsa_eds_op(struct kernel_pkey_params *params, + const void *in, void *out) +{ + struct tpm2_key_rsa *key =3D params->key->payload.data[asym_crypto]; + struct tpm_chip *chip =3D tpm_default_chip(); + + if (!chip) + return -ENODEV; + + switch (params->op) { + case kernel_pkey_encrypt: + return tpm2_key_rsa_encrypt(key, params, in, out); + case kernel_pkey_decrypt: + return tpm2_key_rsa_decrypt(chip, key, params, in, out); + case kernel_pkey_sign: + return tpm2_key_rsa_sign(chip, key, params, in, out); + default: + return -EOPNOTSUPP; + } +} + +static int tpm2_key_rsa_verify(const struct key *key, + const struct public_key_signature *sig) +{ + const struct tpm2_key_rsa *tpm2_key =3D key->payload.data[asym_crypto]; + u8 enc_pub_key[TPM2_KEY_RSA_PUB_ENC_SIZE]; + char alg_name[CRYPTO_MAX_ALG_NAME]; + struct akcipher_request *req; + struct scatterlist src_sg[2]; + struct crypto_akcipher *tfm; + struct crypto_wait cwait; + int rc; + + if (!sig->digest) + return -ENOPKG; + + rc =3D tpm2_key_get_akcipher(sig->encoding, sig->hash_algo, alg_name); + if (rc < 0) + return rc; + + tfm =3D crypto_alloc_akcipher(alg_name, 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + rc =3D tpm2_key_rsa_encode(tpm2_key, enc_pub_key); + if (rc < 0) + goto err_tfm; + + rc =3D crypto_akcipher_set_pub_key(tfm, enc_pub_key, rc); + if (rc < 0) + goto err_tfm; + + rc =3D -ENOMEM; + req =3D akcipher_request_alloc(tfm, GFP_KERNEL); + if (!req) + goto err_tfm; + + sg_init_table(src_sg, 2); + sg_set_buf(&src_sg[0], sig->s, sig->s_size); + sg_set_buf(&src_sg[1], sig->digest, sig->digest_size); + akcipher_request_set_crypt(req, src_sg, NULL, sig->s_size, + sig->digest_size); + crypto_init_wait(&cwait); + akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_req_done, &cwait); + rc =3D crypto_wait_req(crypto_akcipher_verify(req), &cwait); + + akcipher_request_free(req); + +err_tfm: + crypto_free_akcipher(tfm); + return rc; +} + +static int tpm2_key_rsa_query(const struct kernel_pkey_params *params, + struct kernel_pkey_query *info) +{ + struct tpm2_key_rsa *tk =3D params->key->payload.data[asym_crypto]; + u8 enc_pub_key[TPM2_KEY_RSA_PUB_ENC_SIZE]; + char alg_name[CRYPTO_MAX_ALG_NAME]; + struct crypto_akcipher *tfm; + unsigned int len; + int ret; + + ret =3D tpm2_key_get_akcipher(params->encoding, params->hash_algo, alg_na= me); + if (ret < 0) + return ret; + + tfm =3D crypto_alloc_akcipher(alg_name, 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + ret =3D tpm2_key_rsa_encode(tk, enc_pub_key); + if (ret < 0) + goto err_tfm; + + ret =3D crypto_akcipher_set_pub_key(tfm, enc_pub_key, ret); + if (ret < 0) + goto err_tfm; + + len =3D crypto_akcipher_maxsize(tfm); + + info->key_size =3D tk->pub_len * 8; + info->max_data_size =3D tk->pub_len; + info->max_sig_size =3D len; + info->max_enc_size =3D len; + info->max_dec_size =3D tk->pub_len; + + info->supported_ops =3D KEYCTL_SUPPORTS_ENCRYPT | + KEYCTL_SUPPORTS_DECRYPT | + KEYCTL_SUPPORTS_VERIFY | + KEYCTL_SUPPORTS_SIGN; + +err_tfm: + crypto_free_akcipher(tfm); + return ret; +} + +/* + * Asymmetric TPM2 RSA key. Signs and decrypts with TPM. + */ +struct asymmetric_key_subtype tpm2_key_rsa_subtype =3D { + .owner =3D THIS_MODULE, + .name =3D "tpm2_key_rsa", + .name_len =3D sizeof("tpm2_key_rsa") - 1, + .describe =3D tpm2_key_rsa_describe, + .destroy =3D tpm2_key_rsa_destroy, + .query =3D tpm2_key_rsa_query, + .eds_op =3D tpm2_key_rsa_eds_op, + .verify_signature =3D tpm2_key_rsa_verify, +}; +EXPORT_SYMBOL_GPL(tpm2_key_rsa_subtype); + +/* + * Attempt to parse a data blob for a key as a TPM private key blob. + */ +static int tpm2_key_preparse(struct key_preparsed_payload *prep) +{ + struct tpm2_key_rsa *key; + int ret; + + key =3D kzalloc(sizeof(*key), GFP_KERNEL); + if (!key) + return -ENOMEM; + + if (prep->datalen > TPM2_KEY_RSA_BUFFER_SIZE) { + kfree(key); + return -EMSGSIZE; + } + + ret =3D tpm2_key_decode(prep->data, prep->datalen, &key->key, + TPM2_KEY_RSA_BUFFER_SIZE); + if (ret) { + kfree(key); + return ret; + } + + if (key->key.oid !=3D OID_TPMLoadableKey) { + tpm2_key_destroy(&key->key); + kfree(key); + return -EINVAL; + } + + ret =3D tpm2_key_rsa_extract_pub(key); + if (ret < 0) { + tpm2_key_destroy(&key->key); + kfree(key); + return ret; + } + + prep->payload.data[asym_subtype] =3D &tpm2_key_rsa_subtype; + prep->payload.data[asym_key_ids] =3D NULL; + prep->payload.data[asym_crypto] =3D key; + prep->payload.data[asym_auth] =3D NULL; + prep->quotalen =3D 100; + return 0; +} + +static struct asymmetric_key_parser tpm2_key_rsa_parser =3D { + .owner =3D THIS_MODULE, + .name =3D "tpm2_key_rsa_parser", + .parse =3D tpm2_key_preparse, +}; + +static int __init tpm2_key_rsa_init(void) +{ + return register_asymmetric_key_parser(&tpm2_key_rsa_parser); +} + +static void __exit tpm2_key_rsa_exit(void) +{ + unregister_asymmetric_key_parser(&tpm2_key_rsa_parser); +} + +module_init(tpm2_key_rsa_init); +module_exit(tpm2_key_rsa_exit); + +MODULE_DESCRIPTION("Asymmetric TPM2 RSA key"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/tpm.h b/include/linux/tpm.h index c17e4efbb2e5..040be2c75868 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 =3D 0x0000, + TPM_ALG_RSA =3D 0x0001, TPM_ALG_SHA1 =3D 0x0004, TPM_ALG_AES =3D 0x0006, TPM_ALG_KEYEDHASH =3D 0x0008, @@ -271,6 +272,7 @@ enum tpm2_command_codes { TPM2_CC_NV_READ =3D 0x014E, TPM2_CC_CREATE =3D 0x0153, TPM2_CC_LOAD =3D 0x0157, + TPM2_CC_RSA_DECRYPT =3D 0x0159, TPM2_CC_SEQUENCE_UPDATE =3D 0x015C, TPM2_CC_UNSEAL =3D 0x015E, TPM2_CC_CONTEXT_LOAD =3D 0x0161, --=20 2.45.1