Implement a function to invoke TPM_CreateWrapKey. Signed-off-by: David Howells <dhowells@xxxxxxxxxx> --- drivers/char/tpm/tpm-library.c | 248 ++++++++++++++++++++++++++++++++++++++++ include/linux/tpm.h | 16 ++- include/linux/tpm_command.h | 1 3 files changed, 264 insertions(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm-library.c b/drivers/char/tpm/tpm-library.c index 0676165322a3..3413abeb3635 100644 --- a/drivers/char/tpm/tpm-library.c +++ b/drivers/char/tpm/tpm-library.c @@ -500,6 +500,7 @@ static int tpm_create_oiap(struct tpm_chip *chip, struct tpm_buf *tb, struct tpm_digests { unsigned char encauth[SHA1_DIGEST_SIZE]; + unsigned char encauth2[SHA1_DIGEST_SIZE]; unsigned char pubauth[SHA1_DIGEST_SIZE]; unsigned char xorwork[SHA1_DIGEST_SIZE * 2]; unsigned char xorhash[SHA1_DIGEST_SIZE]; @@ -769,6 +770,253 @@ out: } EXPORT_SYMBOL_GPL(tpm_unseal); +enum tpm_key_usage { + TPM_KEY_SIGNING = 0x0010, + TPM_KEY_STORAGE = 0x0011, + TPM_KEY_IDENTITY = 0x0012, + TPM_KEY_AUTHCHANGE = 0x0013, + TPM_KEY_BIND = 0x0014, + TPM_KEY_LEGACY = 0x0015, + TPM_KEY_MIGRATE = 0x0016, +}; + +enum tpm_algorithm_id { + TPM_ALG_RSA = 0x00000001, + TPM_ALG_SHA = 0x00000004, + TPM_ALG_HMAC = 0x00000005, + TPM_ALG_AES128 = 0x00000006, + TPM_ALG_MGF1 = 0x00000007, + TPM_ALG_AES192 = 0x00000008, + TPM_ALG_AES256 = 0x00000009, + TPM_ALG_XOR = 0x0000000a, +}; + +enum tpm_enc_scheme { + TPM_ES_NONE = 0x0001, + TPM_ES_RSAESPKCSv15 = 0x0002, + TPM_ES_RSAESOAEP_SHA1_MGF1 = 0x0003, + TPM_ES_SYM_CTR = 0x0004, + TPM_ES_SYM_OFB = 0x0005, +}; + +enum tpm_sig_scheme { + TPM_SS_NONE = 0x0001, + TPM_SS_RSAESPKCSv15_SHA1 = 0x0002, + TPM_SS_RSAESPKCSv15_DER = 0x0003, + TPM_SS_RSAESPKCSv15_INFO = 0x0004, +}; + +enum tpm_auth_data_usage { + TPM_AUTH_NEVER = 0x00, + TPM_AUTH_ALWAYS = 0x01, + TPM_NO_READ_PUBKEY_AUTH = 0x03, +}; + +#define TPM_KEY_REDIRECTION 0x00000001 +#define TPM_KEY_MIGRATABLE 0x00000002 +#define TPM_KEY_ISVOLATILE 0x00000004 +#define TPM_KEY_PCRIGNOREDONREAD 0x00000008 +#define TPM_KEY_MIGRATEAUTHORITY 0x00000010 + +struct tpm_key { + struct tpm_struct_ver { + u8 major, minor, rev_major, rev_minor; + } ver; + __be16 key_usage; /* enum tpm_key_usage */ + __be32 key_flags; + u8 auth_data_usage; /* enum tpm_auth_data_usage */ + struct tpm_key_parms { + __be32 algorithm_id; /* enum tpm_algorithm_id */ + __be16 enc_scheme; /* enum tpm_enc_scheme */ + __be16 sig_scheme; /* enum tpm_sig_scheme */ + __be32 parm_size; + struct tpm_rsa_key_parms { + __be32 key_length; + __be32 num_primes; + __be32 exponent_size; + } __packed rsa; + } __packed parms; + __be32 pcr_info_size; + struct tpm_store_pubkey { + __be32 key_length; + u8 key_data[0]; + } __packed pub; + __be32 enc_data_size; + u8 enc_data[0]; +} __packed; + +/** + * tpm_create_wrap_key - Generate a new key and return it encrypted + * @chip: The chip to use + * @tb: Large scratch buffer for I/O + * @parent_type: Type of entity attached to @parent_handle + * @parent_handle: TPM-resident key used to encrypt + * @parent_auth: Parent authorisation HMAC key + * @usage_auth: Encrypted usage authdata for the key + * @migration_auth: Encrypted migration authdata for the key (or NULL) + * @_wrapped_key: Pointer to where to return the wrapped key (kmalloc'd) + * + * Have the TPM generate a new key and return it encrypted. The encryption is + * based on a key already resident in the TPM and may also include the state of + * one or more Platform Configuration Registers (PCRs). + * + * AUTH1 is used for sealing key. + */ +int tpm_create_wrap_key(struct tpm_chip *chip, + enum tpm_entity_type parent_type, + uint32_t parent_handle, + const unsigned char *parent_auth, + const unsigned char *usage_auth, + const unsigned char *migration_auth, + struct tpm_wrapped_key **_wrapped_key) +{ + struct tpm_wrapped_key *wrapped_key; + struct tpm_osapsess sess; + struct tpm_digests *td; + struct tpm_buf *tb; + struct tpm_key *result; + unsigned char cont; + __be32 ordinal_be; + int key_size; + int ret; + + struct tpm_key tpm_key = { + .ver = { 0x01, 0x01, 0x00, 0x00 }, + .key_usage = cpu_to_be16(TPM_KEY_SIGNING), + .key_flags = cpu_to_be32(0), + .auth_data_usage = TPM_AUTH_ALWAYS, + .parms.algorithm_id = cpu_to_be32(TPM_ALG_RSA), + .parms.enc_scheme = cpu_to_be16(TPM_ES_RSAESPKCSv15), + .parms.sig_scheme = cpu_to_be16(TPM_SS_RSAESPKCSv15_SHA1), + .parms.parm_size = cpu_to_be32(sizeof(struct tpm_rsa_key_parms)), + .parms.rsa.key_length = cpu_to_be32(2048), + .parms.rsa.num_primes = cpu_to_be32(2), + .parms.rsa.exponent_size = cpu_to_be32(0), + .pcr_info_size = cpu_to_be32(0), + .pub.key_length = cpu_to_be32(0), + .enc_data_size = cpu_to_be32(0), + }; + + kenter(""); + + if (migration_auth) + tpm_key.key_flags |= cpu_to_be32(TPM_KEY_MIGRATABLE); + + /* alloc some work space */ + tb = kmalloc(sizeof(*tb) + sizeof(*td), GFP_KERNEL); + if (!tb) + return -ENOMEM; + td = (void *)tb + sizeof(*tb); + + /* Get the encryption session */ + ret = tpm_create_osap(chip, tb, &sess, + parent_auth, parent_type, parent_handle); + if (ret < 0) + goto out; + dump_sess(&sess); + + /* We need to pass 'passwords' to the TPM with which it will encrypt + * the key before returning it. So that the passwords don't travel to + * the TPM in the clear, we generate a symmetric key from the + * negotiated and encrypted session data and encrypt the passwords with + * that. + */ + ret = tpm_calc_symmetric_authkey(td, sess.secret, &sess.enonce); + if (ret < 0) + goto out; + tpm_crypt_with_authkey(td, usage_auth, td->encauth); + if (migration_auth) + tpm_crypt_with_authkey(td, migration_auth, td->encauth2); + else + tpm_crypt_with_authkey(td, sess.enonce.data, td->encauth2); + + /* Set up the parameters we will be sending */ + ret = tpm_gen_odd_nonce(chip, &td->ononce); + if (ret < 0) + goto out; + + /* calculate authorization HMAC value */ + ordinal_be = cpu_to_be32(TPM_ORD_CREATEWRAPKEY); + cont = 0; + ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE, + &sess.enonce, &td->ononce, cont, + /* 1S */ sizeof(__be32), &ordinal_be, + /* 2S */ SHA1_DIGEST_SIZE, td->encauth, + /* 3S */ SHA1_DIGEST_SIZE, td->encauth2, + /* 4S */ sizeof(tpm_key), &tpm_key, + 0, NULL); + if (ret < 0) + goto out; + + /* build and send the TPM request packet */ + INIT_BUF(tb); + store16(tb, TPM_TAG_RQU_AUTH1_COMMAND); + store32(tb, TPM_DATA_OFFSET + 44 + sizeof(tpm_key) + 45); + store32(tb, TPM_ORD_CREATEWRAPKEY); + store32(tb, parent_handle); + store_s(tb, td->encauth, SHA1_DIGEST_SIZE); + store_s(tb, td->encauth2, SHA1_DIGEST_SIZE); + store_s(tb, &tpm_key, sizeof(tpm_key)); + store32(tb, sess.handle); + store_s(tb, td->ononce.data, TPM_NONCE_SIZE); + store_8(tb, cont); + store_s(tb, td->pubauth, SHA1_DIGEST_SIZE); + + ret = tpm_send_dump(chip, tb, "creating key"); + if (ret < 0) + goto out; + + /* We need to work out how big the TPM_KEY or TPM_KEY12 struct we got + * back is. These structs have several variable-length fields inside + * to make parsing more difficult. However, they are only followed by + * fixed-length structs... + */ + SET_BUF_OFFSET(tb, TPM_SIZE_OFFSET); + key_size = LOAD32(tb); + key_size -= TPM_DATA_OFFSET; + key_size -= 2 * TPM_NONCE_SIZE + 1; + if (key_size < sizeof(tpm_key)) { + ret = -EBADMSG; + goto out; + } + + /* Check the HMAC in the response */ + ret = TSS_checkhmac1(tb, ordinal_be, &td->ononce, + sess.secret, SHA1_DIGEST_SIZE, + /* 3S */ key_size, TPM_DATA_OFFSET, + 0, NULL); + if (ret < 0) + goto out; + + /* Parse the key */ + result = (void *)tb->data + TPM_DATA_OFFSET; + ret = -EBADMSG; + if (key_size < sizeof(tpm_key) + be32_to_cpu(tpm_key.parms.rsa.key_length) / 8) + goto out; + if (memcmp(result, &tpm_key, offsetof(struct tpm_key, pub.key_length)) != 0) + goto out; + if (be32_to_cpu(result->pub.key_length) > + be32_to_cpu(tpm_key.parms.rsa.key_length) / 8) + goto out; + + ret = -ENOMEM; + wrapped_key = kmalloc(sizeof(struct tpm_wrapped_key) + key_size, GFP_KERNEL); + if (!wrapped_key) + goto out; + wrapped_key->data_len = key_size; + wrapped_key->pubkey_offset = offsetof(struct tpm_key, pub.key_data); + wrapped_key->pubkey_len = be32_to_cpu(result->pub.key_length); + memcpy(wrapped_key->data, result, key_size); + *_wrapped_key = wrapped_key; + ret = 0; + +out: + kfree(tb); + kleave(" = %d", ret); + return ret; +} +EXPORT_SYMBOL_GPL(tpm_create_wrap_key); + /** * tpm_library_use - Tell the TPM library we want to make use of it * diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 398bfaef2325..0bc954ac8266 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -83,7 +83,7 @@ static inline int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max) { */ /* implementation specific TPM constants */ #define MAX_PCRINFO_SIZE 64 -#define MAX_BUF_SIZE 512 +#define MAX_BUF_SIZE (2048 - 4) #define TPM_GETRANDOM_SIZE 14 #define TPM_OSAP_SIZE 36 #define TPM_OIAP_SIZE 10 @@ -133,4 +133,18 @@ extern int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb, const unsigned char *blobauth, unsigned char *data, unsigned int *datalen); +struct tpm_wrapped_key { + unsigned short data_len; + unsigned short pubkey_offset; + unsigned short pubkey_len; + u8 data[]; +}; + +extern int tpm_create_wrap_key(struct tpm_chip *chip, + enum tpm_entity_type parent_type, + uint32_t parent_handle, + const unsigned char *parent_auth, + const unsigned char *usage_auth, + const unsigned char *migration_auth, + struct tpm_wrapped_key **_wrapped_key); #endif diff --git a/include/linux/tpm_command.h b/include/linux/tpm_command.h index a3e0bb670e62..0417a3db7e8f 100644 --- a/include/linux/tpm_command.h +++ b/include/linux/tpm_command.h @@ -22,6 +22,7 @@ enum tpm_ordinal { TPM_ORD_PCR_READ = 21, TPM_ORD_SEAL = 23, TPM_ORD_UNSEAL = 24, + TPM_ORD_CREATEWRAPKEY = 31, TPM_ORD_GET_RANDOM = 70, TPM_ORD_CONTINUE_SELFTEST = 83, TPM_ORD_GET_CAP = 101,