162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2004 IBM Corporation 462306a36Sopenharmony_ci * Copyright (C) 2014 Intel Corporation 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/asn1_encoder.h> 862306a36Sopenharmony_ci#include <linux/oid_registry.h> 962306a36Sopenharmony_ci#include <linux/string.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/tpm.h> 1262306a36Sopenharmony_ci#include <linux/tpm_command.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <keys/trusted-type.h> 1562306a36Sopenharmony_ci#include <keys/trusted_tpm.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/unaligned.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "tpm2key.asn1.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic struct tpm2_hash tpm2_hash_map[] = { 2262306a36Sopenharmony_ci {HASH_ALGO_SHA1, TPM_ALG_SHA1}, 2362306a36Sopenharmony_ci {HASH_ALGO_SHA256, TPM_ALG_SHA256}, 2462306a36Sopenharmony_ci {HASH_ALGO_SHA384, TPM_ALG_SHA384}, 2562306a36Sopenharmony_ci {HASH_ALGO_SHA512, TPM_ALG_SHA512}, 2662306a36Sopenharmony_ci {HASH_ALGO_SM3_256, TPM_ALG_SM3_256}, 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic u32 tpm2key_oid[] = { 2, 23, 133, 10, 1, 5 }; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic int tpm2_key_encode(struct trusted_key_payload *payload, 3262306a36Sopenharmony_ci struct trusted_key_options *options, 3362306a36Sopenharmony_ci u8 *src, u32 len) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci const int SCRATCH_SIZE = PAGE_SIZE; 3662306a36Sopenharmony_ci u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL); 3762306a36Sopenharmony_ci u8 *work = scratch, *work1; 3862306a36Sopenharmony_ci u8 *end_work = scratch + SCRATCH_SIZE; 3962306a36Sopenharmony_ci u8 *priv, *pub; 4062306a36Sopenharmony_ci u16 priv_len, pub_len; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci priv_len = get_unaligned_be16(src) + 2; 4362306a36Sopenharmony_ci priv = src; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci src += priv_len; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci pub_len = get_unaligned_be16(src) + 2; 4862306a36Sopenharmony_ci pub = src; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (!scratch) 5162306a36Sopenharmony_ci return -ENOMEM; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci work = asn1_encode_oid(work, end_work, tpm2key_oid, 5462306a36Sopenharmony_ci asn1_oid_len(tpm2key_oid)); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (options->blobauth_len == 0) { 5762306a36Sopenharmony_ci unsigned char bool[3], *w = bool; 5862306a36Sopenharmony_ci /* tag 0 is emptyAuth */ 5962306a36Sopenharmony_ci w = asn1_encode_boolean(w, w + sizeof(bool), true); 6062306a36Sopenharmony_ci if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) 6162306a36Sopenharmony_ci return PTR_ERR(w); 6262306a36Sopenharmony_ci work = asn1_encode_tag(work, end_work, 0, bool, w - bool); 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* 6662306a36Sopenharmony_ci * Assume both octet strings will encode to a 2 byte definite length 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * Note: For a well behaved TPM, this warning should never 6962306a36Sopenharmony_ci * trigger, so if it does there's something nefarious going on 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE, 7262306a36Sopenharmony_ci "BUG: scratch buffer is too small")) 7362306a36Sopenharmony_ci return -EINVAL; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci work = asn1_encode_integer(work, end_work, options->keyhandle); 7662306a36Sopenharmony_ci work = asn1_encode_octet_string(work, end_work, pub, pub_len); 7762306a36Sopenharmony_ci work = asn1_encode_octet_string(work, end_work, priv, priv_len); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci work1 = payload->blob; 8062306a36Sopenharmony_ci work1 = asn1_encode_sequence(work1, work1 + sizeof(payload->blob), 8162306a36Sopenharmony_ci scratch, work - scratch); 8262306a36Sopenharmony_ci if (WARN(IS_ERR(work1), "BUG: ASN.1 encoder failed")) 8362306a36Sopenharmony_ci return PTR_ERR(work1); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return work1 - payload->blob; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistruct tpm2_key_context { 8962306a36Sopenharmony_ci u32 parent; 9062306a36Sopenharmony_ci const u8 *pub; 9162306a36Sopenharmony_ci u32 pub_len; 9262306a36Sopenharmony_ci const u8 *priv; 9362306a36Sopenharmony_ci u32 priv_len; 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int tpm2_key_decode(struct trusted_key_payload *payload, 9762306a36Sopenharmony_ci struct trusted_key_options *options, 9862306a36Sopenharmony_ci u8 **buf) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci int ret; 10162306a36Sopenharmony_ci struct tpm2_key_context ctx; 10262306a36Sopenharmony_ci u8 *blob; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci memset(&ctx, 0, sizeof(ctx)); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci ret = asn1_ber_decoder(&tpm2key_decoder, &ctx, payload->blob, 10762306a36Sopenharmony_ci payload->blob_len); 10862306a36Sopenharmony_ci if (ret < 0) 10962306a36Sopenharmony_ci return ret; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE) 11262306a36Sopenharmony_ci return -EINVAL; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci blob = kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL); 11562306a36Sopenharmony_ci if (!blob) 11662306a36Sopenharmony_ci return -ENOMEM; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci *buf = blob; 11962306a36Sopenharmony_ci options->keyhandle = ctx.parent; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci memcpy(blob, ctx.priv, ctx.priv_len); 12262306a36Sopenharmony_ci blob += ctx.priv_len; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci memcpy(blob, ctx.pub, ctx.pub_len); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ciint tpm2_key_parent(void *context, size_t hdrlen, 13062306a36Sopenharmony_ci unsigned char tag, 13162306a36Sopenharmony_ci const void *value, size_t vlen) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct tpm2_key_context *ctx = context; 13462306a36Sopenharmony_ci const u8 *v = value; 13562306a36Sopenharmony_ci int i; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci ctx->parent = 0; 13862306a36Sopenharmony_ci for (i = 0; i < vlen; i++) { 13962306a36Sopenharmony_ci ctx->parent <<= 8; 14062306a36Sopenharmony_ci ctx->parent |= v[i]; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ciint tpm2_key_type(void *context, size_t hdrlen, 14762306a36Sopenharmony_ci unsigned char tag, 14862306a36Sopenharmony_ci const void *value, size_t vlen) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci enum OID oid = look_up_OID(value, vlen); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (oid != OID_TPMSealedData) { 15362306a36Sopenharmony_ci char buffer[50]; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci sprint_oid(value, vlen, buffer, sizeof(buffer)); 15662306a36Sopenharmony_ci pr_debug("OID is \"%s\" which is not TPMSealedData\n", 15762306a36Sopenharmony_ci buffer); 15862306a36Sopenharmony_ci return -EINVAL; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return 0; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ciint tpm2_key_pub(void *context, size_t hdrlen, 16562306a36Sopenharmony_ci unsigned char tag, 16662306a36Sopenharmony_ci const void *value, size_t vlen) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct tpm2_key_context *ctx = context; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci ctx->pub = value; 17162306a36Sopenharmony_ci ctx->pub_len = vlen; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return 0; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ciint tpm2_key_priv(void *context, size_t hdrlen, 17762306a36Sopenharmony_ci unsigned char tag, 17862306a36Sopenharmony_ci const void *value, size_t vlen) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct tpm2_key_context *ctx = context; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci ctx->priv = value; 18362306a36Sopenharmony_ci ctx->priv_len = vlen; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/** 18962306a36Sopenharmony_ci * tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer. 19062306a36Sopenharmony_ci * 19162306a36Sopenharmony_ci * @buf: an allocated tpm_buf instance 19262306a36Sopenharmony_ci * @session_handle: session handle 19362306a36Sopenharmony_ci * @nonce: the session nonce, may be NULL if not used 19462306a36Sopenharmony_ci * @nonce_len: the session nonce length, may be 0 if not used 19562306a36Sopenharmony_ci * @attributes: the session attributes 19662306a36Sopenharmony_ci * @hmac: the session HMAC or password, may be NULL if not used 19762306a36Sopenharmony_ci * @hmac_len: the session HMAC or password length, maybe 0 if not used 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_cistatic void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle, 20062306a36Sopenharmony_ci const u8 *nonce, u16 nonce_len, 20162306a36Sopenharmony_ci u8 attributes, 20262306a36Sopenharmony_ci const u8 *hmac, u16 hmac_len) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci tpm_buf_append_u32(buf, 9 + nonce_len + hmac_len); 20562306a36Sopenharmony_ci tpm_buf_append_u32(buf, session_handle); 20662306a36Sopenharmony_ci tpm_buf_append_u16(buf, nonce_len); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (nonce && nonce_len) 20962306a36Sopenharmony_ci tpm_buf_append(buf, nonce, nonce_len); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci tpm_buf_append_u8(buf, attributes); 21262306a36Sopenharmony_ci tpm_buf_append_u16(buf, hmac_len); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (hmac && hmac_len) 21562306a36Sopenharmony_ci tpm_buf_append(buf, hmac, hmac_len); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/** 21962306a36Sopenharmony_ci * tpm2_seal_trusted() - seal the payload of a trusted key 22062306a36Sopenharmony_ci * 22162306a36Sopenharmony_ci * @chip: TPM chip to use 22262306a36Sopenharmony_ci * @payload: the key data in clear and encrypted form 22362306a36Sopenharmony_ci * @options: authentication values and other options 22462306a36Sopenharmony_ci * 22562306a36Sopenharmony_ci * Return: < 0 on error and 0 on success. 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_ciint tpm2_seal_trusted(struct tpm_chip *chip, 22862306a36Sopenharmony_ci struct trusted_key_payload *payload, 22962306a36Sopenharmony_ci struct trusted_key_options *options) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci int blob_len = 0; 23262306a36Sopenharmony_ci struct tpm_buf buf; 23362306a36Sopenharmony_ci u32 hash; 23462306a36Sopenharmony_ci u32 flags; 23562306a36Sopenharmony_ci int i; 23662306a36Sopenharmony_ci int rc; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) { 23962306a36Sopenharmony_ci if (options->hash == tpm2_hash_map[i].crypto_id) { 24062306a36Sopenharmony_ci hash = tpm2_hash_map[i].tpm_id; 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (i == ARRAY_SIZE(tpm2_hash_map)) 24662306a36Sopenharmony_ci return -EINVAL; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (!options->keyhandle) 24962306a36Sopenharmony_ci return -EINVAL; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci rc = tpm_try_get_ops(chip); 25262306a36Sopenharmony_ci if (rc) 25362306a36Sopenharmony_ci return rc; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE); 25662306a36Sopenharmony_ci if (rc) { 25762306a36Sopenharmony_ci tpm_put_ops(chip); 25862306a36Sopenharmony_ci return rc; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci tpm_buf_append_u32(&buf, options->keyhandle); 26262306a36Sopenharmony_ci tpm2_buf_append_auth(&buf, TPM2_RS_PW, 26362306a36Sopenharmony_ci NULL /* nonce */, 0, 26462306a36Sopenharmony_ci 0 /* session_attributes */, 26562306a36Sopenharmony_ci options->keyauth /* hmac */, 26662306a36Sopenharmony_ci TPM_DIGEST_SIZE); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* sensitive */ 26962306a36Sopenharmony_ci tpm_buf_append_u16(&buf, 4 + options->blobauth_len + payload->key_len); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci tpm_buf_append_u16(&buf, options->blobauth_len); 27262306a36Sopenharmony_ci if (options->blobauth_len) 27362306a36Sopenharmony_ci tpm_buf_append(&buf, options->blobauth, options->blobauth_len); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci tpm_buf_append_u16(&buf, payload->key_len); 27662306a36Sopenharmony_ci tpm_buf_append(&buf, payload->key, payload->key_len); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* public */ 27962306a36Sopenharmony_ci tpm_buf_append_u16(&buf, 14 + options->policydigest_len); 28062306a36Sopenharmony_ci tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH); 28162306a36Sopenharmony_ci tpm_buf_append_u16(&buf, hash); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* key properties */ 28462306a36Sopenharmony_ci flags = 0; 28562306a36Sopenharmony_ci flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH; 28662306a36Sopenharmony_ci flags |= payload->migratable ? 0 : (TPM2_OA_FIXED_TPM | 28762306a36Sopenharmony_ci TPM2_OA_FIXED_PARENT); 28862306a36Sopenharmony_ci tpm_buf_append_u32(&buf, flags); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* policy */ 29162306a36Sopenharmony_ci tpm_buf_append_u16(&buf, options->policydigest_len); 29262306a36Sopenharmony_ci if (options->policydigest_len) 29362306a36Sopenharmony_ci tpm_buf_append(&buf, options->policydigest, 29462306a36Sopenharmony_ci options->policydigest_len); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* public parameters */ 29762306a36Sopenharmony_ci tpm_buf_append_u16(&buf, TPM_ALG_NULL); 29862306a36Sopenharmony_ci tpm_buf_append_u16(&buf, 0); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* outside info */ 30162306a36Sopenharmony_ci tpm_buf_append_u16(&buf, 0); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* creation PCR */ 30462306a36Sopenharmony_ci tpm_buf_append_u32(&buf, 0); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (buf.flags & TPM_BUF_OVERFLOW) { 30762306a36Sopenharmony_ci rc = -E2BIG; 30862306a36Sopenharmony_ci goto out; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data"); 31262306a36Sopenharmony_ci if (rc) 31362306a36Sopenharmony_ci goto out; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci blob_len = be32_to_cpup((__be32 *) &buf.data[TPM_HEADER_SIZE]); 31662306a36Sopenharmony_ci if (blob_len > MAX_BLOB_SIZE) { 31762306a36Sopenharmony_ci rc = -E2BIG; 31862306a36Sopenharmony_ci goto out; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) { 32162306a36Sopenharmony_ci rc = -EFAULT; 32262306a36Sopenharmony_ci goto out; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci blob_len = tpm2_key_encode(payload, options, 32662306a36Sopenharmony_ci &buf.data[TPM_HEADER_SIZE + 4], 32762306a36Sopenharmony_ci blob_len); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ciout: 33062306a36Sopenharmony_ci tpm_buf_destroy(&buf); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (rc > 0) { 33362306a36Sopenharmony_ci if (tpm2_rc_value(rc) == TPM2_RC_HASH) 33462306a36Sopenharmony_ci rc = -EINVAL; 33562306a36Sopenharmony_ci else 33662306a36Sopenharmony_ci rc = -EPERM; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci if (blob_len < 0) 33962306a36Sopenharmony_ci rc = blob_len; 34062306a36Sopenharmony_ci else 34162306a36Sopenharmony_ci payload->blob_len = blob_len; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci tpm_put_ops(chip); 34462306a36Sopenharmony_ci return rc; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci/** 34862306a36Sopenharmony_ci * tpm2_load_cmd() - execute a TPM2_Load command 34962306a36Sopenharmony_ci * 35062306a36Sopenharmony_ci * @chip: TPM chip to use 35162306a36Sopenharmony_ci * @payload: the key data in clear and encrypted form 35262306a36Sopenharmony_ci * @options: authentication values and other options 35362306a36Sopenharmony_ci * @blob_handle: returned blob handle 35462306a36Sopenharmony_ci * 35562306a36Sopenharmony_ci * Return: 0 on success. 35662306a36Sopenharmony_ci * -E2BIG on wrong payload size. 35762306a36Sopenharmony_ci * -EPERM on tpm error status. 35862306a36Sopenharmony_ci * < 0 error from tpm_send. 35962306a36Sopenharmony_ci */ 36062306a36Sopenharmony_cistatic int tpm2_load_cmd(struct tpm_chip *chip, 36162306a36Sopenharmony_ci struct trusted_key_payload *payload, 36262306a36Sopenharmony_ci struct trusted_key_options *options, 36362306a36Sopenharmony_ci u32 *blob_handle) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci struct tpm_buf buf; 36662306a36Sopenharmony_ci unsigned int private_len; 36762306a36Sopenharmony_ci unsigned int public_len; 36862306a36Sopenharmony_ci unsigned int blob_len; 36962306a36Sopenharmony_ci u8 *blob, *pub; 37062306a36Sopenharmony_ci int rc; 37162306a36Sopenharmony_ci u32 attrs; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci rc = tpm2_key_decode(payload, options, &blob); 37462306a36Sopenharmony_ci if (rc) { 37562306a36Sopenharmony_ci /* old form */ 37662306a36Sopenharmony_ci blob = payload->blob; 37762306a36Sopenharmony_ci payload->old_format = 1; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* new format carries keyhandle but old format doesn't */ 38162306a36Sopenharmony_ci if (!options->keyhandle) 38262306a36Sopenharmony_ci return -EINVAL; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* must be big enough for at least the two be16 size counts */ 38562306a36Sopenharmony_ci if (payload->blob_len < 4) 38662306a36Sopenharmony_ci return -EINVAL; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci private_len = get_unaligned_be16(blob); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* must be big enough for following public_len */ 39162306a36Sopenharmony_ci if (private_len + 2 + 2 > (payload->blob_len)) 39262306a36Sopenharmony_ci return -E2BIG; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci public_len = get_unaligned_be16(blob + 2 + private_len); 39562306a36Sopenharmony_ci if (private_len + 2 + public_len + 2 > payload->blob_len) 39662306a36Sopenharmony_ci return -E2BIG; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci pub = blob + 2 + private_len + 2; 39962306a36Sopenharmony_ci /* key attributes are always at offset 4 */ 40062306a36Sopenharmony_ci attrs = get_unaligned_be32(pub + 4); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if ((attrs & (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT)) == 40362306a36Sopenharmony_ci (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT)) 40462306a36Sopenharmony_ci payload->migratable = 0; 40562306a36Sopenharmony_ci else 40662306a36Sopenharmony_ci payload->migratable = 1; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci blob_len = private_len + public_len + 4; 40962306a36Sopenharmony_ci if (blob_len > payload->blob_len) 41062306a36Sopenharmony_ci return -E2BIG; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD); 41362306a36Sopenharmony_ci if (rc) 41462306a36Sopenharmony_ci return rc; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci tpm_buf_append_u32(&buf, options->keyhandle); 41762306a36Sopenharmony_ci tpm2_buf_append_auth(&buf, TPM2_RS_PW, 41862306a36Sopenharmony_ci NULL /* nonce */, 0, 41962306a36Sopenharmony_ci 0 /* session_attributes */, 42062306a36Sopenharmony_ci options->keyauth /* hmac */, 42162306a36Sopenharmony_ci TPM_DIGEST_SIZE); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci tpm_buf_append(&buf, blob, blob_len); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (buf.flags & TPM_BUF_OVERFLOW) { 42662306a36Sopenharmony_ci rc = -E2BIG; 42762306a36Sopenharmony_ci goto out; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob"); 43162306a36Sopenharmony_ci if (!rc) 43262306a36Sopenharmony_ci *blob_handle = be32_to_cpup( 43362306a36Sopenharmony_ci (__be32 *) &buf.data[TPM_HEADER_SIZE]); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ciout: 43662306a36Sopenharmony_ci if (blob != payload->blob) 43762306a36Sopenharmony_ci kfree(blob); 43862306a36Sopenharmony_ci tpm_buf_destroy(&buf); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (rc > 0) 44162306a36Sopenharmony_ci rc = -EPERM; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return rc; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci/** 44762306a36Sopenharmony_ci * tpm2_unseal_cmd() - execute a TPM2_Unload command 44862306a36Sopenharmony_ci * 44962306a36Sopenharmony_ci * @chip: TPM chip to use 45062306a36Sopenharmony_ci * @payload: the key data in clear and encrypted form 45162306a36Sopenharmony_ci * @options: authentication values and other options 45262306a36Sopenharmony_ci * @blob_handle: blob handle 45362306a36Sopenharmony_ci * 45462306a36Sopenharmony_ci * Return: 0 on success 45562306a36Sopenharmony_ci * -EPERM on tpm error status 45662306a36Sopenharmony_ci * < 0 error from tpm_send 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_cistatic int tpm2_unseal_cmd(struct tpm_chip *chip, 45962306a36Sopenharmony_ci struct trusted_key_payload *payload, 46062306a36Sopenharmony_ci struct trusted_key_options *options, 46162306a36Sopenharmony_ci u32 blob_handle) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct tpm_buf buf; 46462306a36Sopenharmony_ci u16 data_len; 46562306a36Sopenharmony_ci u8 *data; 46662306a36Sopenharmony_ci int rc; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL); 46962306a36Sopenharmony_ci if (rc) 47062306a36Sopenharmony_ci return rc; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci tpm_buf_append_u32(&buf, blob_handle); 47362306a36Sopenharmony_ci tpm2_buf_append_auth(&buf, 47462306a36Sopenharmony_ci options->policyhandle ? 47562306a36Sopenharmony_ci options->policyhandle : TPM2_RS_PW, 47662306a36Sopenharmony_ci NULL /* nonce */, 0, 47762306a36Sopenharmony_ci TPM2_SA_CONTINUE_SESSION, 47862306a36Sopenharmony_ci options->blobauth /* hmac */, 47962306a36Sopenharmony_ci options->blobauth_len); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing"); 48262306a36Sopenharmony_ci if (rc > 0) 48362306a36Sopenharmony_ci rc = -EPERM; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (!rc) { 48662306a36Sopenharmony_ci data_len = be16_to_cpup( 48762306a36Sopenharmony_ci (__be16 *) &buf.data[TPM_HEADER_SIZE + 4]); 48862306a36Sopenharmony_ci if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE) { 48962306a36Sopenharmony_ci rc = -EFAULT; 49062306a36Sopenharmony_ci goto out; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 6 + data_len) { 49462306a36Sopenharmony_ci rc = -EFAULT; 49562306a36Sopenharmony_ci goto out; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci data = &buf.data[TPM_HEADER_SIZE + 6]; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (payload->old_format) { 50062306a36Sopenharmony_ci /* migratable flag is at the end of the key */ 50162306a36Sopenharmony_ci memcpy(payload->key, data, data_len - 1); 50262306a36Sopenharmony_ci payload->key_len = data_len - 1; 50362306a36Sopenharmony_ci payload->migratable = data[data_len - 1]; 50462306a36Sopenharmony_ci } else { 50562306a36Sopenharmony_ci /* 50662306a36Sopenharmony_ci * migratable flag already collected from key 50762306a36Sopenharmony_ci * attributes 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_ci memcpy(payload->key, data, data_len); 51062306a36Sopenharmony_ci payload->key_len = data_len; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ciout: 51562306a36Sopenharmony_ci tpm_buf_destroy(&buf); 51662306a36Sopenharmony_ci return rc; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci/** 52062306a36Sopenharmony_ci * tpm2_unseal_trusted() - unseal the payload of a trusted key 52162306a36Sopenharmony_ci * 52262306a36Sopenharmony_ci * @chip: TPM chip to use 52362306a36Sopenharmony_ci * @payload: the key data in clear and encrypted form 52462306a36Sopenharmony_ci * @options: authentication values and other options 52562306a36Sopenharmony_ci * 52662306a36Sopenharmony_ci * Return: Same as with tpm_send. 52762306a36Sopenharmony_ci */ 52862306a36Sopenharmony_ciint tpm2_unseal_trusted(struct tpm_chip *chip, 52962306a36Sopenharmony_ci struct trusted_key_payload *payload, 53062306a36Sopenharmony_ci struct trusted_key_options *options) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci u32 blob_handle; 53362306a36Sopenharmony_ci int rc; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci rc = tpm_try_get_ops(chip); 53662306a36Sopenharmony_ci if (rc) 53762306a36Sopenharmony_ci return rc; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci rc = tpm2_load_cmd(chip, payload, options, &blob_handle); 54062306a36Sopenharmony_ci if (rc) 54162306a36Sopenharmony_ci goto out; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci rc = tpm2_unseal_cmd(chip, payload, options, blob_handle); 54462306a36Sopenharmony_ci tpm2_flush_context(chip, blob_handle); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ciout: 54762306a36Sopenharmony_ci tpm_put_ops(chip); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return rc; 55062306a36Sopenharmony_ci} 551