Commit c7381b01 authored by Vitaly Chikunov's avatar Vitaly Chikunov Committed by Herbert Xu

crypto: akcipher - new verify API for public key algorithms

Previous akcipher .verify() just `decrypts' (using RSA encrypt which is
using public key) signature to uncover message hash, which was then
compared in upper level public_key_verify_signature() with the expected
hash value, which itself was never passed into verify().

This approach was incompatible with EC-DSA family of algorithms,
because, to verify a signature EC-DSA algorithm also needs a hash value
as input; then it's used (together with a signature divided into halves
`r||s') to produce a witness value, which is then compared with `r' to
determine if the signature is correct. Thus, for EC-DSA, nor
requirements of .verify() itself, nor its output expectations in
public_key_verify_signature() wasn't sufficient.

Make improved .verify() call which gets hash value as input and produce
complete signature check without any output besides status.

Now for the top level verification only crypto_akcipher_verify() needs
to be called and its return value inspected.

Make sure that `digest' is in kmalloc'd memory (in place of `output`) in
{public,tpm}_key_verify_signature() as insisted by Herbert Xu, and will
be changed in the following commit.

Cc: David Howells <dhowells@redhat.com>
Cc: keyrings@vger.kernel.org
Signed-off-by: default avatarVitaly Chikunov <vt@altlinux.org>
Reviewed-by: default avatarDenis Kenzior <denkenz@gmail.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent 3ecc9725
...@@ -744,12 +744,11 @@ static int tpm_key_verify_signature(const struct key *key, ...@@ -744,12 +744,11 @@ static int tpm_key_verify_signature(const struct key *key,
struct crypto_wait cwait; struct crypto_wait cwait;
struct crypto_akcipher *tfm; struct crypto_akcipher *tfm;
struct akcipher_request *req; struct akcipher_request *req;
struct scatterlist sig_sg, digest_sg; struct scatterlist src_sg[2];
char alg_name[CRYPTO_MAX_ALG_NAME]; char alg_name[CRYPTO_MAX_ALG_NAME];
uint8_t der_pub_key[PUB_KEY_BUF_SIZE]; uint8_t der_pub_key[PUB_KEY_BUF_SIZE];
uint32_t der_pub_key_len; uint32_t der_pub_key_len;
void *output; void *digest;
unsigned int outlen;
int ret; int ret;
pr_devel("==>%s()\n", __func__); pr_devel("==>%s()\n", __func__);
...@@ -782,35 +781,22 @@ static int tpm_key_verify_signature(const struct key *key, ...@@ -782,35 +781,22 @@ static int tpm_key_verify_signature(const struct key *key,
goto error_free_tfm; goto error_free_tfm;
ret = -ENOMEM; ret = -ENOMEM;
outlen = crypto_akcipher_maxsize(tfm); digest = kmemdup(sig->digest, sig->digest_size, GFP_KERNEL);
output = kmalloc(outlen, GFP_KERNEL); if (!digest)
if (!output)
goto error_free_req; goto error_free_req;
sg_init_one(&sig_sg, sig->s, sig->s_size); sg_init_table(src_sg, 2);
sg_init_one(&digest_sg, output, outlen); sg_set_buf(&src_sg[0], sig->s, sig->s_size);
akcipher_request_set_crypt(req, &sig_sg, &digest_sg, sig->s_size, sg_set_buf(&src_sg[1], digest, sig->digest_size);
outlen); akcipher_request_set_crypt(req, src_sg, NULL, sig->s_size,
sig->digest_size);
crypto_init_wait(&cwait); crypto_init_wait(&cwait);
akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
CRYPTO_TFM_REQ_MAY_SLEEP, CRYPTO_TFM_REQ_MAY_SLEEP,
crypto_req_done, &cwait); 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); 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(digest);
kfree(output);
error_free_req: error_free_req:
akcipher_request_free(req); akcipher_request_free(req);
error_free_tfm: error_free_tfm:
......
...@@ -227,10 +227,9 @@ int public_key_verify_signature(const struct public_key *pkey, ...@@ -227,10 +227,9 @@ int public_key_verify_signature(const struct public_key *pkey,
struct crypto_wait cwait; struct crypto_wait cwait;
struct crypto_akcipher *tfm; struct crypto_akcipher *tfm;
struct akcipher_request *req; struct akcipher_request *req;
struct scatterlist sig_sg, digest_sg; struct scatterlist src_sg[2];
char alg_name[CRYPTO_MAX_ALG_NAME]; char alg_name[CRYPTO_MAX_ALG_NAME];
void *output; void *digest;
unsigned int outlen;
int ret; int ret;
pr_devel("==>%s()\n", __func__); pr_devel("==>%s()\n", __func__);
...@@ -264,35 +263,22 @@ int public_key_verify_signature(const struct public_key *pkey, ...@@ -264,35 +263,22 @@ int public_key_verify_signature(const struct public_key *pkey,
goto error_free_req; goto error_free_req;
ret = -ENOMEM; ret = -ENOMEM;
outlen = crypto_akcipher_maxsize(tfm); digest = kmemdup(sig->digest, sig->digest_size, GFP_KERNEL);
output = kmalloc(outlen, GFP_KERNEL); if (!digest)
if (!output)
goto error_free_req; goto error_free_req;
sg_init_one(&sig_sg, sig->s, sig->s_size); sg_init_table(src_sg, 2);
sg_init_one(&digest_sg, output, outlen); sg_set_buf(&src_sg[0], sig->s, sig->s_size);
akcipher_request_set_crypt(req, &sig_sg, &digest_sg, sig->s_size, sg_set_buf(&src_sg[1], digest, sig->digest_size);
outlen); akcipher_request_set_crypt(req, src_sg, NULL, sig->s_size,
sig->digest_size);
crypto_init_wait(&cwait); crypto_init_wait(&cwait);
akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
CRYPTO_TFM_REQ_MAY_SLEEP, CRYPTO_TFM_REQ_MAY_SLEEP,
crypto_req_done, &cwait); 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); 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(digest);
kfree(output);
error_free_req: error_free_req:
akcipher_request_free(req); akcipher_request_free(req);
error_free_tfm: error_free_tfm:
......
...@@ -488,14 +488,21 @@ static int pkcs1pad_verify_complete(struct akcipher_request *req, int err) ...@@ -488,14 +488,21 @@ static int pkcs1pad_verify_complete(struct akcipher_request *req, int err)
err = 0; err = 0;
if (req->dst_len < dst_len - pos) if (req->dst_len != dst_len - pos) {
err = -EOVERFLOW; err = -EKEYREJECTED;
req->dst_len = dst_len - pos; req->dst_len = dst_len - pos;
goto done;
if (!err) }
sg_copy_from_buffer(req->dst, /* Extract appended digest. */
sg_nents_for_len(req->dst, req->dst_len), sg_pcopy_to_buffer(req->src,
out_buf + pos, req->dst_len); sg_nents_for_len(req->src,
req->src_len + req->dst_len),
req_ctx->out_buf + ctx->key_size,
req->dst_len, ctx->key_size);
/* Do the actual verification step. */
if (memcmp(req_ctx->out_buf + ctx->key_size, out_buf + pos,
req->dst_len) != 0)
err = -EKEYREJECTED;
done: done:
kzfree(req_ctx->out_buf); kzfree(req_ctx->out_buf);
...@@ -532,10 +539,12 @@ static int pkcs1pad_verify(struct akcipher_request *req) ...@@ -532,10 +539,12 @@ static int pkcs1pad_verify(struct akcipher_request *req)
struct pkcs1pad_request *req_ctx = akcipher_request_ctx(req); struct pkcs1pad_request *req_ctx = akcipher_request_ctx(req);
int err; int err;
if (!ctx->key_size || req->src_len < ctx->key_size) if (WARN_ON(req->dst) ||
WARN_ON(!req->dst_len) ||
!ctx->key_size || req->src_len < ctx->key_size)
return -EINVAL; return -EINVAL;
req_ctx->out_buf = kmalloc(ctx->key_size, GFP_KERNEL); req_ctx->out_buf = kmalloc(ctx->key_size + req->dst_len, GFP_KERNEL);
if (!req_ctx->out_buf) if (!req_ctx->out_buf)
return -ENOMEM; return -ENOMEM;
......
...@@ -2595,7 +2595,7 @@ static int test_akcipher_one(struct crypto_akcipher *tfm, ...@@ -2595,7 +2595,7 @@ static int test_akcipher_one(struct crypto_akcipher *tfm,
struct crypto_wait wait; struct crypto_wait wait;
unsigned int out_len_max, out_len = 0; unsigned int out_len_max, out_len = 0;
int err = -ENOMEM; int err = -ENOMEM;
struct scatterlist src, dst, src_tab[2]; struct scatterlist src, dst, src_tab[3];
const char *m, *c; const char *m, *c;
unsigned int m_size, c_size; unsigned int m_size, c_size;
const char *op; const char *op;
...@@ -2618,13 +2618,12 @@ static int test_akcipher_one(struct crypto_akcipher *tfm, ...@@ -2618,13 +2618,12 @@ static int test_akcipher_one(struct crypto_akcipher *tfm,
if (err) if (err)
goto free_req; goto free_req;
err = -ENOMEM;
out_len_max = crypto_akcipher_maxsize(tfm);
/* /*
* First run test which do not require a private key, such as * First run test which do not require a private key, such as
* encrypt or verify. * encrypt or verify.
*/ */
err = -ENOMEM;
out_len_max = crypto_akcipher_maxsize(tfm);
outbuf_enc = kzalloc(out_len_max, GFP_KERNEL); outbuf_enc = kzalloc(out_len_max, GFP_KERNEL);
if (!outbuf_enc) if (!outbuf_enc)
goto free_req; goto free_req;
...@@ -2650,12 +2649,20 @@ static int test_akcipher_one(struct crypto_akcipher *tfm, ...@@ -2650,12 +2649,20 @@ static int test_akcipher_one(struct crypto_akcipher *tfm,
goto free_all; goto free_all;
memcpy(xbuf[0], m, m_size); memcpy(xbuf[0], m, m_size);
sg_init_table(src_tab, 2); sg_init_table(src_tab, 3);
sg_set_buf(&src_tab[0], xbuf[0], 8); sg_set_buf(&src_tab[0], xbuf[0], 8);
sg_set_buf(&src_tab[1], xbuf[0] + 8, m_size - 8); sg_set_buf(&src_tab[1], xbuf[0] + 8, m_size - 8);
if (vecs->siggen_sigver_test) {
if (WARN_ON(c_size > PAGE_SIZE))
goto free_all;
memcpy(xbuf[1], c, c_size);
sg_set_buf(&src_tab[2], xbuf[1], c_size);
akcipher_request_set_crypt(req, src_tab, NULL, m_size, c_size);
} else {
sg_init_one(&dst, outbuf_enc, out_len_max); sg_init_one(&dst, outbuf_enc, out_len_max);
akcipher_request_set_crypt(req, src_tab, &dst, m_size, akcipher_request_set_crypt(req, src_tab, &dst, m_size,
out_len_max); out_len_max);
}
akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
crypto_req_done, &wait); crypto_req_done, &wait);
...@@ -2668,6 +2675,7 @@ static int test_akcipher_one(struct crypto_akcipher *tfm, ...@@ -2668,6 +2675,7 @@ static int test_akcipher_one(struct crypto_akcipher *tfm,
pr_err("alg: akcipher: %s test failed. err %d\n", op, err); pr_err("alg: akcipher: %s test failed. err %d\n", op, err);
goto free_all; goto free_all;
} }
if (!vecs->siggen_sigver_test) {
if (req->dst_len != c_size) { if (req->dst_len != c_size) {
pr_err("alg: akcipher: %s test failed. Invalid output len\n", pr_err("alg: akcipher: %s test failed. Invalid output len\n",
op); op);
...@@ -2675,12 +2683,14 @@ static int test_akcipher_one(struct crypto_akcipher *tfm, ...@@ -2675,12 +2683,14 @@ static int test_akcipher_one(struct crypto_akcipher *tfm,
goto free_all; goto free_all;
} }
/* verify that encrypted message is equal to expected */ /* verify that encrypted message is equal to expected */
if (memcmp(c, outbuf_enc, c_size)) { if (memcmp(c, outbuf_enc, c_size) != 0) {
pr_err("alg: akcipher: %s test failed. Invalid output\n", op); pr_err("alg: akcipher: %s test failed. Invalid output\n",
op);
hexdump(outbuf_enc, c_size); hexdump(outbuf_enc, c_size);
err = -EINVAL; err = -EINVAL;
goto free_all; goto free_all;
} }
}
/* /*
* Don't invoke (decrypt or sign) test which require a private key * Don't invoke (decrypt or sign) test which require a private key
......
...@@ -19,14 +19,20 @@ ...@@ -19,14 +19,20 @@
* *
* @base: Common attributes for async crypto requests * @base: Common attributes for async crypto requests
* @src: Source data * @src: Source data
* @dst: Destination data * For verify op this is signature + digest, in that case
* total size of @src is @src_len + @dst_len.
* @dst: Destination data (Should be NULL for verify op)
* @src_len: Size of the input buffer * @src_len: Size of the input buffer
* @dst_len: Size of the output buffer. It needs to be at least * For verify op it's size of signature part of @src, this part
* as big as the expected result depending on the operation * is supposed to be operated by cipher.
* @dst_len: Size of @dst buffer (for all ops except verify).
* It needs to be at least as big as the expected result
* depending on the operation.
* After operation it will be updated with the actual size of the * After operation it will be updated with the actual size of the
* result. * result.
* In case of error where the dst sgl size was insufficient, * In case of error where the dst sgl size was insufficient,
* it will be updated to the size required for the operation. * it will be updated to the size required for the operation.
* For verify op this is size of digest part in @src.
* @__ctx: Start of private context data * @__ctx: Start of private context data
*/ */
struct akcipher_request { struct akcipher_request {
...@@ -55,10 +61,9 @@ struct crypto_akcipher { ...@@ -55,10 +61,9 @@ struct crypto_akcipher {
* algorithm. In case of error, where the dst_len was insufficient, * algorithm. In case of error, where the dst_len was insufficient,
* the req->dst_len will be updated to the size required for the * the req->dst_len will be updated to the size required for the
* operation * operation
* @verify: Function performs a sign operation as defined by public key * @verify: Function performs a complete verify operation as defined by
* algorithm. In case of error, where the dst_len was insufficient, * public key algorithm, returning verification status. Requires
* the req->dst_len will be updated to the size required for the * digest value as input parameter.
* operation
* @encrypt: Function performs an encrypt operation as defined by public key * @encrypt: Function performs an encrypt operation as defined by public key
* algorithm. In case of error, where the dst_len was insufficient, * algorithm. In case of error, where the dst_len was insufficient,
* the req->dst_len will be updated to the size required for the * the req->dst_len will be updated to the size required for the
...@@ -238,9 +243,10 @@ static inline void akcipher_request_set_callback(struct akcipher_request *req, ...@@ -238,9 +243,10 @@ static inline void akcipher_request_set_callback(struct akcipher_request *req,
* *
* @req: public key request * @req: public key request
* @src: ptr to input scatter list * @src: ptr to input scatter list
* @dst: ptr to output scatter list * @dst: ptr to output scatter list or NULL for verify op
* @src_len: size of the src input scatter list to be processed * @src_len: size of the src input scatter list to be processed
* @dst_len: size of the dst output scatter list * @dst_len: size of the dst output scatter list or size of signature
* portion in @src for verify op
*/ */
static inline void akcipher_request_set_crypt(struct akcipher_request *req, static inline void akcipher_request_set_crypt(struct akcipher_request *req,
struct scatterlist *src, struct scatterlist *src,
...@@ -343,14 +349,18 @@ static inline int crypto_akcipher_sign(struct akcipher_request *req) ...@@ -343,14 +349,18 @@ static inline int crypto_akcipher_sign(struct akcipher_request *req)
} }
/** /**
* crypto_akcipher_verify() - Invoke public key verify operation * crypto_akcipher_verify() - Invoke public key signature verification
* *
* Function invokes the specific public key verify operation for a given * Function invokes the specific public key signature verification operation
* public key algorithm * for a given public key algorithm.
* *
* @req: asymmetric key request * @req: asymmetric key request
* *
* Return: zero on success; error code in case of error * Note: req->dst should be NULL, req->src should point to SG of size
* (req->src_size + req->dst_size), containing signature (of req->src_size
* length) with appended digest (of req->dst_size length).
*
* Return: zero on verification success; error code in case of error.
*/ */
static inline int crypto_akcipher_verify(struct akcipher_request *req) static inline int crypto_akcipher_verify(struct akcipher_request *req)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment