diff mbox series

[v5,04/10] crypto: akcipher - new verify API for public key algorithms

Message ID 20190224060828.2527-5-vt@altlinux.org (mailing list archive)
State Changes Requested
Delegated to: Herbert Xu
Headers show
Series crypto: add EC-RDSA (GOST 34.10) algorithm | expand

Commit Message

Vitaly Chikunov Feb. 24, 2019, 6:08 a.m. UTC
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.

Cc: David Howells <dhowells@redhat.com>
Cc: keyrings@vger.kernel.org
Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
---
 crypto/asymmetric_keys/asym_tpm.c   | 34 ++++++++-----------------
 crypto/asymmetric_keys/public_key.c | 34 ++++++++-----------------
 crypto/rsa-pkcs1pad.c               | 29 +++++++++++++--------
 crypto/testmgr.c                    | 50 ++++++++++++++++++++++---------------
 include/crypto/akcipher.h           | 40 ++++++++++++++++++-----------
 5 files changed, 95 insertions(+), 92 deletions(-)

Comments

Mimi Zohar Feb. 27, 2019, 11:28 p.m. UTC | #1
Hi Vitaly,

On Sun, 2019-02-24 at 09:08 +0300, Vitaly Chikunov wrote:
> 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.

All but this patch apply.  Which branch is this patch based against?

Mimi
Vitaly Chikunov Feb. 28, 2019, 5:37 a.m. UTC | #2
On Wed, Feb 27, 2019 at 06:28:37PM -0500, Mimi Zohar wrote:
> 
> On Sun, 2019-02-24 at 09:08 +0300, Vitaly Chikunov wrote:
> > 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.
> 
> All but this patch apply.  Which branch is this patch based against?

This patchest is over 920d7f7215d87005beb4aa2b90b9cb0b74b36947 in
cryptodev/master.

> 
> Mimi
David Howells Feb. 28, 2019, 6:18 p.m. UTC | #3
Vitaly Chikunov <vt@altlinux.org> wrote:

> +	digest = kmemdup(sig->digest, sig->digest_size, GFP_KERNEL);
> +	if (!digest)

You shouldn't need to copy the digest.  It's being passed in to the crypto
algorithm, not extracted out.

