Skip to contentSkip to conversation

Templatize ecdsa signature decoding

PATCH In review Lukas Wunner 4 participants 20 messages Jul 29, 2024 – Sep 6, 2024

Subsystems (7)
lore
Share & tools
lore

Lukas Wunner

author maintainer
#0 lore raw

cover Templatize ecdsa signature decoding

Move X9.62 signature decoding out of the ecdsa driver and into a template (patch [4/5]).

This allows introduction of P1363 signature decoding as another template (patch [5/5]), which is needed by the upcoming SPDM library (Security Protocol and Data Model) for PCI device authentication.

Drop usage of sglists for signature verification and use kernel buffers instead (patch [2/5]) to avoid the overhead of extracting from sglists into kernel buffers both in the templates and in the ecdsa driver. This builds on Herbert's elimination of sglists from the akcipher API with commit 63ba4d67594a ("KEYS: asymmetric:
Use new crypto interface without scatterlists").

I realize that P1363 support (patch [5/5]) might not be acceptable standalone, but I'm hoping to get an ack for it so that I may upstream the patch as part of PCI device authentication. Patches [1/5] to [4/5] will hopefully be acceptable standalone.

Note that I've duplicated the ecdsa test vectors in their entirety in patch [4/5]. If that is considered overzealous, I can reduce them to one or a few for testing the "raw" internal encoding. In patch [5/5] I've included just a single test vector for P1363. I can add more if desired.

Link to Herbert's suggestion to use templates for X9.62 and P1363:
https://lore.kernel.org/all/[email protected]/

Link to PCI device authentication v2 patches:
https://lore.kernel.org/all/[email protected]/

Please review and test -- thank you!

Lukas Wunner (5):
ASN.1: Add missing include <linux/types.h>
crypto: akcipher - Drop usage of sglists for verify op
crypto: ecdsa - Avoid signed integer overflow on signature decoding
crypto: ecdsa - Move X9.62 signature decoding into template
crypto: ecdsa - Support P1363 signature decoding

 crypto/Makefile                     |   4 +-
 crypto/akcipher.c                   |  11 +-
 crypto/asymmetric_keys/public_key.c |  43 +-
 crypto/ecdsa-p1363.c                | 155 +++++
 crypto/ecdsa-x962.c                 | 211 +++++++
 crypto/ecdsa.c                      | 122 ++--
 crypto/ecrdsa.c                     |  28 +-
 crypto/rsa-pkcs1pad.c               |  27 +-
 crypto/sig.c                        |  24 +-
 crypto/testmgr.c                    |  57 +-
 crypto/testmgr.h                    | 847 +++++++++++++++++++++++++++-
 include/crypto/akcipher.h           |  53 +-
 include/crypto/internal/ecc.h       |   2 +
 include/linux/asn1_decoder.h        |   1 +
 14 files changed, 1391 insertions(+), 194 deletions(-)
 create mode 100644 crypto/ecdsa-p1363.c
 create mode 100644 crypto/ecdsa-x962.c
Signature · 1 line
2.43.0
author maintainer
#1 lore raw

PATCH 1/5 ASN.1: Add missing include <linux/types.h>

If <linux/asn1_decoder.h> is the first header included from a .c file (due to headers being sorted alphabetically), the compiler complains:

include/linux/asn1_decoder.h:18:29: error: unknown type name 'size_t'

Fix it.

---
 include/linux/asn1_decoder.h | 1 +
 1 file changed, 1 insertion(+)
include/linux/asn1_decoder.h +1 -0
index 83f9c6e1e5e9..b41bce82a191 100644
@@ -9,6 +9,7 @@
 #define _LINUX_ASN1_DECODER_H
 
 #include <linux/asn1.h>
+#include <linux/types.h>
 
 struct asn1_decoder;
 
Signature · 1 line
2.43.0
reviewer Reviewed-by on v1 1/5
#2 lore raw

Re: [PATCH 1/5] ASN.1: Add missing include <linux/types.h>

Quoted 8 lines · Lukas Wunner

If <linux/asn1_decoder.h> is the first header included from a .c file (due to headers being sorted alphabetically), the compiler complains:

include/linux/asn1_decoder.h:18:29: error: unknown type name 'size_t'

Fix it.

Quoted 16 lines
---
  include/linux/asn1_decoder.h | 1 +
  1 file changed, 1 insertion(+)
include/linux/asn1_decoder.h +1 -0
index 83f9c6e1e5e9..b41bce82a191 100644
@@ -9,6 +9,7 @@
  #define _LINUX_ASN1_DECODER_H
 
  #include <linux/asn1.h>
+#include <linux/types.h>
 
  struct asn1_decoder;
 
Reviewed-by on v1 1/5
#3 lore raw

Re: [PATCH 1/5] ASN.1: Add missing include <linux/types.h>

In reply to Lukas Wunner
Quoted 8 lines · On Mon, 29 Jul 2024 15:47:00 +0200 Lukas Wunner <[email protected]> wrote:

If <linux/asn1_decoder.h> is the first header included from a .c file (due to headers being sorted alphabetically), the compiler complains:

include/linux/asn1_decoder.h:18:29: error: unknown type name 'size_t'

Fix it.

FWIW

I see asn1_encoder.h already has this include so this is nice and consistent.

However that header includes bug.h which should probably be pushed down into the relevant c files. Not relevant for this series though so one for another day (or never :)

Quoted 16 lines
---
 include/linux/asn1_decoder.h | 1 +
 1 file changed, 1 insertion(+)
include/linux/asn1_decoder.h +1 -0
index 83f9c6e1e5e9..b41bce82a191 100644
@@ -9,6 +9,7 @@
 #define _LINUX_ASN1_DECODER_H
 
 #include <linux/asn1.h>
+#include <linux/types.h>
 
 struct asn1_decoder;
 
author maintainer
#4 lore raw

PATCH 2/5 crypto: akcipher - Drop usage of sglists for verify op

In reply to Lukas Wunner

Commit 6cb8815f41a9 ("crypto: sig - Add interface for sign/verify") introduced an API which accepts kernel buffers instead of sglists for signature generation and verification.

Commit 63ba4d67594a ("KEYS: asymmetric: Use new crypto interface without scatterlists") converted the sole user in the tree to the new API.

Although the API externally accepts kernel buffers, internally it still converts them to sglists, which results in overhead for asymmetric algorithms because they need to copy the sglists back into kernel buffers.

Take the next step and switch signature verification over to using kernel buffers internally, thereby avoiding the sglists overhead.

Because all ->verify implementations are synchronous, forego invocation of crypto_akcipher_sync_{prep,post}() and call crypto_akcipher_verify() directly from crypto_sig_verify().

---
 crypto/akcipher.c         | 11 +++-----
 crypto/ecdsa.c            | 27 +++++---------------
 crypto/ecrdsa.c           | 28 +++++++--------------
 crypto/rsa-pkcs1pad.c     | 27 ++++++++------------
 crypto/sig.c              | 24 +++++++++---------
 crypto/testmgr.c          | 12 ++++-----
 include/crypto/akcipher.h | 53 +++++++++++++++++++++------------------
 7 files changed, 76 insertions(+), 106 deletions(-)
crypto/akcipher.c +3 -8
index e0ff5f4dda6d..c7c3dcd88b06 100644
@@ -167,10 +167,7 @@ int crypto_akcipher_sync_prep(struct crypto_akcipher_sync_data *data)
 	unsigned int len;
 	u8 *buf;
 
-	if (data->dst)
-		mlen = max(data->slen, data->dlen);
-	else
-		mlen = data->slen + data->dlen;
+	mlen = max(data->slen, data->dlen);
 
 	len = sizeof(*req) + reqsize + mlen;
 	if (len < mlen)
@@ -189,8 +186,7 @@ int crypto_akcipher_sync_prep(struct crypto_akcipher_sync_data *data)
 
 	sg = &data->sg;
 	sg_init_one(sg, buf, mlen);
-	akcipher_request_set_crypt(req, sg, data->dst ? sg : NULL,
-				   data->slen, data->dlen);
+	akcipher_request_set_crypt(req, sg, sg, data->slen, data->dlen);
 
 	crypto_init_wait(&data->cwait);
 	akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP,
@@ -203,8 +199,7 @@ EXPORT_SYMBOL_GPL(crypto_akcipher_sync_prep);
 int crypto_akcipher_sync_post(struct crypto_akcipher_sync_data *data, int err)
 {
 	err = crypto_wait_req(err, &data->cwait);
-	if (data->dst)
-		memcpy(data->dst, data->buf, data->dlen);
+	memcpy(data->dst, data->buf, data->dlen);
 	data->dlen = data->req->dst_len;
 	kfree_sensitive(data->req);
 	return err;
crypto/ecdsa.c +6 -21
index d5a10959ec28..f63731fb7535 100644
@@ -9,7 +9,6 @@
 #include <crypto/akcipher.h>
 #include <crypto/ecdh.h>
 #include <linux/asn1_decoder.h>
-#include <linux/scatterlist.h>
 
 #include "ecdsasignature.asn1.h"
 
@@ -135,37 +134,23 @@ static int ecdsa_verify(struct akcipher_request *req)
 		.curve = ctx->curve,
 	};
 	u64 hash[ECC_MAX_DIGITS];
