KEYS: asym_tpm: Implement encryption operation [ver #2]
authorDenis Kenzior <denkenz@gmail.com>
Tue, 9 Oct 2018 16:48:33 +0000 (17:48 +0100)
committerJames Morris <james.morris@microsoft.com>
Fri, 26 Oct 2018 08:30:47 +0000 (09:30 +0100)
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 <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>
crypto/asymmetric_keys/asym_tpm.c

index 837472d107d5f9f4bc02c67e6312c991e60acab0..8edca3c4c193ca116010407643fe6c71d2edac39 100644 (file)
@@ -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);