> +		if (memcmp(c, outbuf_enc, c_size)) {

Please use == 0 and != 0 with memcmp() and strcmp().  Their return values are
kind of inverted in sense if you treat them as boolean.

David
Vitaly Chikunov Feb. 28, 2019, 6:39 p.m. UTC | #4
David,

On Thu, Feb 28, 2019 at 06:18:54PM +0000, David Howells wrote:
> Vitaly Chikunov <vt@altlinux.org> wrote:
> 
> > +	digest = kmemdup(sig->digest, sig->digest_size, GFP_KERNEL);
> > +	if (!digest)
> 
> You shouldn't need to copy the digest.  It's being passed in to the crypto
> algorithm, not extracted out.

That's how Herbert suggested to do it. Quoting FYI:

| On Fri, Feb 01, 2019 at 02:26:55PM +0800, Herbert Xu wrote:
| > On Fri, Jan 25, 2019 at 09:01:16PM +0300, Vitaly Chikunov wrote:
| > >
| > > @@ -781,36 +780,17 @@ static int tpm_key_verify_signature(const struct key *key,
| > >     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);
| > > +   sg_init_table(&src_sg, 2);
| > > +   sg_set_buf(&src_sg[0], sig->s, sig->s_size);
| > > +   sg_set_buf(&src_sg[1], sig->digest, sig->digest_size);
| > > +   akcipher_request_set_crypt(req, &src_sg, NULL, sig->s_size,
| > > +                              sig->digest_size);
| >
| > It's not clear that sig->digest is guaranteed to be kmalloc memory.
| > In any case, it's best not to mix unrelated changes in a single
| > patch.  So please keep the kmalloc on output and then copy
| > sig->digest into it and put output into the SG list.


> > +		if (memcmp(c, outbuf_enc, c_size)) {
> 
> Please use == 0 and != 0 with memcmp() and strcmp().  Their return values are
> kind of inverted in sense if you treat them as boolean.

OK.

Thanks!
David Howells Feb. 28, 2019, 7:02 p.m. UTC | #5
| > It's not clear that sig->digest is guaranteed to be kmalloc memory.

Well, public_key_signature_free() will go bang if it's not kfree'able.

David
Vitaly Chikunov Feb. 28, 2019, 7:07 p.m. UTC | #6
David,

On Thu, Feb 28, 2019 at 07:02:09PM +0000, David Howells wrote:
> | > It's not clear that sig->digest is guaranteed to be kmalloc memory.
> 
> Well, public_key_signature_free() will go bang if it's not kfree'able.

Well, I had similar argument, FYI:

| On Fri, Feb 01, 2019 at 10:09:23AM +0300, Vitaly Chikunov wrote:
| > On Fri, Feb 01, 2019 at 02:26:55PM +0800, Herbert Xu wrote:
| > >
| > > It's not clear that sig->digest is guaranteed to be kmalloc memory.
| > > In any case, it's best not to mix unrelated changes in a single
| > > patch.  So please keep the kmalloc on output and then copy
| > > sig->digest into it and put output into the SG list.
| >
| > It is not guaranteed that sig->s will be kmalloc memory either. (Except
| > we know it for sure like we know the same about sig->digest).
| >
| > You can see in public_key_signature_free() that both fields are kfree'd
| > together.
| >
| > So, I don't understand why we should treat sig->digest differently than
| > sig->s.
| >
| > I was just removing kmalloc'ed output as crypto_akcipher_verify() does
| > not need any output anymore. So, it's not some sort of mixing unrelated
| > changes, from my point of view.

But then I thought Herbert knows better and implemented his suggestion.

Now I have contradictory requests from two maintainers.
Herbert Xu March 1, 2019, 2:34 a.m. UTC | #7
On Thu, Feb 28, 2019 at 10:07:42PM +0300, Vitaly Chikunov wrote:
> David,
> 
> On Thu, Feb 28, 2019 at 07:02:09PM +0000, David Howells wrote:
> > | > It's not clear that sig->digest is guaranteed to be kmalloc memory.
> > 
> > Well, public_key_signature_free() will go bang if it's not kfree'able.
> 
> Well, I had similar argument, FYI:
> 
> | On Fri, Feb 01, 2019 at 10:09:23AM +0300, Vitaly Chikunov wrote:
> | > On Fri, Feb 01, 2019 at 02:26:55PM +0800, Herbert Xu wrote:
> | > >
> | > > It's not clear that sig->digest is guaranteed to be kmalloc memory.
> | > > In any case, it's best not to mix unrelated changes in a single
> | > > patch.  So please keep the kmalloc on output and then copy
> | > > sig->digest into it and put output into the SG list.
> | >
> | > It is not guaranteed that sig->s will be kmalloc memory either. (Except
> | > we know it for sure like we know the same about sig->digest).
> | >
> | > You can see in public_key_signature_free() that both fields are kfree'd
> | > together.
> | >
> | > So, I don't understand why we should treat sig->digest differently than
> | > sig->s.
> | >
> | > I was just removing kmalloc'ed output as crypto_akcipher_verify() does
> | > not need any output anymore. So, it's not some sort of mixing unrelated
> | > changes, from my point of view.
> 
> But then I thought Herbert knows better and implemented his suggestion.
> 
> Now I have contradictory requests from two maintainers.

My main point is to not include unrelated changes into your patch.
If you want to eliminate the copy that's fine, but please do it in
a separate patch.

Cheers,
diff mbox series

Patch

diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c
index 5d4c270463f6..4e5b6fb57a94 100644
--- a/crypto/asymmetric_keys/asym_tpm.c
+++ b/crypto/asymmetric_keys/asym_tpm.c
@@ -744,12 +744,11 @@  static int tpm_key_verify_signature(const struct key *key,
 	struct crypto_wait cwait;
 	struct crypto_akcipher *tfm;
 	struct akcipher_request *req;
-	struct scatterlist sig_sg, digest_sg;
+	struct scatterlist src_sg[2];
 	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;
+	void *digest;
 	int ret;
 
 	pr_devel("==>%s()\n", __func__);
@@ -782,35 +781,22 @@  static int tpm_key_verify_signature(const struct key *key,
 		goto error_free_tfm;
 
 	ret = -ENOMEM;
-	outlen = crypto_akcipher_maxsize(tfm);
-	output = kmalloc(outlen, GFP_KERNEL);
-	if (!output)
+	digest = kmemdup(sig->digest, sig->digest_size, GFP_KERNEL);
+	if (!digest)
 		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);
+	sg_init_table(src_sg, 2);
+	sg_set_buf(&src_sg[0], sig->s, sig->s_size);
+	sg_set_buf(&src_sg[1], digest, sig->digest_size);
+	akcipher_request_set_crypt(req, src_sg, NULL, sig->s_size,
+				   sig->digest_size);
 	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);
+	kfree(digest);
 error_free_req:
 	akcipher_request_free(req);
 error_free_tfm:
diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
index c2e4e73fcf06..338f2b5352b1 100644
--- a/crypto/asymmetric_keys/public_key.c
+++ b/crypto/asymmetric_keys/public_key.c
@@ -233,10 +233,9 @@  int public_key_verify_signature(const struct public_key *pkey,
 	struct crypto_wait cwait;
 	struct crypto_akcipher *tfm;
 	struct akcipher_request *req;
-	struct scatterlist sig_sg, digest_sg;
+	struct scatterlist src_sg[2];
 	char alg_name[CRYPTO_MAX_ALG_NAME];
-	void *output;
-	unsigned int outlen;
+	void *digest;
 	int ret;
 
 	pr_devel("==>%s()\n", __func__);
@@ -270,35 +269,22 @@  int public_key_verify_signature(const struct public_key *pkey,
 		goto error_free_req;
 
 	ret = -ENOMEM;
-	outlen = crypto_akcipher_maxsize(tfm);
-	output = kmalloc(outlen, GFP_KERNEL);
-	if (!output)
+	digest = kmemdup(sig->digest, sig->digest_size, GFP_KERNEL);
+	if (!digest)
 		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);
+	sg_init_table(src_sg, 2);
+	sg_set_buf(&src_sg[0], sig->s, sig->s_size);
+	sg_set_buf(&src_sg[1], digest, sig->digest_size);
+	akcipher_request_set_crypt(req, src_sg, NULL, sig->s_size,
+				   sig->digest_size);
 	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);
+	kfree(digest);
 error_free_req:
 	akcipher_request_free(req);
 error_free_tfm:
diff --git a/crypto/rsa-pkcs1pad.c b/crypto/rsa-pkcs1pad.c
index 94382fa2c6ac..f2fa1ce90cfa 100644
--- a/crypto/rsa-pkcs1pad.c
+++ b/crypto/rsa-pkcs1pad.c
@@ -488,14 +488,21 @@  static int pkcs1pad_verify_complete(struct akcipher_request *req, int err)
 
 	err = 0;
 
-	if (req->dst_len < dst_len - pos)
-		err = -EOVERFLOW;
-	req->dst_len = dst_len - pos;
-
-	if (!err)
-		sg_copy_from_buffer(req->dst,
-				sg_nents_for_len(req->dst, req->dst_len),
-				out_buf + pos, req->dst_len);
+	if (req->dst_len != dst_len - pos) {
+		err = -EKEYREJECTED;
+		req->dst_len = dst_len - pos;
+		goto done;
+	}
+	/* Extract appended digest. */
+	sg_pcopy_to_buffer(req->src,
+			   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))
+		err = -EKEYREJECTED;
 done:
 	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);
 	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;
 