-	unsigned char *buffer;
 	int ret;
 
 	if (unlikely(!ctx->pub_key_set))
 		return -EINVAL;
 
-	buffer = kmalloc(req->src_len + req->dst_len, GFP_KERNEL);
-	if (!buffer)
-		return -ENOMEM;
-
-	sg_pcopy_to_buffer(req->src,
-		sg_nents_for_len(req->src, req->src_len + req->dst_len),
-		buffer, req->src_len + req->dst_len, 0);
-
 	ret = asn1_ber_decoder(&ecdsasignature_decoder, &sig_ctx,
-			       buffer, req->src_len);
+			       req->sig, req->sig_len);
 	if (ret < 0)
-		goto error;
+		return ret;
 
-	if (bufsize > req->dst_len)
-		bufsize = req->dst_len;
+	if (bufsize > req->digest_len)
+		bufsize = req->digest_len;
 
-	ecc_digits_from_bytes(buffer + req->src_len, bufsize,
+	ecc_digits_from_bytes(req->digest, bufsize,
 			      hash, ctx->curve->g.ndigits);
 
-	ret = _ecdsa_verify(ctx, hash, sig_ctx.r, sig_ctx.s);
-
-error:
-	kfree(buffer);
-
-	return ret;
+	return _ecdsa_verify(ctx, hash, sig_ctx.r, sig_ctx.s);
 }
 
 static int ecdsa_ecc_ctx_init(struct ecc_ctx *ctx, unsigned int curve_id)
crypto/ecrdsa.c +9 -19
index 3811f3805b5d..fd78254fef76 100644
@@ -23,7 +23,6 @@
 #include <crypto/internal/ecc.h>
 #include <crypto/akcipher.h>
 #include <linux/oid_registry.h>
-#include <linux/scatterlist.h>
 #include "ecrdsa_params.asn1.h"
 #include "ecrdsa_pub_key.asn1.h"
 #include "ecrdsa_defs.h"
@@ -72,8 +71,6 @@ static int ecrdsa_verify(struct akcipher_request *req)
 {
 	struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
 	struct ecrdsa_ctx *ctx = akcipher_tfm_ctx(tfm);
-	unsigned char sig[ECRDSA_MAX_SIG_SIZE];
-	unsigned char digest[STREEBOG512_DIGEST_SIZE];
 	unsigned int ndigits = req->dst_len / sizeof(u64);
 	u64 r[ECRDSA_MAX_DIGITS]; /* witness (r) */
 	u64 _r[ECRDSA_MAX_DIGITS]; /* -r */
@@ -91,25 +88,18 @@ static int ecrdsa_verify(struct akcipher_request *req)
 	 */
 	if (!ctx->curve ||
 	    !ctx->digest ||
-	    !req->src ||
+	    !req->sig ||
 	    !ctx->pub_key.x ||
-	    req->dst_len != ctx->digest_len ||
-	    req->dst_len != ctx->curve->g.ndigits * sizeof(u64) ||
+	    req->digest_len != ctx->digest_len ||
+	    req->digest_len != ctx->curve->g.ndigits * sizeof(u64) ||
 	    ctx->pub_key.ndigits != ctx->curve->g.ndigits ||
-	    req->dst_len * 2 != req->src_len ||
-	    WARN_ON(req->src_len > sizeof(sig)) ||
-	    WARN_ON(req->dst_len > sizeof(digest)))
+	    req->digest_len * 2 != req->sig_len ||
+	    WARN_ON(req->sig_len > ECRDSA_MAX_SIG_SIZE) ||
+	    WARN_ON(req->digest_len > STREEBOG512_DIGEST_SIZE))
 		return -EBADMSG;
 
-	sg_copy_to_buffer(req->src, sg_nents_for_len(req->src, req->src_len),
-			  sig, req->src_len);
-	sg_pcopy_to_buffer(req->src,
-			   sg_nents_for_len(req->src,
-					    req->src_len + req->dst_len),
-			   digest, req->dst_len, req->src_len);
-
-	vli_from_be64(s, sig, ndigits);
-	vli_from_be64(r, sig + ndigits * sizeof(u64), ndigits);
+	vli_from_be64(s, req->sig, ndigits);
+	vli_from_be64(r, req->sig + ndigits * sizeof(u64), ndigits);
 
 	/* Step 1: verify that 0 < r < q, 0 < s < q */
 	if (vli_is_zero(r, ndigits) ||
@@ -120,7 +110,7 @@ static int ecrdsa_verify(struct akcipher_request *req)
 
 	/* Step 2: calculate hash (h) of the message (passed as input) */
 	/* Step 3: calculate e = h \mod q */
-	vli_from_le64(e, digest, ndigits);
+	vli_from_le64(e, req->digest, ndigits);
 	if (vli_cmp(e, ctx->curve->n, ndigits) >= 0)
 		vli_sub(e, e, ctx->curve->n, ndigits);
 	if (vli_is_zero(e, ndigits))
crypto/rsa-pkcs1pad.c +11 -16
index cd501195f34a..fe53bead22cc 100644
@@ -182,8 +182,8 @@ static unsigned int pkcs1pad_get_max_size(struct crypto_akcipher *tfm)
 	return ctx->key_size;
 }
 
-static void pkcs1pad_sg_set_buf(struct scatterlist *sg, void *buf, size_t len,
-		struct scatterlist *next)
+static void pkcs1pad_sg_set_buf(struct scatterlist *sg, const void *buf,
+				size_t len, struct scatterlist *next)
 {
 	int nsegs = next ? 2 : 1;
 
@@ -459,8 +459,7 @@ static int pkcs1pad_verify_complete(struct akcipher_request *req, int err)
 	struct akcipher_instance *inst = akcipher_alg_instance(tfm);
 	struct pkcs1pad_inst_ctx *ictx = akcipher_instance_ctx(inst);
 	const struct rsa_asn1_template *digest_info = ictx->digest_info;
-	const unsigned int sig_size = req->src_len;
-	const unsigned int digest_size = req->dst_len;
+	const unsigned int digest_size = req->digest_len;
 	unsigned int dst_len;
 	unsigned int pos;
 	u8 *out_buf;
@@ -512,14 +511,8 @@ static int pkcs1pad_verify_complete(struct akcipher_request *req, int err)
 		req->dst_len = dst_len - pos;
 		goto done;
 	}
-	/* Extract appended digest. */
-	sg_pcopy_to_buffer(req->src,
-			   sg_nents_for_len(req->src, sig_size + digest_size),
-			   req_ctx->out_buf + ctx->key_size,
-			   digest_size, sig_size);
 	/* Do the actual verification step. */
-	if (memcmp(req_ctx->out_buf + ctx->key_size, out_buf + pos,
-		   digest_size) != 0)
+	if (memcmp(req->digest, out_buf + pos, digest_size) != 0)
 		err = -EKEYREJECTED;
 done:
 	kfree_sensitive(req_ctx->out_buf);
@@ -553,27 +546,29 @@ static int pkcs1pad_verify(struct akcipher_request *req)
 	struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
 	struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm);
 	struct pkcs1pad_request *req_ctx = akcipher_request_ctx(req);
-	const unsigned int sig_size = req->src_len;
-	const unsigned int digest_size = req->dst_len;
+	const unsigned int sig_size = req->sig_len;
+	const unsigned int digest_size = req->digest_len;
 	int err;
 
-	if (WARN_ON(req->dst) || WARN_ON(!digest_size) ||
+	if (WARN_ON(!digest_size) ||
 	    !ctx->key_size || sig_size != ctx->key_size)
 		return -EINVAL;
 
-	req_ctx->out_buf = kmalloc(ctx->key_size + digest_size, GFP_KERNEL);
+	req_ctx->out_buf = kmalloc(ctx->key_size, GFP_KERNEL);
 	if (!req_ctx->out_buf)
 		return -ENOMEM;
 
 	pkcs1pad_sg_set_buf(req_ctx->out_sg, req_ctx->out_buf,
 			    ctx->key_size, NULL);
+	pkcs1pad_sg_set_buf(req_ctx->in_sg, req->sig,
+			    req->sig_len, NULL);
 
 	akcipher_request_set_tfm(&req_ctx->child_req, ctx->child);
 	akcipher_request_set_callback(&req_ctx->child_req, req->base.flags,
 			pkcs1pad_verify_complete_cb, req);
 
 	/* Reuse input buffer, output to a new buffer */
-	akcipher_request_set_crypt(&req_ctx->child_req, req->src,
+	akcipher_request_set_crypt(&req_ctx->child_req, req_ctx->in_sg,
 				   req_ctx->out_sg, sig_size, ctx->key_size);
 
 	err = crypto_akcipher_encrypt(&req_ctx->child_req);
crypto/sig.c +12 -12
index 7645bedf3a1f..e88073196417 100644
@@ -98,22 +98,22 @@ int crypto_sig_verify(struct crypto_sig *tfm,
 		      const void *digest, unsigned int dlen)
 {
 	struct crypto_akcipher **ctx = crypto_sig_ctx(tfm);
-	struct crypto_akcipher_sync_data data = {
-		.tfm = *ctx,
-		.src = src,
-		.slen = slen,
-		.dlen = dlen,
-	};
+	struct crypto_akcipher *child_tfm = *ctx;
+	struct akcipher_request *req;
 	int err;
 
-	err = crypto_akcipher_sync_prep(&data);
-	if (err)
-		return err;
+	req = kzalloc(sizeof(*req) + crypto_akcipher_reqsize(child_tfm),
+		      GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	akcipher_request_set_tfm(req, child_tfm);
+	akcipher_request_set_crypt(req, src, digest, slen, dlen);
 
-	memcpy(data.buf + slen, digest, dlen);
+	err = crypto_akcipher_verify(req);
 
-	return crypto_akcipher_sync_post(&data,
-					 crypto_akcipher_verify(data.req));
+	kfree_sensitive(req);
+	return err;
 }
 EXPORT_SYMBOL_GPL(crypto_sig_verify);
 
crypto/testmgr.c +6 -6
index f02cb075bd68..483f87efd4b7 100644
@@ -4105,7 +4105,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[3];
+	struct scatterlist src, dst, src_tab[2];
 	const char *m, *c;
 	unsigned int m_size, c_size;
 	const char *op;
@@ -4169,16 +4169,16 @@ static int test_akcipher_one(struct crypto_akcipher *tfm,
 		goto free_all;
 	memcpy(xbuf[0], m, m_size);
 
-	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);
 	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);
+		akcipher_request_set_crypt(req, xbuf[0], xbuf[1],
+					   m_size, c_size);
 	} else {
+		sg_init_table(src_tab, 2);
+		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);
include/crypto/akcipher.h +29 -24

87 lines not on this page · raw message

Signature · 1 line
2.43.0
#5 lore raw

Re: [PATCH 2/5] crypto: akcipher - Drop usage of sglists for verify op

Quoted 12 lines · On Mon, 29 Jul 2024 15:48:00 +0200 Lukas Wunner <[email protected]> wrote:

Commit 6cb8815f41a9 ("crypto: sig - Add interface for sign/verify") introduced an API which accepts kernel buffers instead of sglists for signature generation and verification.

Commit 63ba4d67594a ("KEYS: asymmetric: Use new crypto interface without scatterlists") converted the sole user in the tree to the new API.

Although the API externally accepts kernel buffers, internally it still converts them to sglists, which results in overhead for asymmetric algorithms because they need to copy the sglists back into kernel buffers.

Quoted 8 lines

Take the next step and switch signature verification over to using kernel buffers internally, thereby avoiding the sglists overhead.

Because all ->verify implementations are synchronous, forego invocation of crypto_akcipher_sync_{prep,post}() and call crypto_akcipher_verify() directly from crypto_sig_verify().

I'm a little out of my depth in this code, but one question did come to mind. Rather than passing scatter lists and buffers via the same function void akcipher_request_set_crypt()
why not add a variant that takes buffers for signing cases so that you can maintain the scatterlist pointers for the encrypt/decrypt cases?

Quoted 63 lines
include/crypto/akcipher.h +22 -15
index 18a10cad07aa..2c5bc35d297a 100644
@@ -16,28 +16,39 @@
  *
  * @base:	Common attributes for async crypto requests
  * @src:	Source 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)
+ * @dst:	Destination data
  * @src_len:	Size of the input buffer
- *		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).
+ * @dst_len:	Size of @dst buffer
  *		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.
+ * @sig:	Signature
+ * @digest:	Digest
+ * @sig_len:	Size of @sig
+ * @digest_len:	Size of @digest
  * @__ctx:	Start of private context data
  */
 struct akcipher_request {
 	struct crypto_async_request base;
-	struct scatterlist *src;
-	struct scatterlist *dst;
-	unsigned int src_len;
-	unsigned int dst_len;
+	union {
+		struct {
+			/* sign, encrypt, decrypt operations */
+			struct scatterlist *src;
+			struct scatterlist *dst;
+			unsigned int src_len;
+			unsigned int dst_len;
+		};
+		struct {
+			/* verify operation */
+			const void *sig;
+			const void *digest;
+			unsigned int sig_len;
+			unsigned int digest_len;
+		};
+	};
 	void *__ctx[] CRYPTO_MINALIGN_ATTR;
 };
 
@@ -242,20 +253,18 @@ static inline void akcipher_request_set_callback(struct akcipher_request *req,
  * Sets parameters required by crypto operation
  *
  * @req:	public key request
- * @src:	ptr to input 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 or size of signature

include/crypto/akcipher.h static inline void akcipher_request_set_callback(struct akcipher_request *req, · line 262

Quoted 10 lines
include/crypto/akcipher.h +5 -3
- *		portion in @src for verify op
+ * @src:	ptr to input scatter list or signature for verify op
+ * @dst:	ptr to output scatter list or digest for verify op
+ * @src_len:	size of @src
+ * @dst_len:	size of @dst
  */
 static inline void akcipher_request_set_crypt(struct akcipher_request *req,
-					      struct scatterlist *src,
-					      struct scatterlist *dst,
+					      const void *src, const void *dst,

Maybe it's worth a 'special' variant.
static inline void akcipher_request_set_crypt_for_verify() so that you can keep the other list typed and not rely on overlapping pointer fields via the union.

Quoted 21 lines
include/crypto/akcipher.h +2 -6
 					      unsigned int src_len,
 					      unsigned int dst_len)
 {
-	req->src = src;
-	req->dst = dst;
+	req->sig = src;
+	req->digest = dst;
 	req->src_len = src_len;
 	req->dst_len = dst_len;
 }
@@ -372,10 +381,6 @@ static inline int crypto_akcipher_sign(struct akcipher_request *req)
  *
  * @req:	asymmetric key request
  *
- * 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)
author maintainer
#6 lore raw

Re: [PATCH 2/5] crypto: akcipher - Drop usage of sglists for verify op

Quoted 25 lines · Jonathan Cameron
Quoted 12 lines · On Mon, 29 Jul 2024 15:48:00 +0200 Lukas Wunner <[email protected]> wrote:

Commit 6cb8815f41a9 ("crypto: sig - Add interface for sign/verify") introduced an API which accepts kernel buffers instead of sglists for signature generation and verification.

Commit 63ba4d67594a ("KEYS: asymmetric: Use new crypto interface without scatterlists") converted the sole user in the tree to the new API.

Although the API externally accepts kernel buffers, internally it still converts them to sglists, which results in overhead for asymmetric algorithms because they need to copy the sglists back into kernel buffers.

Quoted 6 lines

Take the next step and switch signature verification over to using kernel buffers internally, thereby avoiding the sglists overhead.

Because all ->verify implementations are synchronous, forego invocation of crypto_akcipher_sync_{prep,post}() and call crypto_akcipher_verify() directly from crypto_sig_verify().

Rather than passing scatter lists and buffers
via the same function void akcipher_request_set_crypt()
why not add a variant that takes buffers for signing cases so that you can maintain the scatterlist pointers for the encrypt/decrypt cases?

[...]

Quoted 4 lines

Maybe it's worth a 'special' variant.
static inline void akcipher_request_set_crypt_for_verify() so that you can keep the other list typed and not rely on overlapping pointer fields via the union.

I need some guidance from Herbert as to which operations he envisages shall be migrated to kernel buffers.

Perhaps he wants to migrate the encrypt/decrypt operations as well? The present patch migrates verify and the other ones (sign/encrypt/decrypt) could be migrated one by one afterwards.

In that case I don't think it makes sense to introduce a new function. We'd retain the name of the existing function and only change parameters passed to it.

There is no caller for encrypt/decrypt operations in the tree which uses sglists:

* The only caller of crypto_akcipher_sync_{en,de}crypt() in the tree is software_key_eds_op() and uses kernel buffers.
* The only callers of crypto_akcipher_{en,de}crypt() are rsa-pkcs1pad.c and three crypto hardware drivers which fall back to software RSA for certain key sizes (aspeed-acry.c, hpre_crypto.c, jh7110-rsa.c). They could use kernel buffers as well.

Thanks,

Lukas

maintainer
#7 lore raw

Re: [PATCH 2/5] crypto: akcipher - Drop usage of sglists for verify op

In reply to Lukas Wunner
Quoted 20 lines · Lukas Wunner

Commit 6cb8815f41a9 ("crypto: sig - Add interface for sign/verify") introduced an API which accepts kernel buffers instead of sglists for signature generation and verification.

Commit 63ba4d67594a ("KEYS: asymmetric: Use new crypto interface without scatterlists") converted the sole user in the tree to the new API.

Although the API externally accepts kernel buffers, internally it still converts them to sglists, which results in overhead for asymmetric algorithms because they need to copy the sglists back into kernel buffers.

Take the next step and switch signature verification over to using kernel buffers internally, thereby avoiding the sglists overhead.

Because all ->verify implementations are synchronous, forego invocation of crypto_akcipher_sync_{prep,post}() and call crypto_akcipher_verify() directly from crypto_sig_verify().

Quoted 9 lines
---
 crypto/akcipher.c         | 11 +++-----
 crypto/ecdsa.c            | 27 +++++---------------
 crypto/ecrdsa.c           | 28 +++++++--------------
 crypto/rsa-pkcs1pad.c     | 27 ++++++++------------
 crypto/sig.c              | 24 +++++++++---------
 crypto/testmgr.c          | 12 ++++-----
 include/crypto/akcipher.h | 53 +++++++++++++++++++++------------------
 7 files changed, 76 insertions(+), 106 deletions(-)

The link between sig and akcipher is meant to be temporary. The plan is to create a new low-level API for sig and then migrate the signature code over to that from akcipher.

Yes we do want to get rid of the unnecessary SG list ops but is it possible to side-step this for your work? If not perhaps you could help by creating the low-level API for sig? :)

