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: Vitaly Chikunov <vt@altlinux.org> Reviewed-by: Denis Kenzior <denkenz@gmail.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
parent
3ecc972599
commit
c7381b0128
|
@ -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,
|
||||||
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. */
|
kfree(digest);
|
||||||
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:
|
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,
|
||||||
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,
|
||||||
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. */
|
kfree(digest);
|
||||||
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:
|
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)
|
||||||
|
|
||||||
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)
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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);
|
||||||
sg_init_one(&dst, outbuf_enc, out_len_max);
|
if (vecs->siggen_sigver_test) {
|
||||||
akcipher_request_set_crypt(req, src_tab, &dst, m_size,
|
if (WARN_ON(c_size > PAGE_SIZE))
|
||||||
out_len_max);
|
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);
|
||||||
|
akcipher_request_set_crypt(req, src_tab, &dst, m_size,
|
||||||
|
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,18 +2675,21 @@ 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 (req->dst_len != c_size) {
|
if (!vecs->siggen_sigver_test) {
|
||||||
pr_err("alg: akcipher: %s test failed. Invalid output len\n",
|
if (req->dst_len != c_size) {
|
||||||
op);
|
pr_err("alg: akcipher: %s test failed. Invalid output len\n",
|
||||||
err = -EINVAL;
|
op);
|
||||||
goto free_all;
|
err = -EINVAL;
|
||||||
}
|
goto free_all;
|
||||||
/* verify that encrypted message is equal to expected */
|
}
|
||||||
if (memcmp(c, outbuf_enc, c_size)) {
|
/* verify that encrypted message is equal to expected */
|
||||||
pr_err("alg: akcipher: %s test failed. Invalid output\n", op);
|
if (memcmp(c, outbuf_enc, c_size) != 0) {
|
||||||
hexdump(outbuf_enc, c_size);
|
pr_err("alg: akcipher: %s test failed. Invalid output\n",
|
||||||
err = -EINVAL;
|
op);
|
||||||
goto free_all;
|
hexdump(outbuf_enc, c_size);
|
||||||
|
err = -EINVAL;
|
||||||
|
goto free_all;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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 {
|
||||||
* 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,
|
||||||
*
|
*
|
||||||
* @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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue