From mboxrd@z Thu Jan 1 00:00:00 1970 Content-Type: multipart/mixed; boundary="===============6792755459101233058==" MIME-Version: 1.0 From: Jarkko Sakkinen To: kbuild-all@lists.01.org Subject: Re: [PATCH] security: keys: trusted: use ASN.1 TPM2 key format for the blobs Date: Mon, 01 Mar 2021 23:32:00 +0200 Message-ID: In-Reply-To: List-Id: --===============6792755459101233058== Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable On Mon, Mar 01, 2021 at 08:14:56AM -0800, James Bottomley wrote: > Modify the TPM2 key format blob output to export and import in the > ASN.1 form for TPM2 sealed object keys. For compatibility with prior > trusted keys, the importer will also accept two TPM2B quantities > representing the public and private parts of the key. However, the > export via keyctl pipe will only output the ASN.1 format. > = > The benefit of the ASN.1 format is that it's a standard and thus the > exported key can be used by userspace tools (openssl_tpm2_engine, > openconnect and tpm2-tss-engine). The format includes policy > specifications, thus it gets us out of having to construct policy > handles in userspace and the format includes the parent meaning you > don't have to keep passing it in each time. > = > This patch only implements basic handling for the ASN.1 format, so > keys with passwords but no policy. > = > Signed-off-by: James Bottomley > Tested-by: Jarkko Sakkinen > Signed-off-by: Jarkko Sakkinen Thanks. /Jarkko > --- > .../security/keys/trusted-encrypted.rst | 58 +++++ > include/keys/trusted-type.h | 1 + > security/keys/Kconfig | 3 + > security/keys/trusted-keys/Makefile | 3 + > security/keys/trusted-keys/tpm2key.asn1 | 11 + > security/keys/trusted-keys/trusted_tpm1.c | 2 +- > security/keys/trusted-keys/trusted_tpm2.c | 210 +++++++++++++++++- > 7 files changed, 280 insertions(+), 8 deletions(-) > create mode 100644 security/keys/trusted-keys/tpm2key.asn1 > = > diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Document= ation/security/keys/trusted-encrypted.rst > index 1da879a68640..549aa1308949 100644 > --- a/Documentation/security/keys/trusted-encrypted.rst > +++ b/Documentation/security/keys/trusted-encrypted.rst > @@ -207,3 +207,61 @@ about the usage can be found in the file > Another new format 'enc32' has been defined in order to support encrypte= d keys > with payload size of 32 bytes. This will initially be used for nvdimm se= curity > but may expand to other usages that require 32 bytes payload. > + > + > +TPM 2.0 ASN.1 Key Format > +------------------------ > + > +The TPM 2.0 ASN.1 key format is designed to be easily recognisable, > +even in binary form (fixing a problem we had with the TPM 1.2 ASN.1 > +format) and to be extensible for additions like importable keys and > +policy:: > + > + TPMKey ::=3D SEQUENCE { > + type OBJECT IDENTIFIER > + emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL > + parent INTEGER > + pubkey OCTET STRING > + privkey OCTET STRING > + } > + > +type is what distinguishes the key even in binary form since the OID > +is provided by the TCG to be unique and thus forms a recognizable > +binary pattern at offset 3 in the key. The OIDs currently made > +available are:: > + > + 2.23.133.10.1.3 TPM Loadable key. This is an asymmetric key (Usually > + RSA2048 or Elliptic Curve) which can be imported by a > + TPM2_Load() operation. > + > + 2.23.133.10.1.4 TPM Importable Key. This is an asymmetric key (Usua= lly > + RSA2048 or Elliptic Curve) which can be imported by a > + TPM2_Import() operation. > + > + 2.23.133.10.1.5 TPM Sealed Data. This is a set of data (up to 128 > + bytes) which is sealed by the TPM. It usually > + represents a symmetric key and must be unsealed befo= re > + use. > + > +The trusted key code only uses the TPM Sealed Data OID. > + > +emptyAuth is true if the key has well known authorization "". If it > +is false or not present, the key requires an explicit authorization > +phrase. This is used by most user space consumers to decide whether > +to prompt for a password. > + > +parent represents the parent key handle, either in the 0x81 MSO space, > +like 0x81000001 for the RSA primary storage key. Userspace programmes > +also support specifying the primary handle in the 0x40 MSO space. If > +this happens the Elliptic Curve variant of the primary key using the > +TCG defined template will be generated on the fly into a volatile > +object and used as the parent. The current kernel code only supports > +the 0x81 MSO form. > + > +pubkey is the binary representation of TPM2B_PRIVATE excluding the > +initial TPM2B header, which can be reconstructed from the ASN.1 octet > +string length. > + > +privkey is the binary representation of TPM2B_PUBLIC excluding the > +initial TPM2B header which can be reconstructed from the ASN.1 octed > +string length. > diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h > index b2ed3481c6a0..b2d87ad21714 100644 > --- a/include/keys/trusted-type.h > +++ b/include/keys/trusted-type.h > @@ -22,6 +22,7 @@ struct trusted_key_payload { > unsigned int key_len; > unsigned int blob_len; > unsigned char migratable; > + unsigned char old_format; > unsigned char key[MAX_KEY_SIZE + 1]; > unsigned char blob[MAX_BLOB_SIZE]; > }; > diff --git a/security/keys/Kconfig b/security/keys/Kconfig > index 83bc23409164..f0912692469b 100644 > --- a/security/keys/Kconfig > +++ b/security/keys/Kconfig > @@ -75,6 +75,9 @@ config TRUSTED_KEYS > select CRYPTO_HMAC > select CRYPTO_SHA1 > select CRYPTO_HASH_INFO > + select ASN1_ENCODER > + select OID_REGISTRY > + select ASN1 > help > This option provides support for creating, sealing, and unsealing > keys in the kernel. Trusted keys are random number symmetric keys, > diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-= keys/Makefile > index 7b73cebbb378..1e17ab7bf3c5 100644 > --- a/security/keys/trusted-keys/Makefile > +++ b/security/keys/trusted-keys/Makefile > @@ -5,4 +5,7 @@ > = > obj-$(CONFIG_TRUSTED_KEYS) +=3D trusted.o > trusted-y +=3D trusted_tpm1.o > + > +$(obj)/trusted_tpm2.o: $(obj)/tpm2key.asn1.h > trusted-y +=3D trusted_tpm2.o > +trusted-y +=3D tpm2key.asn1.o > diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trus= ted-keys/tpm2key.asn1 > new file mode 100644 > index 000000000000..3f6a9d01d1e5 > --- /dev/null > +++ b/security/keys/trusted-keys/tpm2key.asn1 > @@ -0,0 +1,11 @@ > +--- > +--- ASN.1 for for TPM 2.0 keys > +--- > + > +TPMKey ::=3D SEQUENCE { > + type OBJECT IDENTIFIER ({tpm2_key_type}), > + emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL, > + parent INTEGER ({tpm2_key_parent}), > + pubkey OCTET STRING ({tpm2_key_pub}), > + privkey OCTET STRING ({tpm2_key_priv}) > + } > diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/tr= usted-keys/trusted_tpm1.c > index 1e13c9f7ea8c..713b79576840 100644 > --- a/security/keys/trusted-keys/trusted_tpm1.c > +++ b/security/keys/trusted-keys/trusted_tpm1.c > @@ -1021,7 +1021,7 @@ static int trusted_instantiate(struct key *key, > goto out; > } > = > - if (!options->keyhandle) { > + if (!options->keyhandle && !tpm2) { > ret =3D -EINVAL; > goto out; > } > diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/tr= usted-keys/trusted_tpm2.c > index ac361aa7f3f1..68249db98a4c 100644 > --- a/security/keys/trusted-keys/trusted_tpm2.c > +++ b/security/keys/trusted-keys/trusted_tpm2.c > @@ -4,6 +4,8 @@ > * Copyright (C) 2014 Intel Corporation > */ > = > +#include > +#include > #include > #include > #include > @@ -12,6 +14,10 @@ > #include > #include > = > +#include > + > +#include "tpm2key.asn1.h" > + > static struct tpm2_hash tpm2_hash_map[] =3D { > {HASH_ALGO_SHA1, TPM_ALG_SHA1}, > {HASH_ALGO_SHA256, TPM_ALG_SHA256}, > @@ -20,6 +26,165 @@ static struct tpm2_hash tpm2_hash_map[] =3D { > {HASH_ALGO_SM3_256, TPM_ALG_SM3_256}, > }; > = > +static u32 tpm2key_oid[] =3D { 2, 23, 133, 10, 1, 5 }; > + > +static int tpm2_key_encode(struct trusted_key_payload *payload, > + struct trusted_key_options *options, > + u8 *src, u32 len) > +{ > + const int SCRATCH_SIZE =3D PAGE_SIZE; > + u8 *scratch =3D kmalloc(SCRATCH_SIZE, GFP_KERNEL); > + u8 *work =3D scratch, *work1; > + u8 *end_work =3D scratch + SCRATCH_SIZE; > + u8 *priv, *pub; > + u16 priv_len, pub_len; > + > + priv_len =3D get_unaligned_be16(src) + 2; > + priv =3D src; > + > + src +=3D priv_len; > + > + pub_len =3D get_unaligned_be16(src) + 2; > + pub =3D src; > + > + if (!scratch) > + return -ENOMEM; > + > + work =3D asn1_encode_oid(work, end_work, tpm2key_oid, > + asn1_oid_len(tpm2key_oid)); > + > + if (options->blobauth_len =3D=3D 0) { > + unsigned char bool[3], *w =3D bool; > + /* tag 0 is emptyAuth */ > + w =3D asn1_encode_boolean(w, w + sizeof(bool), true); > + if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) > + return PTR_ERR(w); > + work =3D asn1_encode_tag(work, end_work, 0, bool, w - bool); > + } > + > + /* > + * Assume both octet strings will encode to a 2 byte definite length > + * > + * Note: For a well behaved TPM, this warning should never > + * trigger, so if it does there's something nefarious going on > + */ > + if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE, > + "BUG: scratch buffer is too small")) > + return -EINVAL; > + > + work =3D asn1_encode_integer(work, end_work, options->keyhandle); > + work =3D asn1_encode_octet_string(work, end_work, pub, pub_len); > + work =3D asn1_encode_octet_string(work, end_work, priv, priv_len); > + > + work1 =3D payload->blob; > + work1 =3D asn1_encode_sequence(work1, work1 + sizeof(payload->blob), > + scratch, work - scratch); > + if (WARN(IS_ERR(work1), "BUG: ASN.1 encoder failed")) > + return PTR_ERR(work1); > + > + return work1 - payload->blob; > +} > + > +struct tpm2_key_context { > + u32 parent; > + const u8 *pub; > + u32 pub_len; > + const u8 *priv; > + u32 priv_len; > +}; > + > +static int tpm2_key_decode(struct trusted_key_payload *payload, > + struct trusted_key_options *options, > + u8 **buf) > +{ > + int ret; > + struct tpm2_key_context ctx; > + u8 *blob; > + > + memset(&ctx, 0, sizeof(ctx)); > + > + ret =3D asn1_ber_decoder(&tpm2key_decoder, &ctx, payload->blob, > + payload->blob_len); > + if (ret < 0) > + return ret; > + > + if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE) > + return -EINVAL; > + > + blob =3D kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL); > + if (!blob) > + return -ENOMEM; > + > + *buf =3D blob; > + options->keyhandle =3D ctx.parent; > + > + memcpy(blob, ctx.priv, ctx.priv_len); > + blob +=3D ctx.priv_len; > + > + memcpy(blob, ctx.pub, ctx.pub_len); > + > + return 0; > +} > + > +int tpm2_key_parent(void *context, size_t hdrlen, > + unsigned char tag, > + const void *value, size_t vlen) > +{ > + struct tpm2_key_context *ctx =3D context; > + const u8 *v =3D value; > + int i; > + > + ctx->parent =3D 0; > + for (i =3D 0; i < vlen; i++) { > + ctx->parent <<=3D 8; > + ctx->parent |=3D v[i]; > + } > + > + return 0; > +} > + > +int tpm2_key_type(void *context, size_t hdrlen, > + unsigned char tag, > + const void *value, size_t vlen) > +{ > + enum OID oid =3D look_up_OID(value, vlen); > + > + if (oid !=3D OID_TPMSealedData) { > + char buffer[50]; > + > + sprint_oid(value, vlen, buffer, sizeof(buffer)); > + pr_debug("OID is \"%s\" which is not TPMSealedData\n", > + buffer); > + return -EINVAL; > + } > + > + return 0; > +} > + > +int tpm2_key_pub(void *context, size_t hdrlen, > + unsigned char tag, > + const void *value, size_t vlen) > +{ > + struct tpm2_key_context *ctx =3D context; > + > + ctx->pub =3D value; > + ctx->pub_len =3D vlen; > + > + return 0; > +} > + > +int tpm2_key_priv(void *context, size_t hdrlen, > + unsigned char tag, > + const void *value, size_t vlen) > +{ > + struct tpm2_key_context *ctx =3D context; > + > + ctx->priv =3D value; > + ctx->priv_len =3D vlen; > + > + return 0; > +} > + > /** > * tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer. > * > @@ -63,7 +228,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, > struct trusted_key_payload *payload, > struct trusted_key_options *options) > { > - unsigned int blob_len; > + int blob_len =3D 0; > struct tpm_buf buf; > u32 hash; > int i; > @@ -79,6 +244,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip, > if (i =3D=3D ARRAY_SIZE(tpm2_hash_map)) > return -EINVAL; > = > + if (!options->keyhandle) > + return -EINVAL; > + > rc =3D tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE); > if (rc) > return rc; > @@ -152,8 +320,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip, > goto out; > } > = > - memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len); > - payload->blob_len =3D blob_len; > + blob_len =3D tpm2_key_encode(payload, options, > + &buf.data[TPM_HEADER_SIZE + 4], > + blob_len); > = > out: > tpm_buf_destroy(&buf); > @@ -164,6 +333,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip, > else > rc =3D -EPERM; > } > + if (blob_len < 0) > + return blob_len; > + > + payload->blob_len =3D blob_len; > = > tpm_put_ops(chip); > return rc; > @@ -191,13 +364,34 @@ static int tpm2_load_cmd(struct tpm_chip *chip, > unsigned int private_len; > unsigned int public_len; > unsigned int blob_len; > + u8 *blob; > int rc; > = > - private_len =3D be16_to_cpup((__be16 *) &payload->blob[0]); > - if (private_len > (payload->blob_len - 2)) > + rc =3D tpm2_key_decode(payload, options, &blob); > + if (rc) { > + /* old form */ > + blob =3D payload->blob; > + payload->old_format =3D 1; > + } > + > + /* new format carries keyhandle but old format doesn't */ > + if (!options->keyhandle) > + return -EINVAL; > + > + /* must be big enough for at least the two be16 size counts */ > + if (payload->blob_len < 4) > + return -EINVAL; > + > + private_len =3D get_unaligned_be16(blob); > + > + /* must be big enough for following public_len */ > + if (private_len + 2 + 2 > (payload->blob_len)) > + return -E2BIG; > + > + public_len =3D get_unaligned_be16(blob + 2 + private_len); > + if (private_len + 2 + public_len + 2 > payload->blob_len) > return -E2BIG; > = > - public_len =3D be16_to_cpup((__be16 *) &payload->blob[2 + private_len]); > blob_len =3D private_len + public_len + 4; > if (blob_len > payload->blob_len) > return -E2BIG; > @@ -213,7 +407,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip, > options->keyauth /* hmac */, > TPM_DIGEST_SIZE); > = > - tpm_buf_append(&buf, payload->blob, blob_len); > + tpm_buf_append(&buf, blob, blob_len); > = > if (buf.flags & TPM_BUF_OVERFLOW) { > rc =3D -E2BIG; > @@ -226,6 +420,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip, > (__be32 *) &buf.data[TPM_HEADER_SIZE]); > = > out: > + if (blob !=3D payload->blob) > + kfree(blob); > tpm_buf_destroy(&buf); > = > if (rc > 0) > -- = > 2.26.2 > = > = >=20 --===============6792755459101233058==--