Thanks,

Signature · 3 lines
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
author maintainer
#8 lore raw

Re: [PATCH 2/5] crypto: akcipher - Drop usage of sglists for verify op

Quoted 20 lines · Herbert Xu
Quoted 12 lines · Lukas Wunner

Commit 6cb8815f41a9 ("crypto: sig - Add interface for sign/verify") introduced an API which accepts kernel buffers instead of sglists for signature generation and verification.

Commit 63ba4d67594a ("KEYS: asymmetric: Use new crypto interface without scatterlists") converted the sole user in the tree to the new API.

Although the API externally accepts kernel buffers, internally it still converts them to sglists, which results in overhead for asymmetric algorithms because they need to copy the sglists back into kernel buffers.

Quoted 6 lines

Take the next step and switch signature verification over to using kernel buffers internally, thereby avoiding the sglists overhead.

Because all ->verify implementations are synchronous, forego invocation of crypto_akcipher_sync_{prep,post}() and call crypto_akcipher_verify() directly from crypto_sig_verify().

Quoted 7 lines

The link between sig and akcipher is meant to be temporary. The plan is to create a new low-level API for sig and then migrate the signature code over to that from akcipher.

Yes we do want to get rid of the unnecessary SG list ops but is it possible to side-step this for your work? If not perhaps you could help by creating the low-level API for sig? :)

I'm looking through the code right now to understand what would be necessary to get there.

One issue I see is an algorithm name collision in rsa-pkcs1pad.c:
I think I'd have to register two instances in pkcs1pad_create(), an akcipher_instance and a sig_instance.

And inst->alg.base.cra_name would be the same in both cases, i.e. "pkcs1pad(%s)". Now what? :(

The last couple of days I've been contemplating amending struct akcipher_alg with additional callbacks to get the max_sig_size and max_data_size. For RSA it's the same as the keysize (which is available through the existing ->max_size callback), but for ECDSA it's different depending on the template. Adding those new callbacks to a new struct sig_alg would be cleaner of course than shoehorning them into struct akcipher_alg.

Thanks,

Lukas

maintainer
#9 lore raw

Re: [PATCH 2/5] crypto: akcipher - Drop usage of sglists for verify op

Quoted 3 lines · Lukas Wunner

I'm looking through the code right now to understand what would be necessary to get there.

Great :)

Quoted 3 lines

One issue I see is an algorithm name collision in rsa-pkcs1pad.c:
I think I'd have to register two instances in pkcs1pad_create(), an akcipher_instance and a sig_instance.

Since there is precisely one user -- crypto/asymmetric, we could simply rename the sig version of pkcs1pad to something else without causing too much churn. Perhaps leave the akcipher pkcs1pad as is and create a new template for sig called pkcs1sig.

So you could do it in a little series without breaking bisection:

1) Add sig type and then create the pkcs1sig template;
2) Switch to pkcs1sig when signing in crypto/asymmetric;
3) Remove now-unused signing code from pkcs1pad.

Quoted 8 lines

The last couple of days I've been contemplating amending struct akcipher_alg with additional callbacks to get the max_sig_size and max_data_size. For RSA it's the same as the keysize (which is available through the existing ->max_size callback), but for ECDSA it's different depending on the template. Adding those new callbacks to a new struct sig_alg would be cleaner of course than shoehorning them into struct akcipher_alg.

Yes having a separate alg for sig is definitely where we want to be since there is very little that the two types actually share.

The only place where they currently intersect is pkcs1pad :)

Cheers,

Signature · 3 lines
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
author maintainer
#10 lore raw

Re: [PATCH 2/5] crypto: akcipher - Drop usage of sglists for verify op

In reply to Herbert Xu
Quoted 7 lines · Herbert Xu

The link between sig and akcipher is meant to be temporary. The plan is to create a new low-level API for sig and then migrate the signature code over to that from akcipher.

Yes we do want to get rid of the unnecessary SG list ops but is it possible to side-step this for your work? If not perhaps you could help by creating the low-level API for sig? :)

Status update -- I've done that and pushed an initial version to:

https://github.com/l1k/linux/commits/spdm-future

in commits:

629611b crypto: sig - Introduce sig_alg backend
39b2f45 crypto: ecdsa - Migrate to sig_alg backend
e603b20 crypto: ecrdsa - Migrate to sig_alg backend
299f197 crypto: rsa-pkcs1pad - Deduplicate set_{pub,priv}_key callbacks 6c5ec06 crypto: rsassa-pkcs1 - Migrate to sig_alg backend 6d95a64 crypto: rsassa-pkcs1 - Harden digest length verification 17ac60d crypto: rsassa-pkcs1 - Avoid copying hash prefix c6c7360 crypto: drivers - Drop bogus sign/verify operations fb4fa6c crypto: akcipher - Drop sign/verify operations fb5f4d2 crypto: sig - Move crypto_sig_*() API calls to include file

I'll have to polish and test this a little more before submission.

However, I came across a snag:

There's a virtio interface for akcipher implemented by:

drivers/crypto/virtio/virtio_crypto_akcipher_algs.c include/uapi/linux/virtio_crypto.h

That's user space ABI, so we're stuck with it. The user space ABI combines sign/verify and encrypt/decrypt in common structs.

But it should be possible to change virtio_crypto_akcipher_algs.c so that it uses crypto_sig or crypto_akcipher depending on the virtio request.

That will take some more time and because I also need to prepare for Plumbers, this work may get delayed until the next cycle.

If you want to review and maybe apply a first batch of patches to migrate the algorithms, I can submit that. But the removal of sign/verify from akcipher with the above-mentioned commit fb4fa6c ("crypto: akcipher - Drop sign/verify operations") cannot happen until virtio is migrated.

Thanks,

Lukas

maintainer
#11 lore raw

Re: [PATCH 2/5] crypto: akcipher - Drop usage of sglists for verify op

Quoted 3 lines · Lukas Wunner

That's user space ABI, so we're stuck with it. The user space ABI combines sign/verify and encrypt/decrypt in common structs.

I would say that this is something that we can break. Breaking it is no different to running virtio on a host that does not support these algorithms. After all, a software implementation must always be present.

I deliberately left akcipher out of crypto_user because the API is still in flux. We should not let virtio constrain ourselves.

Thanks,

Signature · 3 lines
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
author maintainer
#12 lore raw

PATCH 3/5 crypto: ecdsa - Avoid signed integer overflow on signature decoding

In reply to Lukas Wunner

When extracting a signature component r or s from an ASN.1-encoded integer, ecdsa_get_signature_rs() subtracts the expected length "bufsize" from the ASN.1 length "vlen" (both of unsigned type size_t) and stores the result in "diff" (of signed type ssize_t).

This results in a signed integer overflow if vlen > SSIZE_MAX + bufsize.

The kernel is compiled with -fno-strict-overflow, which implies -fwrapv, meaning signed integer overflow is not undefined behavior. And the function does check for overflow:

if (-diff >= bufsize)
return -EINVAL;

So the code is fine in principle but not very obvious. In the future it might trigger a false-positive with CONFIG_UBSAN_SIGNED_WRAP=y.

Avoid by comparing the two unsigned variables directly and erroring out if "vlen" is too large.

---
 crypto/ecdsa.c | 17 ++++-------------
 1 file changed, 4 insertions(+), 13 deletions(-)
