From 9542d82be5a4308a0dc0e8b80bd89762c3bdc1ca Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:48:02 +0100 Subject: KEYS: asym_tpm: add skeleton for asym_tpm [ver #2] This patch adds the basic skeleton for the asym_tpm asymmetric key subtype. Signed-off-by: Denis Kenzior Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- crypto/asymmetric_keys/asym_tpm.c | 90 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 crypto/asymmetric_keys/asym_tpm.c (limited to 'crypto/asymmetric_keys/asym_tpm.c') diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c new file mode 100644 index 00000000..d0b2b97e --- /dev/null +++ b/crypto/asymmetric_keys/asym_tpm.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0 +#define pr_fmt(fmt) "ASYM-TPM: "fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Provide a part of a description of the key for /proc/keys. + */ +static void asym_tpm_describe(const struct key *asymmetric_key, + struct seq_file *m) +{ + struct tpm_key *tk = asymmetric_key->payload.data[asym_crypto]; + + if (!tk) + return; + + seq_printf(m, "TPM1.2/Blob"); +} + +static void asym_tpm_destroy(void *payload0, void *payload3) +{ + struct tpm_key *tk = payload0; + + if (!tk) + return; + + kfree(tk->blob); + tk->blob_len = 0; + + kfree(tk); +} + +/* Given the blob, parse it and load it into the TPM */ +struct tpm_key *tpm_key_create(const void *blob, uint32_t blob_len) +{ + int r; + struct tpm_key *tk; + + r = tpm_is_tpm2(NULL); + if (r < 0) + goto error; + + /* We don't support TPM2 yet */ + if (r > 0) { + r = -ENODEV; + goto error; + } + + r = -ENOMEM; + tk = kzalloc(sizeof(struct tpm_key), GFP_KERNEL); + if (!tk) + goto error; + + tk->blob = kmemdup(blob, blob_len, GFP_KERNEL); + if (!tk->blob) + goto error_memdup; + + tk->blob_len = blob_len; + + return tk; + +error_memdup: + kfree(tk); +error: + return ERR_PTR(r); +} +EXPORT_SYMBOL_GPL(tpm_key_create); + +/* + * TPM-based asymmetric key subtype + */ +struct asymmetric_key_subtype asym_tpm_subtype = { + .owner = THIS_MODULE, + .name = "asym_tpm", + .name_len = sizeof("asym_tpm") - 1, + .describe = asym_tpm_describe, + .destroy = asym_tpm_destroy, +}; +EXPORT_SYMBOL_GPL(asym_tpm_subtype); + +MODULE_DESCRIPTION("TPM based asymmetric key subtype"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 0d8bf3fdcb962c4236b5f57abb5e8f268e23523a Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:48:10 +0100 Subject: KEYS: asym_tpm: extract key size & public key [ver #2] The parsed BER/DER blob obtained from user space contains a TPM_Key structure. This structure has some information about the key as well as the public key portion. This patch extracts this information for future use. Signed-off-by: Denis Kenzior Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- crypto/asymmetric_keys/asym_tpm.c | 112 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) (limited to 'crypto/asymmetric_keys/asym_tpm.c') diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c index d0b2b97e..308c51e0 100644 --- a/crypto/asymmetric_keys/asym_tpm.c +++ b/crypto/asymmetric_keys/asym_tpm.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -37,6 +38,110 @@ static void asym_tpm_destroy(void *payload0, void *payload3) kfree(tk); } +/* + * Parse enough information out of TPM_KEY structure: + * TPM_STRUCT_VER -> 4 bytes + * TPM_KEY_USAGE -> 2 bytes + * TPM_KEY_FLAGS -> 4 bytes + * TPM_AUTH_DATA_USAGE -> 1 byte + * TPM_KEY_PARMS -> variable + * UINT32 PCRInfoSize -> 4 bytes + * BYTE* -> PCRInfoSize bytes + * TPM_STORE_PUBKEY + * UINT32 encDataSize; + * BYTE* -> encDataSize; + * + * TPM_KEY_PARMS: + * TPM_ALGORITHM_ID -> 4 bytes + * TPM_ENC_SCHEME -> 2 bytes + * TPM_SIG_SCHEME -> 2 bytes + * UINT32 parmSize -> 4 bytes + * BYTE* -> variable + */ +static int extract_key_parameters(struct tpm_key *tk) +{ + const void *cur = tk->blob; + uint32_t len = tk->blob_len; + const void *pub_key; + uint32_t sz; + uint32_t key_len; + + if (len < 11) + return -EBADMSG; + + /* Ensure this is a legacy key */ + if (get_unaligned_be16(cur + 4) != 0x0015) + return -EBADMSG; + + /* Skip to TPM_KEY_PARMS */ + cur += 11; + len -= 11; + + if (len < 12) + return -EBADMSG; + + /* Make sure this is an RSA key */ + if (get_unaligned_be32(cur) != 0x00000001) + return -EBADMSG; + + /* Make sure this is TPM_ES_RSAESPKCSv15 encoding scheme */ + if (get_unaligned_be16(cur + 4) != 0x0002) + return -EBADMSG; + + /* Make sure this is TPM_SS_RSASSAPKCS1v15_DER signature scheme */ + if (get_unaligned_be16(cur + 6) != 0x0003) + return -EBADMSG; + + sz = get_unaligned_be32(cur + 8); + if (len < sz + 12) + return -EBADMSG; + + /* Move to TPM_RSA_KEY_PARMS */ + len -= 12; + cur += 12; + + /* Grab the RSA key length */ + key_len = get_unaligned_be32(cur); + + switch (key_len) { + case 512: + case 1024: + case 1536: + case 2048: + break; + default: + return -EINVAL; + } + + /* Move just past TPM_KEY_PARMS */ + cur += sz; + len -= sz; + + if (len < 4) + return -EBADMSG; + + sz = get_unaligned_be32(cur); + if (len < 4 + sz) + return -EBADMSG; + + /* Move to TPM_STORE_PUBKEY */ + cur += 4 + sz; + len -= 4 + sz; + + /* Grab the size of the public key, it should jive with the key size */ + sz = get_unaligned_be32(cur); + if (sz > 256) + return -EINVAL; + + pub_key = cur + 4; + + tk->key_len = key_len; + tk->pub_key = pub_key; + tk->pub_key_len = sz; + + return 0; +} + /* Given the blob, parse it and load it into the TPM */ struct tpm_key *tpm_key_create(const void *blob, uint32_t blob_len) { @@ -64,8 +169,15 @@ struct tpm_key *tpm_key_create(const void *blob, uint32_t blob_len) tk->blob_len = blob_len; + r = extract_key_parameters(tk); + if (r < 0) + goto error_extract; + return tk; +error_extract: + kfree(tk->blob); + tk->blob_len = 0; error_memdup: kfree(tk); error: -- cgit v1.2.3 From 6cdd0568289d49f137e37e4cc010e2bc4c4e4b9c Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:48:25 +0100 Subject: KEYS: asym_tpm: Implement pkey_query [ver #2] This commit implements the pkey_query operation. This is accomplished by utilizing the public key portion to obtain max encryption size information for the operations that utilize the public key (encrypt, verify). The private key size extracted from the TPM_Key data structure is used to fill the information where the private key is used (decrypt, sign). The kernel uses a DER/BER format for public keys and does not support setting the key via the raw binary form. To get around this a simple DER/BER formatter is implemented which stores the DER/BER formatted key and exponent in a temporary buffer for use by the crypto API. The only exponent supported currently is 65537. This holds true for other Linux TPM tools such as 'create_tpm_key' and trousers-openssl_tpm_engine. Signed-off-by: Denis Kenzior Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- crypto/asymmetric_keys/asym_tpm.c | 135 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) (limited to 'crypto/asymmetric_keys/asym_tpm.c') diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c index 308c51e0..837472d1 100644 --- a/crypto/asymmetric_keys/asym_tpm.c +++ b/crypto/asymmetric_keys/asym_tpm.c @@ -7,10 +7,24 @@ #include #include #include +#include #include #include #include +/* + * Maximum buffer size for the BER/DER encoded public key. The public key + * is of the form SEQUENCE { INTEGER n, INTEGER e } where n is a maximum 2048 + * bit key and e is usually 65537 + * The encoding overhead is: + * - max 4 bytes for SEQUENCE + * - max 4 bytes for INTEGER n type/length + * - 257 bytes of n + * - max 2 bytes for INTEGER e type/length + * - 3 bytes of e + */ +#define PUB_KEY_BUF_SIZE (4 + 4 + 257 + 2 + 3) + /* * Provide a part of a description of the key for /proc/keys. */ @@ -38,6 +52,126 @@ static void asym_tpm_destroy(void *payload0, void *payload3) kfree(tk); } +/* How many bytes will it take to encode the length */ +static inline uint32_t definite_length(uint32_t len) +{ + if (len <= 127) + return 1; + if (len <= 255) + return 2; + return 3; +} + +static inline uint8_t *encode_tag_length(uint8_t *buf, uint8_t tag, + uint32_t len) +{ + *buf++ = tag; + + if (len <= 127) { + buf[0] = len; + return buf + 1; + } + + if (len <= 255) { + buf[0] = 0x81; + buf[1] = len; + return buf + 2; + } + + buf[0] = 0x82; + put_unaligned_be16(len, buf + 1); + return buf + 3; +} + +static uint32_t derive_pub_key(const void *pub_key, uint32_t len, uint8_t *buf) +{ + uint8_t *cur = buf; + uint32_t n_len = definite_length(len) + 1 + len + 1; + uint32_t e_len = definite_length(3) + 1 + 3; + uint8_t e[3] = { 0x01, 0x00, 0x01 }; + + /* SEQUENCE */ + cur = encode_tag_length(cur, 0x30, n_len + e_len); + /* INTEGER n */ + cur = encode_tag_length(cur, 0x02, len + 1); + cur[0] = 0x00; + memcpy(cur + 1, pub_key, len); + cur += len + 1; + cur = encode_tag_length(cur, 0x02, sizeof(e)); + memcpy(cur, e, sizeof(e)); + cur += sizeof(e); + + return cur - buf; +} + +/* + * Determine the crypto algorithm name. + */ +static int determine_akcipher(const char *encoding, const char *hash_algo, + char alg_name[CRYPTO_MAX_ALG_NAME]) +{ + /* TODO: We don't support hashing yet */ + if (hash_algo) + return -ENOPKG; + + if (strcmp(encoding, "pkcs1") == 0) { + strcpy(alg_name, "pkcs1pad(rsa)"); + return 0; + } + + if (strcmp(encoding, "raw") == 0) { + strcpy(alg_name, "rsa"); + return 0; + } + + return -ENOPKG; +} + +/* + * Query information about a key. + */ +static int tpm_key_query(const struct kernel_pkey_params *params, + struct kernel_pkey_query *info) +{ + struct tpm_key *tk = params->key->payload.data[asym_crypto]; + int ret; + char alg_name[CRYPTO_MAX_ALG_NAME]; + struct crypto_akcipher *tfm; + uint8_t der_pub_key[PUB_KEY_BUF_SIZE]; + uint32_t der_pub_key_len; + int len; + + /* TPM only works on private keys, public keys still done in software */ + ret = determine_akcipher(params->encoding, params->hash_algo, alg_name); + if (ret < 0) + return ret; + + tfm = crypto_alloc_akcipher(alg_name, 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + der_pub_key_len = derive_pub_key(tk->pub_key, tk->pub_key_len, + der_pub_key); + + ret = crypto_akcipher_set_pub_key(tfm, der_pub_key, der_pub_key_len); + if (ret < 0) + goto error_free_tfm; + + len = crypto_akcipher_maxsize(tfm); + + info->key_size = tk->key_len; + info->max_data_size = tk->key_len / 8; + info->max_sig_size = len; + info->max_enc_size = len; + info->max_dec_size = tk->key_len / 8; + + ret = 0; +error_free_tfm: + crypto_free_akcipher(tfm); + pr_devel("<==%s() = %d\n", __func__, ret); + return ret; +} + /* * Parse enough information out of TPM_KEY structure: * TPM_STRUCT_VER -> 4 bytes @@ -194,6 +328,7 @@ struct asymmetric_key_subtype asym_tpm_subtype = { .name_len = sizeof("asym_tpm") - 1, .describe = asym_tpm_describe, .destroy = asym_tpm_destroy, + .query = tpm_key_query, }; EXPORT_SYMBOL_GPL(asym_tpm_subtype); -- cgit v1.2.3 From 61e4c308f504a03f029485e02b2b5b617b3ca6e0 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:48:33 +0100 Subject: KEYS: asym_tpm: Implement encryption operation [ver #2] This patch impelements the pkey_encrypt operation. The public key portion extracted from the TPM key blob is used. The operation is performed entirely in software using the crypto API. Signed-off-by: Denis Kenzior Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- crypto/asymmetric_keys/asym_tpm.c | 84 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) (limited to 'crypto/asymmetric_keys/asym_tpm.c') diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c index 837472d1..8edca3c4 100644 --- a/crypto/asymmetric_keys/asym_tpm.c +++ b/crypto/asymmetric_keys/asym_tpm.c @@ -165,6 +165,8 @@ static int tpm_key_query(const struct kernel_pkey_params *params, info->max_enc_size = len; info->max_dec_size = tk->key_len / 8; + info->supported_ops = KEYCTL_SUPPORTS_ENCRYPT; + ret = 0; error_free_tfm: crypto_free_akcipher(tfm); @@ -172,6 +174,87 @@ error_free_tfm: return ret; } +/* + * Encryption operation is performed with the public key. Hence it is done + * in software + */ +static int tpm_key_encrypt(struct tpm_key *tk, + struct kernel_pkey_params *params, + const void *in, void *out) +{ + char alg_name[CRYPTO_MAX_ALG_NAME]; + struct crypto_akcipher *tfm; + struct akcipher_request *req; + struct crypto_wait cwait; + struct scatterlist in_sg, out_sg; + uint8_t der_pub_key[PUB_KEY_BUF_SIZE]; + uint32_t der_pub_key_len; + int ret; + + pr_devel("==>%s()\n", __func__); + + ret = determine_akcipher(params->encoding, params->hash_algo, alg_name); + if (ret < 0) + return ret; + + tfm = crypto_alloc_akcipher(alg_name, 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + der_pub_key_len = derive_pub_key(tk->pub_key, tk->pub_key_len, + der_pub_key); + + ret = crypto_akcipher_set_pub_key(tfm, der_pub_key, der_pub_key_len); + if (ret < 0) + goto error_free_tfm; + + req = akcipher_request_alloc(tfm, GFP_KERNEL); + if (!req) + goto error_free_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); + ret = crypto_wait_req(ret, &cwait); + + if (ret == 0) + ret = req->dst_len; + + akcipher_request_free(req); +error_free_tfm: + crypto_free_akcipher(tfm); + pr_devel("<==%s() = %d\n", __func__, ret); + return ret; +} + +/* + * Do encryption, decryption and signing ops. + */ +static int tpm_key_eds_op(struct kernel_pkey_params *params, + const void *in, void *out) +{ + struct tpm_key *tk = params->key->payload.data[asym_crypto]; + int ret = -EOPNOTSUPP; + + /* Perform the encryption calculation. */ + switch (params->op) { + case kernel_pkey_encrypt: + ret = tpm_key_encrypt(tk, params, in, out); + break; + default: + BUG(); + } + + return ret; +} + /* * Parse enough information out of TPM_KEY structure: * TPM_STRUCT_VER -> 4 bytes @@ -329,6 +412,7 @@ struct asymmetric_key_subtype asym_tpm_subtype = { .describe = asym_tpm_describe, .destroy = asym_tpm_destroy, .query = tpm_key_query, + .eds_op = tpm_key_eds_op, }; EXPORT_SYMBOL_GPL(asym_tpm_subtype); -- cgit v1.2.3 From 1b590d0984cdf88c2a6ae5493dc782151f7d0ec9 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:48:58 +0100 Subject: KEYS: asym_tpm: Add loadkey2 and flushspecific [ver #2] This commit adds TPM_LoadKey2 and TPM_FlushSpecific operations. Signed-off-by: Denis Kenzior Signed-off-by: David Howells Reviewed-by: James Morris Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- crypto/asymmetric_keys/asym_tpm.c | 94 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) (limited to 'crypto/asymmetric_keys/asym_tpm.c') diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c index 8edca3c4..6a2d3301 100644 --- a/crypto/asymmetric_keys/asym_tpm.c +++ b/crypto/asymmetric_keys/asym_tpm.c @@ -7,11 +7,105 @@ #include #include #include +#include #include +#include +#include #include #include +#include #include +#define TPM_ORD_FLUSHSPECIFIC 186 +#define TPM_ORD_LOADKEY2 65 +#define TPM_LOADKEY2_SIZE 59 +#define TPM_FLUSHSPECIFIC_SIZE 18 + +#define TPM_RT_KEY 0x00000001 + +/* + * Load a TPM key from the blob provided by userspace + */ +static int tpm_loadkey2(struct tpm_buf *tb, + uint32_t keyhandle, unsigned char *keyauth, + const unsigned char *keyblob, int keybloblen, + uint32_t *newhandle) +{ + unsigned char nonceodd[TPM_NONCE_SIZE]; + unsigned char enonce[TPM_NONCE_SIZE]; + unsigned char authdata[SHA1_DIGEST_SIZE]; + uint32_t authhandle = 0; + unsigned char cont = 0; + uint32_t ordinal; + int ret; + + ordinal = htonl(TPM_ORD_LOADKEY2); + + /* session for loading the key */ + ret = oiap(tb, &authhandle, enonce); + if (ret < 0) { + pr_info("oiap failed (%d)\n", ret); + return ret; + } + + /* generate odd nonce */ + ret = tpm_get_random(NULL, nonceodd, TPM_NONCE_SIZE); + if (ret < 0) { + pr_info("tpm_get_random failed (%d)\n", ret); + return ret; + } + + /* calculate authorization HMAC value */ + ret = TSS_authhmac(authdata, keyauth, SHA1_DIGEST_SIZE, enonce, + nonceodd, cont, sizeof(uint32_t), &ordinal, + keybloblen, keyblob, 0, 0); + if (ret < 0) + return ret; + + /* build the request buffer */ + INIT_BUF(tb); + store16(tb, TPM_TAG_RQU_AUTH1_COMMAND); + store32(tb, TPM_LOADKEY2_SIZE + keybloblen); + store32(tb, TPM_ORD_LOADKEY2); + store32(tb, keyhandle); + storebytes(tb, keyblob, keybloblen); + store32(tb, authhandle); + storebytes(tb, nonceodd, TPM_NONCE_SIZE); + store8(tb, cont); + storebytes(tb, authdata, SHA1_DIGEST_SIZE); + + ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE); + if (ret < 0) { + pr_info("authhmac failed (%d)\n", ret); + return ret; + } + + ret = TSS_checkhmac1(tb->data, ordinal, nonceodd, keyauth, + SHA1_DIGEST_SIZE, 0, 0); + if (ret < 0) { + pr_info("TSS_checkhmac1 failed (%d)\n", ret); + return ret; + } + + *newhandle = LOAD32(tb->data, TPM_DATA_OFFSET); + return 0; +} + +/* + * Execute the FlushSpecific TPM command + */ +static int tpm_flushspecific(struct tpm_buf *tb, uint32_t handle) +{ + INIT_BUF(tb); + store16(tb, TPM_TAG_RQU_COMMAND); + store32(tb, TPM_FLUSHSPECIFIC_SIZE); + store32(tb, TPM_ORD_FLUSHSPECIFIC); + store32(tb, handle); + store32(tb, TPM_RT_KEY); + + return trusted_tpm_send(tb->data, MAX_BUF_SIZE); +} + /* * Maximum buffer size for the BER/DER encoded public key. The public key * is of the form SEQUENCE { INTEGER n, INTEGER e } where n is a maximum 2048 -- cgit v1.2.3 From 9c955615601225002643f6b7824d89fb5af79b8a Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:49:05 +0100 Subject: KEYS: asym_tpm: Implement tpm_unbind [ver #2] Signed-off-by: Denis Kenzior Reviewed-by: James Morris Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- crypto/asymmetric_keys/asym_tpm.c | 82 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) (limited to 'crypto/asymmetric_keys/asym_tpm.c') diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c index 6a2d3301..e893b521 100644 --- a/crypto/asymmetric_keys/asym_tpm.c +++ b/crypto/asymmetric_keys/asym_tpm.c @@ -18,8 +18,10 @@ #define TPM_ORD_FLUSHSPECIFIC 186 #define TPM_ORD_LOADKEY2 65 +#define TPM_ORD_UNBIND 30 #define TPM_LOADKEY2_SIZE 59 #define TPM_FLUSHSPECIFIC_SIZE 18 +#define TPM_UNBIND_SIZE 63 #define TPM_RT_KEY 0x00000001 @@ -106,6 +108,86 @@ static int tpm_flushspecific(struct tpm_buf *tb, uint32_t handle) return trusted_tpm_send(tb->data, MAX_BUF_SIZE); } +/* + * Decrypt a blob provided by userspace using a specific key handle. + * The handle is a well known handle or previously loaded by e.g. LoadKey2 + */ +static int tpm_unbind(struct tpm_buf *tb, + uint32_t keyhandle, unsigned char *keyauth, + const unsigned char *blob, uint32_t bloblen, + void *out, uint32_t outlen) +{ + unsigned char nonceodd[TPM_NONCE_SIZE]; + unsigned char enonce[TPM_NONCE_SIZE]; + unsigned char authdata[SHA1_DIGEST_SIZE]; + uint32_t authhandle = 0; + unsigned char cont = 0; + uint32_t ordinal; + uint32_t datalen; + int ret; + + ordinal = htonl(TPM_ORD_UNBIND); + datalen = htonl(bloblen); + + /* session for loading the key */ + ret = oiap(tb, &authhandle, enonce); + if (ret < 0) { + pr_info("oiap failed (%d)\n", ret); + return ret; + } + + /* generate odd nonce */ + ret = tpm_get_random(NULL, nonceodd, TPM_NONCE_SIZE); + if (ret < 0) { + pr_info("tpm_get_random failed (%d)\n", ret); + return ret; + } + + /* calculate authorization HMAC value */ + ret = TSS_authhmac(authdata, keyauth, SHA1_DIGEST_SIZE, enonce, + nonceodd, cont, sizeof(uint32_t), &ordinal, + sizeof(uint32_t), &datalen, + bloblen, blob, 0, 0); + if (ret < 0) + return ret; + + /* build the request buffer */ + INIT_BUF(tb); + store16(tb, TPM_TAG_RQU_AUTH1_COMMAND); + store32(tb, TPM_UNBIND_SIZE + bloblen); + store32(tb, TPM_ORD_UNBIND); + store32(tb, keyhandle); + store32(tb, bloblen); + storebytes(tb, blob, bloblen); + store32(tb, authhandle); + storebytes(tb, nonceodd, TPM_NONCE_SIZE); + store8(tb, cont); + storebytes(tb, authdata, SHA1_DIGEST_SIZE); + + ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE); + if (ret < 0) { + pr_info("authhmac failed (%d)\n", ret); + return ret; + } + + datalen = LOAD32(tb->data, TPM_DATA_OFFSET); + + ret = TSS_checkhmac1(tb->data, ordinal, nonceodd, + keyauth, SHA1_DIGEST_SIZE, + sizeof(uint32_t), TPM_DATA_OFFSET, + datalen, TPM_DATA_OFFSET + sizeof(uint32_t), + 0, 0); + if (ret < 0) { + pr_info("TSS_checkhmac1 failed (%d)\n", ret); + return ret; + } + + memcpy(out, tb->data + TPM_DATA_OFFSET + sizeof(uint32_t), + min(outlen, datalen)); + + return datalen; +} + /* * Maximum buffer size for the BER/DER encoded public key. The public key * is of the form SEQUENCE { INTEGER n, INTEGER e } where n is a maximum 2048 -- cgit v1.2.3 From 4574e923a251485f32653155548d893da091ebc0 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:49:13 +0100 Subject: KEYS: asym_tpm: Implement the decrypt operation [ver #2] This patch implements the pkey_decrypt operation using the private key blob. The blob is first loaded into the TPM via tpm_loadkey2. Once the handle is obtained, tpm_unbind operation is used to decrypt the data on the TPM and the result is returned. The key loaded by tpm_loadkey2 is then evicted via tpm_flushspecific operation. This patch assumes that the SRK authorization is a well known 20-byte of zeros and the same holds for the key authorization of the provided key. Signed-off-by: Denis Kenzior Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- crypto/asymmetric_keys/asym_tpm.c | 58 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) (limited to 'crypto/asymmetric_keys/asym_tpm.c') diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c index e893b521..6f5d5cf9 100644 --- a/crypto/asymmetric_keys/asym_tpm.c +++ b/crypto/asymmetric_keys/asym_tpm.c @@ -341,7 +341,8 @@ static int tpm_key_query(const struct kernel_pkey_params *params, info->max_enc_size = len; info->max_dec_size = tk->key_len / 8; - info->supported_ops = KEYCTL_SUPPORTS_ENCRYPT; + info->supported_ops = KEYCTL_SUPPORTS_ENCRYPT | + KEYCTL_SUPPORTS_DECRYPT; ret = 0; error_free_tfm: @@ -410,6 +411,58 @@ error_free_tfm: return ret; } +/* + * Decryption operation is performed with the private key in the TPM. + */ +static int tpm_key_decrypt(struct tpm_key *tk, + struct kernel_pkey_params *params, + const void *in, void *out) +{ + struct tpm_buf *tb; + uint32_t keyhandle; + uint8_t srkauth[SHA1_DIGEST_SIZE]; + uint8_t keyauth[SHA1_DIGEST_SIZE]; + int r; + + pr_devel("==>%s()\n", __func__); + + if (params->hash_algo) + return -ENOPKG; + + if (strcmp(params->encoding, "pkcs1")) + return -ENOPKG; + + tb = kzalloc(sizeof(*tb), GFP_KERNEL); + if (!tb) + return -ENOMEM; + + /* TODO: Handle a non-all zero SRK authorization */ + memset(srkauth, 0, sizeof(srkauth)); + + r = tpm_loadkey2(tb, SRKHANDLE, srkauth, + tk->blob, tk->blob_len, &keyhandle); + if (r < 0) { + pr_devel("loadkey2 failed (%d)\n", r); + goto error; + } + + /* TODO: Handle a non-all zero key authorization */ + memset(keyauth, 0, sizeof(keyauth)); + + r = tpm_unbind(tb, keyhandle, keyauth, + in, params->in_len, out, params->out_len); + if (r < 0) + pr_devel("tpm_unbind failed (%d)\n", r); + + if (tpm_flushspecific(tb, keyhandle) < 0) + pr_devel("flushspecific failed (%d)\n", r); + +error: + kzfree(tb); + pr_devel("<==%s() = %d\n", __func__, r); + return r; +} + /* * Do encryption, decryption and signing ops. */ @@ -424,6 +477,9 @@ static int tpm_key_eds_op(struct kernel_pkey_params *params, case kernel_pkey_encrypt: ret = tpm_key_encrypt(tk, params, in, out); break; + case kernel_pkey_decrypt: + ret = tpm_key_decrypt(tk, params, in, out); + break; default: BUG(); } -- cgit v1.2.3 From c4ce92489a27b5cef40e001f00b9e1960b4cb08d Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:49:20 +0100 Subject: KEYS: asym_tpm: Implement signature verification [ver #2] This patch implements the verify_signature operation. The public key portion extracted from the TPM key blob is used. The operation is performed entirely in software using the crypto API. Signed-off-by: Denis Kenzior Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- crypto/asymmetric_keys/asym_tpm.c | 106 +++++++++++++++++++++++++++++++++++--- 1 file changed, 100 insertions(+), 6 deletions(-) (limited to 'crypto/asymmetric_keys/asym_tpm.c') diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c index 6f5d5cf9..a38ba375 100644 --- a/crypto/asymmetric_keys/asym_tpm.c +++ b/crypto/asymmetric_keys/asym_tpm.c @@ -15,6 +15,7 @@ #include #include #include +#include #define TPM_ORD_FLUSHSPECIFIC 186 #define TPM_ORD_LOADKEY2 65 @@ -286,12 +287,16 @@ static uint32_t derive_pub_key(const void *pub_key, uint32_t len, uint8_t *buf) static int determine_akcipher(const char *encoding, const char *hash_algo, char alg_name[CRYPTO_MAX_ALG_NAME]) { - /* TODO: We don't support hashing yet */ - if (hash_algo) - return -ENOPKG; - if (strcmp(encoding, "pkcs1") == 0) { - strcpy(alg_name, "pkcs1pad(rsa)"); + if (!hash_algo) { + strcpy(alg_name, "pkcs1pad(rsa)"); + return 0; + } + + if (snprintf(alg_name, CRYPTO_MAX_ALG_NAME, "pkcs1pad(rsa,%s)", + hash_algo) >= CRYPTO_MAX_ALG_NAME) + return -EINVAL; + return 0; } @@ -342,7 +347,8 @@ static int tpm_key_query(const struct kernel_pkey_params *params, info->max_dec_size = tk->key_len / 8; info->supported_ops = KEYCTL_SUPPORTS_ENCRYPT | - KEYCTL_SUPPORTS_DECRYPT; + KEYCTL_SUPPORTS_DECRYPT | + KEYCTL_SUPPORTS_VERIFY; ret = 0; error_free_tfm: @@ -487,6 +493,93 @@ static int tpm_key_eds_op(struct kernel_pkey_params *params, return ret; } +/* + * Verify a signature using a public key. + */ +static int tpm_key_verify_signature(const struct key *key, + const struct public_key_signature *sig) +{ + const struct tpm_key *tk = key->payload.data[asym_crypto]; + struct crypto_wait cwait; + struct crypto_akcipher *tfm; + struct akcipher_request *req; + struct scatterlist sig_sg, digest_sg; + char alg_name[CRYPTO_MAX_ALG_NAME]; + uint8_t der_pub_key[PUB_KEY_BUF_SIZE]; + uint32_t der_pub_key_len; + void *output; + unsigned int outlen; + int ret; + + pr_devel("==>%s()\n", __func__); + + BUG_ON(!tk); + BUG_ON(!sig); + BUG_ON(!sig->s); + + if (!sig->digest) + return -ENOPKG; + + ret = determine_akcipher(sig->encoding, sig->hash_algo, alg_name); + if (ret < 0) + return ret; + + tfm = crypto_alloc_akcipher(alg_name, 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + der_pub_key_len = derive_pub_key(tk->pub_key, tk->pub_key_len, + der_pub_key); + + ret = crypto_akcipher_set_pub_key(tfm, der_pub_key, der_pub_key_len); + if (ret < 0) + goto error_free_tfm; + + ret = -ENOMEM; + req = akcipher_request_alloc(tfm, GFP_KERNEL); + if (!req) + goto error_free_tfm; + + ret = -ENOMEM; + outlen = crypto_akcipher_maxsize(tfm); + output = kmalloc(outlen, GFP_KERNEL); + if (!output) + goto error_free_req; + + sg_init_one(&sig_sg, sig->s, sig->s_size); + sg_init_one(&digest_sg, output, outlen); + akcipher_request_set_crypt(req, &sig_sg, &digest_sg, sig->s_size, + outlen); + crypto_init_wait(&cwait); + akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_req_done, &cwait); + + /* Perform the verification calculation. This doesn't actually do the + * verification, but rather calculates the hash expected by the + * signature and returns that to us. + */ + ret = crypto_wait_req(crypto_akcipher_verify(req), &cwait); + if (ret) + goto out_free_output; + + /* Do the actual verification step. */ + if (req->dst_len != sig->digest_size || + memcmp(sig->digest, output, sig->digest_size) != 0) + ret = -EKEYREJECTED; + +out_free_output: + kfree(output); +error_free_req: + akcipher_request_free(req); +error_free_tfm: + crypto_free_akcipher(tfm); + pr_devel("<==%s() = %d\n", __func__, ret); + if (WARN_ON_ONCE(ret > 0)) + ret = -EINVAL; + return ret; +} + /* * Parse enough information out of TPM_KEY structure: * TPM_STRUCT_VER -> 4 bytes @@ -645,6 +738,7 @@ struct asymmetric_key_subtype asym_tpm_subtype = { .destroy = asym_tpm_destroy, .query = tpm_key_query, .eds_op = tpm_key_eds_op, + .verify_signature = tpm_key_verify_signature, }; EXPORT_SYMBOL_GPL(asym_tpm_subtype); -- cgit v1.2.3 From d807cd6411da807be6d03d4c283248d127650f17 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:49:28 +0100 Subject: KEYS: asym_tpm: Implement tpm_sign [ver #2] Signed-off-by: Denis Kenzior Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- crypto/asymmetric_keys/asym_tpm.c | 87 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) (limited to 'crypto/asymmetric_keys/asym_tpm.c') diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c index a38ba375..a5a5f913 100644 --- a/crypto/asymmetric_keys/asym_tpm.c +++ b/crypto/asymmetric_keys/asym_tpm.c @@ -20,9 +20,11 @@ #define TPM_ORD_FLUSHSPECIFIC 186 #define TPM_ORD_LOADKEY2 65 #define TPM_ORD_UNBIND 30 +#define TPM_ORD_SIGN 60 #define TPM_LOADKEY2_SIZE 59 #define TPM_FLUSHSPECIFIC_SIZE 18 #define TPM_UNBIND_SIZE 63 +#define TPM_SIGN_SIZE 63 #define TPM_RT_KEY 0x00000001 @@ -189,6 +191,91 @@ static int tpm_unbind(struct tpm_buf *tb, return datalen; } +/* + * Sign a blob provided by userspace (that has had the hash function applied) + * using a specific key handle. The handle is assumed to have been previously + * loaded by e.g. LoadKey2. + * + * Note that the key signature scheme of the used key should be set to + * TPM_SS_RSASSAPKCS1v15_DER. This allows the hashed input to be of any size + * up to key_length_in_bytes - 11 and not be limited to size 20 like the + * TPM_SS_RSASSAPKCS1v15_SHA1 signature scheme. + */ +static int tpm_sign(struct tpm_buf *tb, + uint32_t keyhandle, unsigned char *keyauth, + const unsigned char *blob, uint32_t bloblen, + void *out, uint32_t outlen) +{ + unsigned char nonceodd[TPM_NONCE_SIZE]; + unsigned char enonce[TPM_NONCE_SIZE]; + unsigned char authdata[SHA1_DIGEST_SIZE]; + uint32_t authhandle = 0; + unsigned char cont = 0; + uint32_t ordinal; + uint32_t datalen; + int ret; + + ordinal = htonl(TPM_ORD_SIGN); + datalen = htonl(bloblen); + + /* session for loading the key */ + ret = oiap(tb, &authhandle, enonce); + if (ret < 0) { + pr_info("oiap failed (%d)\n", ret); + return ret; + } + + /* generate odd nonce */ + ret = tpm_get_random(NULL, nonceodd, TPM_NONCE_SIZE); + if (ret < 0) { + pr_info("tpm_get_random failed (%d)\n", ret); + return ret; + } + + /* calculate authorization HMAC value */ + ret = TSS_authhmac(authdata, keyauth, SHA1_DIGEST_SIZE, enonce, + nonceodd, cont, sizeof(uint32_t), &ordinal, + sizeof(uint32_t), &datalen, + bloblen, blob, 0, 0); + if (ret < 0) + return ret; + + /* build the request buffer */ + INIT_BUF(tb); + store16(tb, TPM_TAG_RQU_AUTH1_COMMAND); + store32(tb, TPM_SIGN_SIZE + bloblen); + store32(tb, TPM_ORD_SIGN); + store32(tb, keyhandle); + store32(tb, bloblen); + storebytes(tb, blob, bloblen); + store32(tb, authhandle); + storebytes(tb, nonceodd, TPM_NONCE_SIZE); + store8(tb, cont); + storebytes(tb, authdata, SHA1_DIGEST_SIZE); + + ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE); + if (ret < 0) { + pr_info("authhmac failed (%d)\n", ret); + return ret; + } + + datalen = LOAD32(tb->data, TPM_DATA_OFFSET); + + ret = TSS_checkhmac1(tb->data, ordinal, nonceodd, + keyauth, SHA1_DIGEST_SIZE, + sizeof(uint32_t), TPM_DATA_OFFSET, + datalen, TPM_DATA_OFFSET + sizeof(uint32_t), + 0, 0); + if (ret < 0) { + pr_info("TSS_checkhmac1 failed (%d)\n", ret); + return ret; + } + + memcpy(out, tb->data + TPM_DATA_OFFSET + sizeof(uint32_t), + min(datalen, outlen)); + + return datalen; +} /* * Maximum buffer size for the BER/DER encoded public key. The public key * is of the form SEQUENCE { INTEGER n, INTEGER e } where n is a maximum 2048 -- cgit v1.2.3 From c7ef39a437ecd8db2f7f3c7b36cbcc1eeb525636 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:49:35 +0100 Subject: KEYS: asym_tpm: Add support for the sign operation [ver #2] The sign operation can operate in a non-hashed mode by running the RSA sign operation directly on the input. This assumes that the input is less than key_size_in_bytes - 11. Since the TPM performs its own PKCS1 padding, it isn't possible to support 'raw' mode, only 'pkcs1'. Alternatively, a hashed version is also possible. In this variant the input is hashed (by userspace) via the selected hash function first. Then this implementation takes care of converting the hash to ASN.1 format and the sign operation is performed on the result. This is similar to the implementation inside crypto/rsa-pkcs1pad.c. ASN1 templates were copied from crypto/rsa-pkcs1pad.c. There seems to be no easy way to expose that functionality, but likely the templates should be shared somehow. The sign operation is implemented via TPM_Sign operation on the TPM. It is assumed that the TPM wrapped key provided uses TPM_SS_RSASSAPKCS1v15_DER signature scheme. This allows the TPM_Sign operation to work on data up to key_len_in_bytes - 11 bytes long. In theory, we could also use TPM_Unbind instead of TPM_Sign, but we would have to manually pkcs1 pad the digest first. Signed-off-by: Denis Kenzior Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- crypto/asymmetric_keys/asym_tpm.c | 156 +++++++++++++++++++++++++++++++++++++- 1 file changed, 155 insertions(+), 1 deletion(-) (limited to 'crypto/asymmetric_keys/asym_tpm.c') diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c index a5a5f913..5d4c2704 100644 --- a/crypto/asymmetric_keys/asym_tpm.c +++ b/crypto/asymmetric_keys/asym_tpm.c @@ -435,7 +435,8 @@ static int tpm_key_query(const struct kernel_pkey_params *params, info->supported_ops = KEYCTL_SUPPORTS_ENCRYPT | KEYCTL_SUPPORTS_DECRYPT | - KEYCTL_SUPPORTS_VERIFY; + KEYCTL_SUPPORTS_VERIFY | + KEYCTL_SUPPORTS_SIGN; ret = 0; error_free_tfm: @@ -556,6 +557,156 @@ error: return r; } +/* + * Hash algorithm OIDs plus ASN.1 DER wrappings [RFC4880 sec 5.2.2]. + */ +static const u8 digest_info_md5[] = { + 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, /* OID */ + 0x05, 0x00, 0x04, 0x10 +}; + +static const u8 digest_info_sha1[] = { + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, + 0x2b, 0x0e, 0x03, 0x02, 0x1a, + 0x05, 0x00, 0x04, 0x14 +}; + +static const u8 digest_info_rmd160[] = { + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, + 0x2b, 0x24, 0x03, 0x02, 0x01, + 0x05, 0x00, 0x04, 0x14 +}; + +static const u8 digest_info_sha224[] = { + 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, + 0x05, 0x00, 0x04, 0x1c +}; + +static const u8 digest_info_sha256[] = { + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, + 0x05, 0x00, 0x04, 0x20 +}; + +static const u8 digest_info_sha384[] = { + 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, + 0x05, 0x00, 0x04, 0x30 +}; + +static const u8 digest_info_sha512[] = { + 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, + 0x05, 0x00, 0x04, 0x40 +}; + +static const struct asn1_template { + const char *name; + const u8 *data; + size_t size; +} asn1_templates[] = { +#define _(X) { #X, digest_info_##X, sizeof(digest_info_##X) } + _(md5), + _(sha1), + _(rmd160), + _(sha256), + _(sha384), + _(sha512), + _(sha224), + { NULL } +#undef _ +}; + +static const struct asn1_template *lookup_asn1(const char *name) +{ + const struct asn1_template *p; + + for (p = asn1_templates; p->name; p++) + if (strcmp(name, p->name) == 0) + return p; + return NULL; +} + +/* + * Sign operation is performed with the private key in the TPM. + */ +static int tpm_key_sign(struct tpm_key *tk, + struct kernel_pkey_params *params, + const void *in, void *out) +{ + struct tpm_buf *tb; + uint32_t keyhandle; + uint8_t srkauth[SHA1_DIGEST_SIZE]; + uint8_t keyauth[SHA1_DIGEST_SIZE]; + void *asn1_wrapped = NULL; + uint32_t in_len = params->in_len; + int r; + + pr_devel("==>%s()\n", __func__); + + if (strcmp(params->encoding, "pkcs1")) + return -ENOPKG; + + if (params->hash_algo) { + const struct asn1_template *asn1 = + lookup_asn1(params->hash_algo); + + if (!asn1) + return -ENOPKG; + + /* request enough space for the ASN.1 template + input hash */ + asn1_wrapped = kzalloc(in_len + asn1->size, GFP_KERNEL); + if (!asn1_wrapped) + return -ENOMEM; + + /* 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; + } + + if (in_len > tk->key_len / 8 - 11) { + r = -EOVERFLOW; + goto error_free_asn1_wrapped; + } + + r = -ENOMEM; + tb = kzalloc(sizeof(*tb), GFP_KERNEL); + if (!tb) + goto error_free_asn1_wrapped; + + /* TODO: Handle a non-all zero SRK authorization */ + memset(srkauth, 0, sizeof(srkauth)); + + r = tpm_loadkey2(tb, SRKHANDLE, srkauth, + tk->blob, tk->blob_len, &keyhandle); + if (r < 0) { + pr_devel("loadkey2 failed (%d)\n", r); + goto error_free_tb; + } + + /* TODO: Handle a non-all zero key authorization */ + memset(keyauth, 0, sizeof(keyauth)); + + r = tpm_sign(tb, keyhandle, keyauth, in, in_len, out, params->out_len); + if (r < 0) + pr_devel("tpm_sign failed (%d)\n", r); + + if (tpm_flushspecific(tb, keyhandle) < 0) + pr_devel("flushspecific failed (%d)\n", r); + +error_free_tb: + kzfree(tb); +error_free_asn1_wrapped: + kfree(asn1_wrapped); + pr_devel("<==%s() = %d\n", __func__, r); + return r; +} + /* * Do encryption, decryption and signing ops. */ @@ -573,6 +724,9 @@ static int tpm_key_eds_op(struct kernel_pkey_params *params, case kernel_pkey_decrypt: ret = tpm_key_decrypt(tk, params, in, out); break; + case kernel_pkey_sign: + ret = tpm_key_sign(tk, params, in, out); + break; default: BUG(); } -- cgit v1.2.3