diff options
| author | Denis Kenzior <denkenz@gmail.com> | 2018-10-09 12:49:20 -0400 |
|---|---|---|
| committer | James Morris <james.morris@microsoft.com> | 2018-10-26 04:30:47 -0400 |
| commit | e08e6891231f5fae82a6ffb4affdfa2ced8c1a77 (patch) | |
| tree | 35766b02094256b30237bfd93656ced38faf11a4 | |
| parent | a335974ae0883e045151a2160093a22aa02c3626 (diff) | |
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 <denkenz@gmail.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Tested-by: Marcel Holtmann <marcel@holtmann.org>
Reviewed-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: James Morris <james.morris@microsoft.com>
| -rw-r--r-- | crypto/asymmetric_keys/asym_tpm.c | 106 |
1 files changed, 100 insertions, 6 deletions
diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c index 6f5d5cf98910..a38ba375675e 100644 --- a/crypto/asymmetric_keys/asym_tpm.c +++ b/crypto/asymmetric_keys/asym_tpm.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include <keys/asymmetric-subtype.h> | 15 | #include <keys/asymmetric-subtype.h> |
| 16 | #include <keys/trusted.h> | 16 | #include <keys/trusted.h> |
| 17 | #include <crypto/asym_tpm_subtype.h> | 17 | #include <crypto/asym_tpm_subtype.h> |
| 18 | #include <crypto/public_key.h> | ||
| 18 | 19 | ||
| 19 | #define TPM_ORD_FLUSHSPECIFIC 186 | 20 | #define TPM_ORD_FLUSHSPECIFIC 186 |
| 20 | #define TPM_ORD_LOADKEY2 65 | 21 | #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) | |||
| 286 | static int determine_akcipher(const char *encoding, const char *hash_algo, | 287 | static int determine_akcipher(const char *encoding, const char *hash_algo, |
| 287 | char alg_name[CRYPTO_MAX_ALG_NAME]) | 288 | char alg_name[CRYPTO_MAX_ALG_NAME]) |
| 288 | { | 289 | { |
| 289 | /* TODO: We don't support hashing yet */ | ||
| 290 | if (hash_algo) | ||
| 291 | return -ENOPKG; | ||
| 292 | |||
| 293 | if (strcmp(encoding, "pkcs1") == 0) { | 290 | if (strcmp(encoding, "pkcs1") == 0) { |
| 294 | strcpy(alg_name, "pkcs1pad(rsa)"); | 291 | if (!hash_algo) { |
| 292 | strcpy(alg_name, "pkcs1pad(rsa)"); | ||
| 293 | return 0; | ||
| 294 | } | ||
| 295 | |||
| 296 | if (snprintf(alg_name, CRYPTO_MAX_ALG_NAME, "pkcs1pad(rsa,%s)", | ||
| 297 | hash_algo) >= CRYPTO_MAX_ALG_NAME) | ||
| 298 | return -EINVAL; | ||
| 299 | |||
| 295 | return 0; | 300 | return 0; |
| 296 | } | 301 | } |
| 297 | 302 | ||
| @@ -342,7 +347,8 @@ static int tpm_key_query(const struct kernel_pkey_params *params, | |||
| 342 | info->max_dec_size = tk->key_len / 8; | 347 | info->max_dec_size = tk->key_len / 8; |
| 343 | 348 | ||
| 344 | info->supported_ops = KEYCTL_SUPPORTS_ENCRYPT | | 349 | info->supported_ops = KEYCTL_SUPPORTS_ENCRYPT | |
| 345 | KEYCTL_SUPPORTS_DECRYPT; | 350 | KEYCTL_SUPPORTS_DECRYPT | |
| 351 | KEYCTL_SUPPORTS_VERIFY; | ||
| 346 | 352 | ||
| 347 | ret = 0; | 353 | ret = 0; |
| 348 | error_free_tfm: | 354 | error_free_tfm: |
| @@ -488,6 +494,93 @@ static int tpm_key_eds_op(struct kernel_pkey_params *params, | |||
| 488 | } | 494 | } |
| 489 | 495 | ||
| 490 | /* | 496 | /* |
| 497 | * Verify a signature using a public key. | ||
| 498 | */ | ||
| 499 | static int tpm_key_verify_signature(const struct key *key, | ||
| 500 | const struct public_key_signature *sig) | ||
| 501 | { | ||
| 502 | const struct tpm_key *tk = key->payload.data[asym_crypto]; | ||
| 503 | struct crypto_wait cwait; | ||
| 504 | struct crypto_akcipher *tfm; | ||
| 505 | struct akcipher_request *req; | ||
| 506 | struct scatterlist sig_sg, digest_sg; | ||
| 507 | char alg_name[CRYPTO_MAX_ALG_NAME]; | ||
| 508 | uint8_t der_pub_key[PUB_KEY_BUF_SIZE]; | ||
| 509 | uint32_t der_pub_key_len; | ||
| 510 | void *output; | ||
| 511 | unsigned int outlen; | ||
| 512 | int ret; | ||
| 513 | |||
| 514 | pr_devel("==>%s()\n", __func__); | ||
| 515 | |||
| 516 | BUG_ON(!tk); | ||
| 517 | BUG_ON(!sig); | ||
| 518 | BUG_ON(!sig->s); | ||
| 519 | |||
| 520 | if (!sig->digest) | ||
| 521 | return -ENOPKG; | ||
| 522 | |||
| 523 | ret = determine_akcipher(sig->encoding, sig->hash_algo, alg_name); | ||
| 524 | if (ret < 0) | ||
| 525 | return ret; | ||
| 526 | |||
| 527 | tfm = crypto_alloc_akcipher(alg_name, 0, 0); | ||
| 528 | if (IS_ERR(tfm)) | ||
| 529 | return PTR_ERR(tfm); | ||
| 530 | |||
| 531 | der_pub_key_len = derive_pub_key(tk->pub_key, tk->pub_key_len, | ||
| 532 | der_pub_key); | ||
| 533 | |||
| 534 | ret = crypto_akcipher_set_pub_key(tfm, der_pub_key, der_pub_key_len); | ||
| 535 | if (ret < 0) | ||
| 536 | goto error_free_tfm; | ||
| 537 | |||
| 538 | ret = -ENOMEM; | ||
| 539 | req = akcipher_request_alloc(tfm, GFP_KERNEL); | ||
| 540 | if (!req) | ||
| 541 | goto error_free_tfm; | ||
| 542 | |||
| 543 | ret = -ENOMEM; | ||
| 544 | outlen = crypto_akcipher_maxsize(tfm); | ||
| 545 | output = kmalloc(outlen, GFP_KERNEL); | ||
| 546 | if (!output) | ||
| 547 | goto error_free_req; | ||
| 548 | |||
| 549 | sg_init_one(&sig_sg, sig->s, sig->s_size); | ||
| 550 | sg_init_one(&digest_sg, output, outlen); | ||
| 551 | akcipher_request_set_crypt(req, &sig_sg, &digest_sg, sig->s_size, | ||
| 552 | outlen); | ||
| 553 | crypto_init_wait(&cwait); | ||
| 554 | akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | | ||
| 555 | CRYPTO_TFM_REQ_MAY_SLEEP, | ||
| 556 | crypto_req_done, &cwait); | ||
| 557 | |||
| 558 | /* Perform the verification calculation. This doesn't actually do the | ||
| 559 | * verification, but rather calculates the hash expected by the | ||
| 560 | * signature and returns that to us. | ||
| 561 | */ | ||
| 562 | ret = crypto_wait_req(crypto_akcipher_verify(req), &cwait); | ||
| 563 | if (ret) | ||
| 564 | goto out_free_output; | ||
| 565 | |||
| 566 | /* Do the actual verification step. */ | ||
| 567 | if (req->dst_len != sig->digest_size || | ||
| 568 | memcmp(sig->digest, output, sig->digest_size) != 0) | ||
| 569 | ret = -EKEYREJECTED; | ||
| 570 | |||
| 571 | out_free_output: | ||
| 572 | kfree(output); | ||
| 573 | error_free_req: | ||
| 574 | akcipher_request_free(req); | ||
| 575 | error_free_tfm: | ||
| 576 | crypto_free_akcipher(tfm); | ||
| 577 | pr_devel("<==%s() = %d\n", __func__, ret); | ||
| 578 | if (WARN_ON_ONCE(ret > 0)) | ||
| 579 | ret = -EINVAL; | ||
| 580 | return ret; | ||
| 581 | } | ||
| 582 | |||
| 583 | /* | ||
| 491 | * Parse enough information out of TPM_KEY structure: | 584 | * Parse enough information out of TPM_KEY structure: |
| 492 | * TPM_STRUCT_VER -> 4 bytes | 585 | * TPM_STRUCT_VER -> 4 bytes |
| 493 | * TPM_KEY_USAGE -> 2 bytes | 586 | * TPM_KEY_USAGE -> 2 bytes |
| @@ -645,6 +738,7 @@ struct asymmetric_key_subtype asym_tpm_subtype = { | |||
| 645 | .destroy = asym_tpm_destroy, | 738 | .destroy = asym_tpm_destroy, |
| 646 | .query = tpm_key_query, | 739 | .query = tpm_key_query, |
| 647 | .eds_op = tpm_key_eds_op, | 740 | .eds_op = tpm_key_eds_op, |
| 741 | .verify_signature = tpm_key_verify_signature, | ||
| 648 | }; | 742 | }; |
| 649 | EXPORT_SYMBOL_GPL(asym_tpm_subtype); | 743 | EXPORT_SYMBOL_GPL(asym_tpm_subtype); |
| 650 | 744 | ||