crypto/ecdsa.c +4 -13
index f63731fb7535..03f608132242 100644
@@ -35,29 +35,20 @@ static int ecdsa_get_signature_rs(u64 *dest, size_t hdrlen, unsigned char tag,
 				  const void *value, size_t vlen, unsigned int ndigits)
 {
 	size_t bufsize = ndigits * sizeof(u64);
-	ssize_t diff = vlen - bufsize;
 	const char *d = value;
 
-	if (!value || !vlen)
+	if (!value || !vlen || vlen > bufsize + 1)
 		return -EINVAL;
 
-	/* diff = 0: 'value' has exacly the right size
-	 * diff > 0: 'value' has too many bytes; one leading zero is allowed that
-	 *           makes the value a positive integer; error on more
-	 * diff < 0: 'value' is missing leading zeros
-	 */
-	if (diff > 0) {
+	if (vlen > bufsize) {
 		/* skip over leading zeros that make 'value' a positive int */
 		if (*d == 0) {
 			vlen -= 1;
-			diff--;
 			d++;
-		}
-		if (diff)
+		} else {
 			return -EINVAL;
+		}
 	}
-	if (-diff >= bufsize)
-		return -EINVAL;
 
 	ecc_digits_from_bytes(d, vlen, dest, ndigits);
 
Signature · 1 line
2.43.0
reviewer Reviewed-by on v1 3/5
#13 lore raw

Re: [PATCH 3/5] crypto: ecdsa - Avoid signed integer overflow on signature decoding

Quoted 37 lines · Lukas Wunner

When extracting a signature component r or s from an ASN.1-encoded integer, ecdsa_get_signature_rs() subtracts the expected length "bufsize" from the ASN.1 length "vlen" (both of unsigned type size_t) and stores the result in "diff" (of signed type ssize_t).

This results in a signed integer overflow if vlen > SSIZE_MAX + bufsize.

The kernel is compiled with -fno-strict-overflow, which implies -fwrapv, meaning signed integer overflow is not undefined behavior. And the function does check for overflow:

if (-diff >= bufsize)
return -EINVAL;

So the code is fine in principle but not very obvious. In the future it might trigger a false-positive with CONFIG_UBSAN_SIGNED_WRAP=y.

Avoid by comparing the two unsigned variables directly and erroring out if "vlen" is too large.

---
  crypto/ecdsa.c | 17 ++++-------------
  1 file changed, 4 insertions(+), 13 deletions(-)
crypto/ecdsa.c +0 -1
index f63731fb7535..03f608132242 100644
@@ -35,29 +35,20 @@ static int ecdsa_get_signature_rs(u64 *dest, size_t hdrlen, unsigned char tag,
  				  const void *value, size_t vlen, unsigned int ndigits)
  {
  	size_t bufsize = ndigits * sizeof(u64);
-	ssize_t diff = vlen - bufsize;
  	const char *d = value;
 

crypto/ecdsa.c static int ecdsa_get_signature_rs(u64 *dest, size_t hdrlen, unsigned char tag, · line 42

Quoted 10 lines
crypto/ecdsa.c +2 -7
-	if (!value || !vlen)
+	if (!value || !vlen || vlen > bufsize + 1)
  		return -EINVAL; >
-	/* diff = 0: 'value' has exacly the right size
-	 * diff > 0: 'value' has too many bytes; one leading zero is allowed that
-	 *           makes the value a positive integer; error on more
-	 * diff < 0: 'value' is missing leading zeros
-	 */
-	if (diff > 0) {
+	if (vlen > bufsize) {

At this point vlen could be 1 larger then bufsize in the worst case and there must be a leading 0.

Quoted 4 lines
crypto/ecdsa.c +0 -1
  		/* skip over leading zeros that make 'value' a positive int */
  		if (*d == 0) {
  			vlen -= 1;
-			diff--;

crypto/ecdsa.c static int ecdsa_get_signature_rs(u64 *dest, size_t hdrlen, unsigned char tag, · line 54

Quoted 10 lines
crypto/ecdsa.c +2 -3
  			d++;
-		 > -		if (diff)
+		} else { >   			return -EINVAL;
+		}
  	}
-	if (-diff >= bufsize)
-		return -EINVAL;
 
  	ecc_digits_from_bytes(d, vlen, dest, ndigits);
 
Reviewed-by on v1 3/5
#14 lore raw

Re: [PATCH 3/5] crypto: ecdsa - Avoid signed integer overflow on signature decoding

In reply to Lukas Wunner
Quoted 14 lines · On Mon, 29 Jul 2024 15:49:00 +0200 Lukas Wunner <[email protected]> wrote:

When extracting a signature component r or s from an ASN.1-encoded integer, ecdsa_get_signature_rs() subtracts the expected length "bufsize" from the ASN.1 length "vlen" (both of unsigned type size_t) and stores the result in "diff" (of signed type ssize_t).

This results in a signed integer overflow if vlen > SSIZE_MAX + bufsize.

The kernel is compiled with -fno-strict-overflow, which implies -fwrapv, meaning signed integer overflow is not undefined behavior. And the function does check for overflow:

if (-diff >= bufsize)
return -EINVAL;

Quoted 7 lines

So the code is fine in principle but not very obvious. In the future it might trigger a false-positive with CONFIG_UBSAN_SIGNED_WRAP=y.

Avoid by comparing the two unsigned variables directly and erroring out if "vlen" is too large.

Change looks fine to me.
Random musing inline.

Quoted 9 lines
---
 crypto/ecdsa.c | 17 ++++-------------
 1 file changed, 4 insertions(+), 13 deletions(-)
crypto/ecdsa.c +0 -0
index f63731fb7535..03f608132242 100644

crypto/ecdsa.c static int ecdsa_get_signature_rs(u64 *dest, size_t hdrlen, unsigned char tag, · line 40

Quoted 8 lines
crypto/ecdsa.c +1 -2
@@ -35,29 +35,20 @@ static int ecdsa_get_signature_rs(u64 *dest, size_t hdrlen, unsigned char tag,
 				  const void *value, size_t vlen, unsigned int ndigits)
 {
 	size_t bufsize = ndigits * sizeof(u64);
-	ssize_t diff = vlen - bufsize;
 	const char *d = value;
 
-	if (!value || !vlen)
+	if (!value || !vlen || vlen > bufsize + 1)

Given vlen and bufsize unsigned. Even in the weird case of bufsize + 1 == 0 vlen cannot be zero. So could drop the second condition? (or am I missing something?) Maybe it's easier to reason that vlen == 0 is invalid though.

Quoted 25 lines
crypto/ecdsa.c +3 -11
 		return -EINVAL;
 
-	/* diff = 0: 'value' has exacly the right size
-	 * diff > 0: 'value' has too many bytes; one leading zero is allowed that
-	 *           makes the value a positive integer; error on more
-	 * diff < 0: 'value' is missing leading zeros
-	 */
-	if (diff > 0) {
+	if (vlen > bufsize) {
 		/* skip over leading zeros that make 'value' a positive int */
 		if (*d == 0) {
 			vlen -= 1;
-			diff--;
 			d++;
-		}
-		if (diff)
+		} else {
 			return -EINVAL;
+		}
 	}
-	if (-diff >= bufsize)
-		return -EINVAL;
 
 	ecc_digits_from_bytes(d, vlen, dest, ndigits);
 
author maintainer
#15 lore raw

PATCH 4/5 crypto: ecdsa - Move X9.62 signature decoding into template

In reply to Lukas Wunner

Unlike the rsa driver, which separates signature decoding and signature verification into two steps, the ecdsa driver does both in one.

This restricts users to the one signature format currently supported (X9.62) and prevents addition of others such as P1363, which is needed by the forthcoming SPDM library (Security Protocol and Data Model) for PCI device authentication.

Per Herbert's suggestion, change ecdsa to use a "raw" signature encoding and then implement X9.62 and P1363 as templates which convert their respective encodings to the raw one. One may then specify "x962(ecdsa-nist-XXX)" or "p1363(ecdsa-nist-XXX)" to pick the encoding.

The present commit moves X9.62 decoding to a template. A separate commit is going to introduce another template for P1363 decoding.

The ecdsa driver internally represents a signature as two u64 arrays of size ECC_MAX_BYTES. This appears to be the most natural choice for the raw format as it can directly be used for verification without having to further decode signature data or copy it around.

Repurpose all the existing test vectors for "x962(ecdsa-nist-XXX)" and create a duplicate of them to test the raw encoding.

---
 crypto/Makefile                     |   3 +-
 crypto/asymmetric_keys/public_key.c |   3 +
 crypto/ecdsa-x962.c                 | 211 ++++++++
 crypto/ecdsa.c                      |  86 +--
 crypto/testmgr.c                    |  27 +
 crypto/testmgr.h                    | 813 +++++++++++++++++++++++++++-
 include/crypto/internal/ecc.h       |   1 +
 7 files changed, 1077 insertions(+), 67 deletions(-)
 create mode 100644 crypto/ecdsa-x962.c
crypto/Makefile +2 -1
index 4c99e5d376f6..eb6479493f6c 100644
@@ -51,8 +51,9 @@ rsa_generic-y += rsa-pkcs1pad.o
 obj-$(CONFIG_CRYPTO_RSA) += rsa_generic.o
 
 $(obj)/ecdsasignature.asn1.o: $(obj)/ecdsasignature.asn1.c $(obj)/ecdsasignature.asn1.h
-$(obj)/ecdsa.o: $(obj)/ecdsasignature.asn1.h
+$(obj)/ecdsa-x962.o: $(obj)/ecdsasignature.asn1.h
 ecdsa_generic-y += ecdsa.o
+ecdsa_generic-y += ecdsa-x962.o
 ecdsa_generic-y += ecdsasignature.asn1.o
 obj-$(CONFIG_CRYPTO_ECDSA) += ecdsa_generic.o
 
crypto/asymmetric_keys/public_key.c +3 -0
index 422940a6706a..5391f1158bcd 100644
@@ -124,6 +124,9 @@ software_key_determine_akcipher(const struct public_key *pkey,
 		    strcmp(hash_algo, "sha3-384") != 0 &&
 		    strcmp(hash_algo, "sha3-512") != 0)
 			return -EINVAL;
+		n = snprintf(alg_name, CRYPTO_MAX_ALG_NAME, "%s(%s)",
+			     encoding, pkey->pkey_algo);
+		return n >= CRYPTO_MAX_ALG_NAME ? -EINVAL : 0;
 	} else if (strcmp(pkey->pkey_algo, "ecrdsa") == 0) {
 		if (strcmp(encoding, "raw") != 0)
 			return -EINVAL;
crypto/ecdsa-x962.c new file +211 -0

211 lines not on this page · raw message

crypto/ecdsa.c +23 -63

129 lines not on this page · raw message

crypto/testmgr.c +27 -0
index 483f87efd4b7..5d3af7c8e723 100644
@@ -5658,6 +5658,33 @@ static const struct alg_test_desc alg_test_descs[] = {
 		.suite = {
 			.hash = __VECS(wp512_tv_template)
 		}
+	}, {
+		.alg = "x962(ecdsa-nist-p192)",
+		.test = alg_test_akcipher,
+		.suite = {
+			.akcipher = __VECS(x962_ecdsa_nist_p192_tv_template)
+		}
+	}, {
+		.alg = "x962(ecdsa-nist-p256)",
+		.test = alg_test_akcipher,
+		.fips_allowed = 1,
+		.suite = {
+			.akcipher = __VECS(x962_ecdsa_nist_p256_tv_template)
+		}
+	}, {
+		.alg = "x962(ecdsa-nist-p384)",
+		.test = alg_test_akcipher,
+		.fips_allowed = 1,
+		.suite = {
+			.akcipher = __VECS(x962_ecdsa_nist_p384_tv_template)
+		}
+	}, {
+		.alg = "x962(ecdsa-nist-p521)",
+		.test = alg_test_akcipher,
+		.fips_allowed = 1,
+		.suite = {
+			.akcipher = __VECS(x962_ecdsa_nist_p521_tv_template)
+		}
 	}, {
 		.alg = "xcbc(aes)",
 		.test = alg_test_hash,
crypto/testmgr.h +810 -3

847 lines not on this page · raw message

include/crypto/internal/ecc.h +1 -0
index 0717a53ae732..568b7c5352b9 100644
@@ -293,4 +293,5 @@ void ecc_point_mult_shamir(const struct ecc_point *result,
 			   const u64 *y, const struct ecc_point *q,
 			   const struct ecc_curve *curve);
 
+extern struct crypto_template ecdsa_x962_tmpl;
 #endif
Signature · 1 line
2.43.0
#16 lore raw

Re: [PATCH 4/5] crypto: ecdsa - Move X9.62 signature decoding into template

Quoted 21 lines · On Mon, 29 Jul 2024 15:50:00 +0200 Lukas Wunner <[email protected]> wrote:

Unlike the rsa driver, which separates signature decoding and signature verification into two steps, the ecdsa driver does both in one.

This restricts users to the one signature format currently supported (X9.62) and prevents addition of others such as P1363, which is needed by the forthcoming SPDM library (Security Protocol and Data Model) for PCI device authentication.

Per Herbert's suggestion, change ecdsa to use a "raw" signature encoding and then implement X9.62 and P1363 as templates which convert their respective encodings to the raw one. One may then specify "x962(ecdsa-nist-XXX)" or "p1363(ecdsa-nist-XXX)" to pick the encoding.

The present commit moves X9.62 decoding to a template. A separate commit is going to introduce another template for P1363 decoding.

The ecdsa driver internally represents a signature as two u64 arrays of size ECC_MAX_BYTES. This appears to be the most natural choice for the raw format as it can directly be used for verification without having to further decode signature data or copy it around.

Quoted 5 lines

Repurpose all the existing test vectors for "x962(ecdsa-nist-XXX)" and create a duplicate of them to test the raw encoding.

Trivial stuff inline.

Quoted 45 lines
---
 crypto/Makefile                     |   3 +-
 crypto/asymmetric_keys/public_key.c |   3 +
 crypto/ecdsa-x962.c                 | 211 ++++++++
 crypto/ecdsa.c                      |  86 +--
 crypto/testmgr.c                    |  27 +
 crypto/testmgr.h                    | 813 +++++++++++++++++++++++++++-
 include/crypto/internal/ecc.h       |   1 +
 7 files changed, 1077 insertions(+), 67 deletions(-)
 create mode 100644 crypto/ecdsa-x962.c
crypto/ecdsa-x962.c new file +27 -0
new file mode 100644
index 000000000000..ff2da5be1355
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ECDSA X9.62 signature encoding
+ *
+ * Copyright (c) 2021 IBM Corporation
+ * Copyright (c) 2024 Intel Corporation
+ */
+
+#include <linux/asn1_decoder.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <crypto/akcipher.h>
+#include <crypto/algapi.h>
+#include <crypto/internal/akcipher.h>
+#include <crypto/internal/ecc.h>
+
+#include "ecdsasignature.asn1.h"
+
+struct ecdsa_x962_ctx {
+	struct crypto_akcipher *child;
+};
+
+struct ecdsa_x962_request {
+	u64 r[ECC_MAX_DIGITS];
+	u64 s[ECC_MAX_DIGITS];
+	struct akcipher_request child_req;
+};

crypto/ecdsa-x962.c line 37

Quoted 10 lines
crypto/ecdsa-x962.c new file +10 -0
+
+/* Get the r and s components of a signature from the X.509 certificate. */
+static int ecdsa_get_signature_rs(u64 *dest, size_t hdrlen, unsigned char tag,
+				  const void *value, size_t vlen,
+				  unsigned int ndigits)
+{
+	size_t bufsize = ndigits * sizeof(u64);
+	const char *d = value;
+
+	if (!value || !vlen || vlen > bufsize + 1)

Assuming previous musing correct middle test isn't needed. Maybe want to keep it though. Up to you.

Quoted 17 lines
crypto/ecdsa-x962.c new file +17 -0
+		return -EINVAL;
+
+	if (vlen > bufsize) {
+		/* skip over leading zeros that make 'value' a positive int */
+		if (*d == 0) {
+			vlen -= 1;
+			d++;
+		} else {
+			return -EINVAL;
+		}
+	}
+
+	ecc_digits_from_bytes(d, vlen, dest, ndigits);
+
+	return 0;
+}
+

...

Quoted 19 lines
crypto/ecdsa-x962.c new file +19 -0
+static int ecdsa_x962_create(struct crypto_template *tmpl, struct rtattr **tb)
+{
+	struct crypto_akcipher_spawn *spawn;
+	struct akcipher_instance *inst;
+	struct akcipher_alg *ecdsa_alg;
+	u32 mask;
+	int err;
+
+	err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_AKCIPHER, &mask);
+	if (err)
+		return err;
+
+	inst = kzalloc(sizeof(*inst) + sizeof(*spawn), GFP_KERNEL);
+	if (!inst)
+		return -ENOMEM;
+
+	spawn = akcipher_instance_ctx(inst);
+
+	err = crypto_grab_akcipher(spawn, akcipher_crypto_instance(inst),

crypto/ecdsa-x962.c line 179

Quoted 10 lines
crypto/ecdsa-x962.c new file +10 -0
+				   crypto_attr_alg_name(tb[1]), 0, mask);
+	if (err)
+		goto err_free_inst;
+
+	ecdsa_alg = crypto_spawn_akcipher_alg(spawn);
+
+	err = -EINVAL;
+	if (strncmp(ecdsa_alg->base.cra_name, "ecdsa", 5) != 0)
+		goto err_free_inst;
+
	if (cmp(ecdsa_alg->base.cra_name, "ecdsa", 5) != 0) {
		err = -EINVAL;
		goto err_free_inst;
	}

Seems more readable to me.

Quoted 13 lines
crypto/ecdsa-x962.c new file +13 -0
+	err = crypto_inst_setname(akcipher_crypto_instance(inst), tmpl->name,
+				  &ecdsa_alg->base);
+	if (err)
+		goto err_free_inst;
+
+	inst->alg.base.cra_priority = ecdsa_alg->base.cra_priority;
+	inst->alg.base.cra_ctxsize = sizeof(struct ecdsa_x962_ctx);
+
+	inst->alg.init = ecdsa_x962_init_tfm;
+	inst->alg.exit = ecdsa_x962_exit_tfm;
+
+	inst->alg.verify = ecdsa_x962_verify;
+	inst->alg.max_size = ecdsa_x962_max_size;

crypto/ecdsa-x962.c line 202

Quoted 10 lines
crypto/ecdsa-x962.c new file +10 -0
+	inst->alg.set_pub_key = ecdsa_x962_set_pub_key;
+
+	inst->free = ecdsa_x962_free;
+
+	err = akcipher_register_instance(tmpl, inst);
+	if (err) {
+err_free_inst:
+		ecdsa_x962_free(inst);
+	}
+	return err;

I'd rather see a separate error path even if it's a little more code.

	if (err)
		goto err_free_inst;

	return 0;

err_free_inst:

	ecdsa_x862_free(inst)
	return err;
Quoted 2 lines
crypto/ecdsa-x962.c new file +2 -0
+}
+
author maintainer
#17 lore raw

Re: [PATCH 4/5] crypto: ecdsa - Move X9.62 signature decoding into template

Quoted 12 lines · Jonathan Cameron

crypto/ecdsa-x962.c line 37

Quoted 8 lines · On Mon, 29 Jul 2024 15:50:00 +0200 Lukas Wunner <[email protected]> wrote:
crypto/ecdsa-x962.c new file +8 -0
+static int ecdsa_get_signature_rs(u64 *dest, size_t hdrlen, unsigned char tag,
+				  const void *value, size_t vlen,
+				  unsigned int ndigits)
+{
+	size_t bufsize = ndigits * sizeof(u64);
+	const char *d = value;
+
+	if (!value || !vlen || vlen > bufsize + 1)

Assuming previous musing correct middle test isn't needed. Maybe want to keep it though. Up to you.

The kernel wouldn't crash in the !vlen case because ecc_digits_from_bytes() can cope with a zero length argument.

However an integer ASN.1 tag with zero length would be illegal I think. The integer R or S could be very short, but ought to be at least 1 byte long. asn1_decoder.c does not seem to error out on zero length, I assume that would be legal at least for *some* tags.

So it does seem prudent to keep the !vlen check.

Quoted 11 lines

crypto/ecdsa-x962.c line 179

Quoted 4 lines
crypto/ecdsa-x962.c new file +4 -0
+	err = -EINVAL;
+	if (strncmp(ecdsa_alg->base.cra_name, "ecdsa", 5) != 0)
+		goto err_free_inst;
+
	if (cmp(ecdsa_alg->base.cra_name, "ecdsa", 5) != 0) {
		err = -EINVAL;
		goto err_free_inst;
	}

Seems more readable to me.

I'm aware that it looks unusual but in the crypto subsystem it appears to be a fairly common pattern, so I followed it to blend in:

First set the return variable to an error code, then check for an error condition followed by goto.

See e.g.:

__crypto_register_alg() in crypto/algapi.c software_key_eds_op() in crypto/asymmetric_keys/public_key.c x509_get_sig_params() in crypto/asymmetric_keys/x509_public_key.c x509_check_for_self_signed() in crypto/asymmetric_keys/x509_public_key.c

Quoted 9 lines

crypto/ecdsa-x962.c line 202

Quoted 6 lines
crypto/ecdsa-x962.c new file +6 -0
+	err = akcipher_register_instance(tmpl, inst);
+	if (err) {
+err_free_inst:
+		ecdsa_x962_free(inst);
+	}
+	return err;

I'd rather see a separate error path even if it's a little more code.

Quoted 9 lines
	if (err)
		goto err_free_inst;

	return 0;

err_free_inst:

	ecdsa_x862_free(inst)
	return err;
Quoted 1 line

+}

It seems all the existing crypto_templates uniformly follow that pattern of jumping to an "err_free_inst" label in the *_register_instance() error path:

$ git grep -C 4 "err_free_inst:" -- crypto/

It seemed unwise to go against the current, so I followed the existing pattern. Upstream diplomacy. ;)

Thanks,

Lukas

author maintainer
#18 lore raw

PATCH 5/5 crypto: ecdsa - Support P1363 signature decoding

In reply to Lukas Wunner

Alternatively to the X9.62 encoding of ecdsa signatures, which uses ASN.1 and is already supported by the kernel, there's another common encoding called P1363. It stores r and s as the concatenation of two big endian, unsigned integers. The name originates from IEEE P1363.

Add a P1363 template in support of the forthcoming SPDM library (Security Protocol and Data Model) for PCI device authentication.

P1363 is prescribed by SPDM 1.2.1 margin no 44:

"For ECDSA signatures, excluding SM2, in SPDM, the signature shall be
the concatenation of r and s. The size of r shall be the size of the selected curve. Likewise, the size of s shall be the size of the selected curve. See BaseAsymAlgo in NEGOTIATE_ALGORITHMS for the size of r and s. The byte order for r and s shall be in big endian order. When placing ECDSA signatures into an SPDM signature field, r shall come first followed by s."

---
 crypto/Makefile                     |   1 +
 crypto/asymmetric_keys/public_key.c |  40 ++++---
 crypto/ecdsa-p1363.c                | 155 ++++++++++++++++++++++++++++
 crypto/ecdsa.c                      |   8 ++
 crypto/testmgr.c                    |  18 ++++
 crypto/testmgr.h                    |  34 ++++++
 include/crypto/internal/ecc.h       |   1 +
 7 files changed, 241 insertions(+), 16 deletions(-)
 create mode 100644 crypto/ecdsa-p1363.c
crypto/Makefile +1 -0
index eb6479493f6c..f7a7b1a6c93f 100644
@@ -54,6 +54,7 @@ $(obj)/ecdsasignature.asn1.o: $(obj)/ecdsasignature.asn1.c $(obj)/ecdsasignature
 $(obj)/ecdsa-x962.o: $(obj)/ecdsasignature.asn1.h
 ecdsa_generic-y += ecdsa.o
 ecdsa_generic-y += ecdsa-x962.o
+ecdsa_generic-y += ecdsa-p1363.o
 ecdsa_generic-y += ecdsasignature.asn1.o
 obj-$(CONFIG_CRYPTO_ECDSA) += ecdsa_generic.o
 
crypto/asymmetric_keys/public_key.c +24 -16
index 5391f1158bcd..6b004022f655 100644
@@ -104,7 +104,8 @@ software_key_determine_akcipher(const struct public_key *pkey,
 			return -EINVAL;
 		*sig = false;
 	} else if (strncmp(pkey->pkey_algo, "ecdsa", 5) == 0) {
-		if (strcmp(encoding, "x962") != 0)
+		if (strcmp(encoding, "x962") != 0 &&
+		    strcmp(encoding, "p1363") != 0)
 			return -EINVAL;
 		/*
 		 * ECDSA signatures are taken over a raw hash, so they don't
@@ -230,7 +231,6 @@ static int software_key_query(const struct kernel_pkey_params *params,
 	info->key_size = len * 8;
 
 	if (strncmp(pkey->pkey_algo, "ecdsa", 5) == 0) {
-		int slen = len;
 		/*
 		 * ECDSA key sizes are much smaller than RSA, and thus could
 		 * operate on (hashed) inputs that are larger than key size.
@@ -242,21 +242,29 @@ static int software_key_query(const struct kernel_pkey_params *params,
 
 		/*
 		 * Verify takes ECDSA-Sig (described in RFC 5480) as input,
-		 * which is actually 2 'key_size'-bit integers encoded in
-		 * ASN.1.  Account for the ASN.1 encoding overhead here.
-		 *
-		 * NIST P192/256/384 may prepend a '0' to a coordinate to
-		 * indicate a positive integer. NIST P521 never needs it.
+		 * which is actually 2 'key_size'-bit integers.
 		 */
-		if (strcmp(pkey->pkey_algo, "ecdsa-nist-p521") != 0)
-			slen += 1;
-		/* Length of encoding the x & y coordinates */
-		slen = 2 * (slen + 2);
-		/*
-		 * If coordinate encoding takes at least 128 bytes then an
-		 * additional byte for length encoding is needed.
-		 */
-		info->max_sig_size = 1 + (slen >= 128) + 1 + slen;
+		if (strcmp(params->encoding, "x962") == 0) {
+			int slen = len;
+
+			/*
+			 * Account for the ASN.1 encoding overhead here.
+			 *
+			 * NIST P192/256/384 may prepend a '0' to a coordinate
+			 * to indicate a positive integer. NIST P521 does not.
+			 */
+			if (strcmp(pkey->pkey_algo, "ecdsa-nist-p521") != 0)
+				slen += 1;
+			/* Length of encoding the x & y coordinates */
+			slen = 2 * (slen + 2);
+			/*
+			 * If coordinate encoding takes at least 128 bytes then
+			 * an additional byte for length encoding is needed.
+			 */
+			info->max_sig_size = 1 + (slen >= 128) + 1 + slen;
+		} else if (strcmp(params->encoding, "p1363") == 0) {
+			info->max_sig_size = 2 * len;
+		}
 	} else {
 		info->max_data_size = len;
 		info->max_sig_size = len;
crypto/ecdsa-p1363.c new file +155 -0

155 lines not on this page · raw message

crypto/ecdsa.c +8 -0
index 86fc64cb1aee..63cffd978ebd 100644
@@ -291,8 +291,15 @@ static int __init ecdsa_init(void)
 	if (ret)
 		goto x962_tmpl_error;
 
+	ret = crypto_register_template(&ecdsa_p1363_tmpl);
+	if (ret)
+		goto p1363_tmpl_error;
+
 	return 0;
 
+p1363_tmpl_error:
+	crypto_unregister_template(&ecdsa_x962_tmpl);
+
 x962_tmpl_error:
 	crypto_unregister_akcipher(&ecdsa_nist_p521);
 
@@ -311,6 +318,7 @@ static int __init ecdsa_init(void)
 static void __exit ecdsa_exit(void)
 {
 	crypto_unregister_template(&ecdsa_x962_tmpl);
+	crypto_unregister_template(&ecdsa_p1363_tmpl);
 
 	if (ecdsa_nist_p192_registered)
 		crypto_unregister_akcipher(&ecdsa_nist_p192);
crypto/testmgr.c +18 -0
index 5d3af7c8e723..fab2b453b3e9 100644
@@ -5427,6 +5427,24 @@ static const struct alg_test_desc alg_test_descs[] = {
 		.suite = {
 			.hash = __VECS(nhpoly1305_tv_template)
 		}
+	}, {
+		.alg = "p1363(ecdsa-nist-p192)",
+		.test = alg_test_null,
+	}, {
+		.alg = "p1363(ecdsa-nist-p256)",
+		.test = alg_test_akcipher,
+		.fips_allowed = 1,
+		.suite = {
+			.akcipher = __VECS(p1363_ecdsa_nist_p256_tv_template)
+		}
+	}, {
+		.alg = "p1363(ecdsa-nist-p384)",
+		.test = alg_test_null,
+		.fips_allowed = 1,
+	}, {
+		.alg = "p1363(ecdsa-nist-p521)",
+		.test = alg_test_null,
+		.fips_allowed = 1,
 	}, {
 		.alg = "pcbc(fcrypt)",
 		.test = alg_test_skcipher,
crypto/testmgr.h +34 -0
index d052d18c8f5b..cdd33dc19396 100644
@@ -2024,6 +2024,40 @@ static const struct akcipher_testvec x962_ecdsa_nist_p521_tv_template[] = {
 	},
 };
 
+/*
+ * ECDSA P1363 test vectors.
+ *
+ * Identical to ECDSA test vectors, except signature in "c" is P1363 encoded.
+ */
+static const struct akcipher_testvec p1363_ecdsa_nist_p256_tv_template[] = {
+	{
+	.key =
+	"\x04\xf1\xea\xc4\x53\xf3\xb9\x0e\x9f\x7e\xad\xe3\xea\xd7\x0e\x0f"
+	"\xd6\x98\x9a\xca\x92\x4d\x0a\x80\xdb\x2d\x45\xc7\xec\x4b\x97\x00"
+	"\x2f\xe9\x42\x6c\x29\xdc\x55\x0e\x0b\x53\x12\x9b\x2b\xad\x2c\xe9"
+	"\x80\xe6\xc5\x43\xc2\x1d\x5e\xbb\x65\x21\x50\xb6\x37\xb0\x03\x8e"
+	"\xb8",
+	.key_len = 65,
+	.params =
+	"\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
+	"\xce\x3d\x03\x01\x07",
+	.param_len = 21,
+	.m =
+	"\x8f\x43\x43\x46\x64\x8f\x6b\x96\xdf\x89\xdd\xa9\x01\xc5\x17\x6b"
+	"\x10\xa6\xd8\x39\x61\xdd\x3c\x1a\xc8\x8b\x59\xb2\xdc\x32\x7a\xa4",
+	.m_size = 32,
+	.algo = OID_id_ecdsa_with_sha256,
+	.c =
+	"\x08\x31\xfa\x74\x0d\x1d\x21\x5d\x09\xdc\x29\x63\xa8\x1a\xad\xfc"
+	"\xac\x44\xc3\xe8\x24\x11\x2d\xa4\x91\xdc\x02\x67\xdc\x0c\xd0\x82"
+	"\xbd\xff\xce\xee\x42\xc3\x97\xff\xf9\xa9\x81\xac\x4a\x50\xd0\x91"
+	"\x0a\x6e\x1b\xc4\xaf\xe1\x83\xc3\x4f\x2a\x65\x35\x23\xe3\x1d\xfa",
+	.c_size = 64,
+	.public_key_vec = true,
+	.siggen_sigver_test = true,
+	},
+};
+
 /*
  * EC-RDSA test vectors are generated by gost-engine.
  */
include/crypto/internal/ecc.h +1 -0
index 568b7c5352b9..af0763d859ce 100644
@@ -294,4 +294,5 @@ void ecc_point_mult_shamir(const struct ecc_point *result,
 			   const struct ecc_curve *curve);
 
 extern struct crypto_template ecdsa_x962_tmpl;
+extern struct crypto_template ecdsa_p1363_tmpl;
 #endif
Signature · 1 line
2.43.0
Reviewed-by on v1 5/5
#19 lore raw

Re: [PATCH 5/5] crypto: ecdsa - Support P1363 signature decoding

Quoted 18 lines · On Mon, 29 Jul 2024 15:51:00 +0200 Lukas Wunner <[email protected]> wrote:

Alternatively to the X9.62 encoding of ecdsa signatures, which uses ASN.1 and is already supported by the kernel, there's another common encoding called P1363. It stores r and s as the concatenation of two big endian, unsigned integers. The name originates from IEEE P1363.

Add a P1363 template in support of the forthcoming SPDM library (Security Protocol and Data Model) for PCI device authentication.

P1363 is prescribed by SPDM 1.2.1 margin no 44:

"For ECDSA signatures, excluding SM2, in SPDM, the signature shall be
the concatenation of r and s. The size of r shall be the size of the selected curve. Likewise, the size of s shall be the size of the selected curve. See BaseAsymAlgo in NEGOTIATE_ALGORITHMS for the size of r and s. The byte order for r and s shall be in big endian order. When placing ECDSA signatures into an SPDM signature field, r shall come first followed by s."

Quoted 2 lines

One trivial follow on from previous patch. Up to you though as style comment only. FWIW as this all gives me a headache ;)

Quoted 46 lines
crypto/ecdsa-p1363.c new file +39 -0
new file mode 100644
index 000000000000..c0610d88aa9e
@@ -0,0 +1,155 @@
 
+static int ecdsa_p1363_create(struct crypto_template *tmpl, struct rtattr **tb)
+{
+	struct crypto_akcipher_spawn *spawn;
+	struct akcipher_instance *inst;
+	struct akcipher_alg *ecdsa_alg;
+	u32 mask;
+	int err;
+
+	err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_AKCIPHER, &mask);
+	if (err)
+		return err;
+
+	inst = kzalloc(sizeof(*inst) + sizeof(*spawn), GFP_KERNEL);
+	if (!inst)
+		return -ENOMEM;
+
+	spawn = akcipher_instance_ctx(inst);
+
+	err = crypto_grab_akcipher(spawn, akcipher_crypto_instance(inst),
+				   crypto_attr_alg_name(tb[1]), 0, mask);
+	if (err)
+		goto err_free_inst;
+
+	ecdsa_alg = crypto_spawn_akcipher_alg(spawn);
+
+	err = -EINVAL;
+	if (strncmp(ecdsa_alg->base.cra_name, "ecdsa", 5) != 0)
+		goto err_free_inst;
+
+	err = crypto_inst_setname(akcipher_crypto_instance(inst), tmpl->name,
+				  &ecdsa_alg->base);
+	if (err)
+		goto err_free_inst;
+
+	inst->alg.base.cra_priority = ecdsa_alg->base.cra_priority;
+	inst->alg.base.cra_ctxsize = sizeof(struct ecdsa_p1363_ctx);
+
+	inst->alg.init = ecdsa_p1363_init_tfm;
+	inst->alg.exit = ecdsa_p1363_exit_tfm;

crypto/ecdsa-p1363.c line 143

Quoted 10 lines
crypto/ecdsa-p1363.c new file +10 -0
+
+	inst->alg.verify = ecdsa_p1363_verify;
+	inst->alg.max_size = ecdsa_p1363_max_size;
+	inst->alg.set_pub_key = ecdsa_p1363_set_pub_key;
+
+	inst->free = ecdsa_p1363_free;
+
+	err = akcipher_register_instance(tmpl, inst);
+	if (err) {
+err_free_inst:

Same comment as in previous patch. I'd use a separate error path after a return 0 to improve readability.

Quoted 4 lines
crypto/ecdsa-p1363.c new file +4 -0
+		ecdsa_p1363_free(inst);
+	}
+	return err;
+}