-	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)
 		return -ENOMEM;
 
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index d582a2758feb..924a1b9cf5ca 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -2501,7 +2501,7 @@  static int test_akcipher_one(struct crypto_akcipher *tfm,
 	struct crypto_wait wait;
 	unsigned int out_len_max, out_len = 0;
 	int err = -ENOMEM;
-	struct scatterlist src, dst, src_tab[2];
+	struct scatterlist src, dst, src_tab[3];
 	const char *m, *c;
 	unsigned int m_size, c_size;
 	const char *op;
@@ -2524,13 +2524,12 @@  static int test_akcipher_one(struct crypto_akcipher *tfm,
 	if (err)
 		goto free_req;
 
-	err = -ENOMEM;
-	out_len_max = crypto_akcipher_maxsize(tfm);
-
 	/*
 	 * First run test which do not require a private key, such as
 	 * encrypt or verify.
 	 */
+	err = -ENOMEM;
+	out_len_max = crypto_akcipher_maxsize(tfm);
 	outbuf_enc = kzalloc(out_len_max, GFP_KERNEL);
 	if (!outbuf_enc)
 		goto free_req;
@@ -2556,12 +2555,20 @@  static int test_akcipher_one(struct crypto_akcipher *tfm,
 		goto free_all;
 	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[1], xbuf[0] + 8, m_size - 8);
-	sg_init_one(&dst, outbuf_enc, out_len_max);
-	akcipher_request_set_crypt(req, src_tab, &dst, m_size,
-				   out_len_max);
+	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);
+		akcipher_request_set_crypt(req, src_tab, &dst, m_size,
+					   out_len_max);
+	}
 	akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
 				      crypto_req_done, &wait);
 
@@ -2574,18 +2581,21 @@  static int test_akcipher_one(struct crypto_akcipher *tfm,
 		pr_err("alg: akcipher: %s test failed. err %d\n", op, err);
 		goto free_all;
 	}
-	if (req->dst_len != c_size) {
-		pr_err("alg: akcipher: %s test failed. Invalid output len\n",
-		       op);
-		err = -EINVAL;
-		goto free_all;
-	}
-	/* verify that encrypted message is equal to expected */
-	if (memcmp(c, outbuf_enc, c_size)) {
-		pr_err("alg: akcipher: %s test failed. Invalid output\n", op);
-		hexdump(outbuf_enc, c_size);
-		err = -EINVAL;
-		goto free_all;
+	if (!vecs->siggen_sigver_test) {
+		if (req->dst_len != c_size) {
+			pr_err("alg: akcipher: %s test failed. Invalid output len\n",
+			       op);
+			err = -EINVAL;
+			goto free_all;
+		}
+		/* verify that encrypted message is equal to expected */
+		if (memcmp(c, outbuf_enc, c_size)) {
+			pr_err("alg: akcipher: %s test failed. Invalid output\n",
+			       op);
+			hexdump(outbuf_enc, c_size);
+			err = -EINVAL;
+			goto free_all;
+		}
 	}
 
 	/*
diff --git a/include/crypto/akcipher.h b/include/crypto/akcipher.h
index f537fad1989f..28ffa9ef03a9 100644
--- a/include/crypto/akcipher.h
+++ b/include/crypto/akcipher.h
@@ -19,14 +19,20 @@ 
  *
  * @base:	Common attributes for async crypto requests
  * @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
- * @dst_len:	Size of the output buffer. It needs to be at least
- *		as big as the expected result depending	on the operation
+ *		For verify op it's size of signature part of @src, this part
+ *		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
  *		result.
  *		In case of error where the dst sgl size was insufficient,
  *		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
  */
 struct akcipher_request {
@@ -55,10 +61,9 @@  struct crypto_akcipher {
  *		algorithm. In case of error, where the dst_len was insufficient,
  *		the req->dst_len will be updated to the size required for the
  *		operation
- * @verify:	Function performs a sign operation as defined by public key
- *		algorithm. In case of error, where the dst_len was insufficient,
- *		the req->dst_len will be updated to the size required for the
- *		operation
+ * @verify:	Function performs a complete verify operation as defined by
+ *		public key algorithm, returning verification status. Requires
+ *		digest value as input parameter.
  * @encrypt:	Function performs an encrypt operation as defined by public key
  *		algorithm. In case of error, where the dst_len was insufficient,
  *		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
  * @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
- * @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,
 					      struct scatterlist *src,
@@ -349,14 +355,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
- * public key algorithm
+ * Function invokes the specific public key signature verification operation
+ * for a given public key algorithm.
  *
  * @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)
 {
@@ -366,7 +376,9 @@  static inline int crypto_akcipher_verify(struct akcipher_request *req)
 	int ret = -ENOSYS;
 
 	crypto_stats_get(calg);
-	if (alg->verify)
+	if (req->dst || !req->dst_len)
+		ret = -EINVAL;
+	else if (alg->verify)
 		ret = alg->verify(req);
 	crypto_stats_akcipher_verify(ret, calg);
 	return ret;