2021-05-12 14:06:22

by Hongbo Li

[permalink] [raw]
Subject: [PATCH 0/7] crypto: add eddsa support for x509

From: Hongbo Li <[email protected]>

This series of patches add support for x509 cert signed by eddsa,
which is described in RFC8032 [1], currently ed25519 only.

According to RFC8032 section 4 [2], there're two variants: PureEdDSA and
HashEdDSA. These patches support PureEdDSA which named Ed25519.

Patch1 fix a memory leak bug in sm2.

Patch2 fix a mpi_resize bug, this bug will cause eddsa verification failed.

Patch3 exports some mpi common functions.

Patch4 makes x509 layer support eddsa.

Patch5 moves some common code in sm2 to separate files. These code is also
used by eddsa.

Patch6 is the implementation of eddsa verification according to RFC8032
section 5.1.7 [3].

Patch7 adds test vector for eddsa.

Test by the following script:

keyctl newring test @u

while :; do
certfile="cert.der"

openssl req \
-x509 \
-newkey ED25519 \
-keyout key.pem \
-days 365 \
-subj '/CN=test' \
-nodes \
-outform der \
-out ${certfile} 2>/dev/null

exp=0
id=$(keyctl padd asymmetric testkey %keyring:test < "${certfile}")
rc=$?
if [ $rc -ne $exp ]; then
case "$exp" in
0) echo "Error: Could not load ed25519 certificate $certfile!";
esac
exit 1
else
case "$rc" in
0) printf "load ed25519 cert keyid: %-10s\n" $id;
esac
fi
done

Best Regards

Hongbo

[1] https://datatracker.ietf.org/doc/html/rfc8032
[2] https://datatracker.ietf.org/doc/html/rfc8032#section-4
[3] https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.7

Hongbo Li (7):
crypto: fix a memory leak in sm2
lib/mpi: use kcalloc in mpi_resize
lib/mpi: export some common function
x509: add support for eddsa
crypto: move common code in sm2 to ec_mpi.c and ec_mpi.h
crypto: support ed25519 x509 cert
crypto: add eddsa test vector

crypto/Kconfig | 15 ++++
crypto/Makefile | 4 +
crypto/asymmetric_keys/public_key.c | 73 +++++++++++++--
crypto/asymmetric_keys/x509_cert_parser.c | 14 ++-
crypto/asymmetric_keys/x509_public_key.c | 4 +-
crypto/sm2.c | 104 +---------------------
crypto/testmgr.c | 6 ++
crypto/testmgr.h | 32 +++++++
include/linux/oid_registry.h | 1 +
lib/mpi/mpi-add.c | 4 +-
lib/mpi/mpiutil.c | 2 +-
11 files changed, 146 insertions(+), 113 deletions(-)

--
2.27.0


2021-05-12 14:06:25

by Hongbo Li

[permalink] [raw]
Subject: [PATCH 2/7] lib/mpi: use kcalloc in mpi_resize

From: Hongbo Li <[email protected]>

We should set the additional space to 0 in mpi_resize().
So use kcalloc() instead of kmalloc_array().

Signed-off-by: Hongbo Li <[email protected]>
---
lib/mpi/mpiutil.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/mpi/mpiutil.c b/lib/mpi/mpiutil.c
index 3c63710..e6c4b31 100644
--- a/lib/mpi/mpiutil.c
+++ b/lib/mpi/mpiutil.c
@@ -148,7 +148,7 @@ int mpi_resize(MPI a, unsigned nlimbs)
return 0; /* no need to do it */

if (a->d) {
- p = kmalloc_array(nlimbs, sizeof(mpi_limb_t), GFP_KERNEL);
+ p = kcalloc(nlimbs, sizeof(mpi_limb_t), GFP_KERNEL);
if (!p)
return -ENOMEM;
memcpy(p, a->d, a->alloced * sizeof(mpi_limb_t));
--
1.8.3.1

2021-05-12 14:07:13

by Hongbo Li

[permalink] [raw]
Subject: [PATCH 4/7] x509: add support for eddsa

From: Hongbo Li <[email protected]>

This patch make x509 support eddsa(currently ed25519). According to
RFC8032 section 5.1.7[1], the digest is not on the original message,
but on a special formated message string:
SHA512(dom2(F, C) || R || A || PH(M))

[1]: https://tools.ietf.org/html/rfc8032#section-5.1.7

Signed-off-by: Hongbo Li <[email protected]>
---
crypto/asymmetric_keys/public_key.c | 73 +++++++++++++++++++++++++++----
crypto/asymmetric_keys/x509_cert_parser.c | 14 +++++-
crypto/asymmetric_keys/x509_public_key.c | 4 +-
include/linux/oid_registry.h | 1 +
4 files changed, 82 insertions(+), 10 deletions(-)

diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
index 4fefb21..c1236a8 100644
--- a/crypto/asymmetric_keys/public_key.c
+++ b/crypto/asymmetric_keys/public_key.c
@@ -251,8 +251,8 @@ static int software_key_eds_op(struct kernel_pkey_params *params,
}

#if IS_REACHABLE(CONFIG_CRYPTO_SM2)
-static int cert_sig_digest_update(const struct public_key_signature *sig,
- struct crypto_akcipher *tfm_pkey)
+static int sm2_cert_sig_digest_update(const struct public_key_signature *sig,
+ struct crypto_akcipher *tfm_pkey)
{
struct crypto_shash *tfm;
struct shash_desc *desc;
@@ -297,7 +297,7 @@ static int cert_sig_digest_update(const struct public_key_signature *sig,
return ret;
}
#else
-static inline int cert_sig_digest_update(
+static inline int sm2_cert_sig_digest_update(
const struct public_key_signature *sig,
struct crypto_akcipher *tfm_pkey)
{
@@ -305,6 +305,58 @@ static inline int cert_sig_digest_update(
}
#endif /* ! IS_REACHABLE(CONFIG_CRYPTO_SM2) */

+static int eddsa_cert_sig_digest_update(const struct public_key *pub,
+ const struct public_key_signature *sig)
+{
+ struct crypto_shash *tfm = NULL;
+ struct shash_desc *desc = NULL;
+ int key_size, ret = 0;
+
+ if (strcmp(pub->pkey_algo, "eddsa-25519"))
+ return -ENOPKG;
+
+ tfm = crypto_alloc_shash(sig->hash_algo, 0, 0);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+
+ desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);
+ if (!desc) {
+ ret = -ENOMEM;
+ goto free;
+ }
+
+ desc->tfm = tfm;
+
+ /* RFC8032 section 5.1.7
+ * step 2. SHA512(dom2(F, C) || R || A || PH(M))
+ */
+ key_size = 32;
+ if (sig->s_size != key_size * 2 ||
+ pub->keylen != key_size) {
+ ret = -EINVAL;
+ goto free;
+ }
+
+ ret = crypto_shash_init(desc);
+ if (ret < 0)
+ goto free;
+
+ ret = crypto_shash_update(desc, sig->s, key_size);
+ if (ret < 0)
+ goto free;
+
+ ret = crypto_shash_update(desc, pub->key, key_size);
+ if (ret < 0)
+ goto free;
+
+ ret = crypto_shash_finup(desc, sig->data, sig->data_size, sig->digest);
+
+free:
+ kfree(desc);
+ crypto_free_shash(tfm);
+ return ret;
+}
+
/*
* Verify a signature using a public key.
*/
@@ -358,11 +410,16 @@ int public_key_verify_signature(const struct public_key *pkey,
if (ret)
goto error_free_key;

- if (sig->pkey_algo && strcmp(sig->pkey_algo, "sm2") == 0 &&
- sig->data_size) {
- ret = cert_sig_digest_update(sig, tfm);
- if (ret)
- goto error_free_key;
+ if (sig->pkey_algo && sig->data_size) {
+ if (strcmp(sig->pkey_algo, "sm2") == 0) {
+ ret = sm2_cert_sig_digest_update(sig, tfm);
+ if (ret)
+ goto error_free_key;
+ } else if (strcmp(sig->pkey_algo, "eddsa") == 0) {
+ ret = eddsa_cert_sig_digest_update(pkey, sig);
+ if (ret)
+ goto error_free_key;
+ }
}

sg_init_table(src_sg, 2);
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index 6d00309..3f60c57 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -258,6 +258,9 @@ int x509_note_pkey_algo(void *context, size_t hdrlen,
case OID_SM2_with_SM3:
ctx->cert->sig->hash_algo = "sm3";
goto sm2;
+ case OID_ed25519:
+ ctx->cert->sig->hash_algo = "sha512";
+ goto eddsa;
}

rsa_pkcs1:
@@ -280,6 +283,11 @@ int x509_note_pkey_algo(void *context, size_t hdrlen,
ctx->cert->sig->encoding = "x962";
ctx->algo_oid = ctx->last_oid;
return 0;
+eddsa:
+ ctx->cert->sig->pkey_algo = "eddsa";
+ ctx->cert->sig->encoding = "raw";
+ ctx->algo_oid = ctx->last_oid;
+ return 0;
}

/*
@@ -302,7 +310,8 @@ int x509_note_signature(void *context, size_t hdrlen,
if (strcmp(ctx->cert->sig->pkey_algo, "rsa") == 0 ||
strcmp(ctx->cert->sig->pkey_algo, "ecrdsa") == 0 ||
strcmp(ctx->cert->sig->pkey_algo, "sm2") == 0 ||
- strcmp(ctx->cert->sig->pkey_algo, "ecdsa") == 0) {
+ strcmp(ctx->cert->sig->pkey_algo, "ecdsa") == 0 ||
+ strcmp(ctx->cert->sig->pkey_algo, "eddsa") == 0) {
/* Discard the BIT STRING metadata */
if (vlen < 1 || *(const u8 *)value != 0)
return -EBADMSG;
@@ -517,6 +526,9 @@ int x509_extract_key_data(void *context, size_t hdrlen,
return -ENOPKG;
}
break;
+ case OID_ed25519:
+ ctx->cert->pub->pkey_algo = "eddsa-25519";
+ break;
default:
return -ENOPKG;
}
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
index 3d45161..a8fd368 100644
--- a/crypto/asymmetric_keys/x509_public_key.c
+++ b/crypto/asymmetric_keys/x509_public_key.c
@@ -131,7 +131,9 @@ int x509_check_for_self_signed(struct x509_certificate *cert)
ret = -EKEYREJECTED;
if (strcmp(cert->pub->pkey_algo, cert->sig->pkey_algo) != 0 &&
(strncmp(cert->pub->pkey_algo, "ecdsa-", 6) != 0 ||
- strcmp(cert->sig->pkey_algo, "ecdsa") != 0))
+ strcmp(cert->sig->pkey_algo, "ecdsa") != 0) &&
+ (strncmp(cert->pub->pkey_algo, "eddsa-", 6) != 0 ||
+ strcmp(cert->sig->pkey_algo, "eddsa") != 0))
goto out;

ret = public_key_verify_signature(cert->pub, cert->sig);
diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
index cc64d94..d84bb86 100644
--- a/include/linux/oid_registry.h
+++ b/include/linux/oid_registry.h
@@ -64,6 +64,7 @@ enum OID {

OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */
OID_sha1, /* 1.3.14.3.2.26 */
+ OID_ed25519, /* 1.3.101.112 */
OID_id_ansip384r1, /* 1.3.132.0.34 */
OID_sha256, /* 2.16.840.1.101.3.4.2.1 */
OID_sha384, /* 2.16.840.1.101.3.4.2.2 */
--
1.8.3.1

2021-05-12 14:07:31

by Hongbo Li

[permalink] [raw]
Subject: [PATCH 5/7] crypto: move common code in sm2 to ec_mpi.c and ec_mpi.h

From: Hongbo Li <[email protected]>

Some structs and functions in sm2 are common codes, and could be
used by the following eddsa patch. So move them to common files:
ec_mpi.c and ec_mpi.h.

Signed-off-by: Hongbo Li <[email protected]>
---
crypto/Kconfig | 4 +++
crypto/Makefile | 1 +
crypto/ec_mpi.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++
crypto/ec_mpi.h | 37 ++++++++++++++++++++++
crypto/sm2.c | 98 ++-------------------------------------------------------
5 files changed, 127 insertions(+), 95 deletions(-)
create mode 100644 crypto/ec_mpi.c
create mode 100644 crypto/ec_mpi.h

diff --git a/crypto/Kconfig b/crypto/Kconfig
index 4a0d187..75ae7d3 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -265,6 +265,9 @@ config CRYPTO_ECRDSA
standard algorithms (called GOST algorithms). Only signature verification
is implemented.

+config CRYPTO_EC_MPI
+ tristate
+
config CRYPTO_SM2
tristate "SM2 algorithm"
select CRYPTO_SM3
@@ -272,6 +275,7 @@ config CRYPTO_SM2
select CRYPTO_MANAGER
select MPILIB
select ASN1
+ select CRYPTO_EC_MPI
help
Generic implementation of the SM2 public key algorithm. It was
published by State Encryption Management Bureau, China.
diff --git a/crypto/Makefile b/crypto/Makefile
index 10526d4..8afb393 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -177,6 +177,7 @@ obj-$(CONFIG_CRYPTO_OFB) += ofb.o
obj-$(CONFIG_CRYPTO_ECC) += ecc.o
obj-$(CONFIG_CRYPTO_ESSIV) += essiv.o
obj-$(CONFIG_CRYPTO_CURVE25519) += curve25519-generic.o
+obj-$(CONFIG_CRYPTO_EC_MPI) += ec_mpi.o

ecdh_generic-y += ecdh.o
ecdh_generic-y += ecdh_helper.o
diff --git a/crypto/ec_mpi.c b/crypto/ec_mpi.c
new file mode 100644
index 0000000..a537e6f
--- /dev/null
+++ b/crypto/ec_mpi.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EC MPI common functions.
+ *
+ * Copyright (c) 2020, Alibaba Group.
+ * Authors: Tianjia Zhang <[email protected]>
+ */
+
+#include <linux/module.h>
+#include <linux/mpi.h>
+#include "ec_mpi.h"
+
+int ec_mpi_ctx_init(struct mpi_ec_ctx *ec, const struct ecc_domain_parms *ecp)
+{
+ MPI p, a, b;
+ MPI x, y;
+ int rc = -EINVAL;
+
+ p = mpi_scanval(ecp->p);
+ a = mpi_scanval(ecp->a);
+ b = mpi_scanval(ecp->b);
+ if (!p || !a || !b)
+ goto free_p;
+
+ x = mpi_scanval(ecp->g_x);
+ y = mpi_scanval(ecp->g_y);
+ if (!x || !y)
+ goto free;
+
+ rc = -ENOMEM;
+
+ ec->Q = mpi_point_new(0);
+ if (!ec->Q)
+ goto free;
+
+ /* mpi_ec_setup_elliptic_curve */
+ ec->G = mpi_point_new(0);
+ if (!ec->G) {
+ mpi_point_release(ec->Q);
+ goto free;
+ }
+
+ mpi_set(ec->G->x, x);
+ mpi_set(ec->G->y, y);
+ mpi_set_ui(ec->G->z, 1);
+
+ rc = -EINVAL;
+ ec->n = mpi_scanval(ecp->n);
+ if (!ec->n) {
+ mpi_point_release(ec->Q);
+ mpi_point_release(ec->G);
+ goto free;
+ }
+
+ ec->h = ecp->h;
+ ec->name = ecp->desc;
+ mpi_ec_init(ec, ecp->model, ecp->dialect, 0, p, a, b);
+
+ rc = 0;
+
+free:
+ mpi_free(x);
+ mpi_free(y);
+free_p:
+ mpi_free(p);
+ mpi_free(a);
+ mpi_free(b);
+
+ return rc;
+}
+EXPORT_SYMBOL(ec_mpi_ctx_init);
+
+void ec_mpi_ctx_deinit(struct mpi_ec_ctx *ec)
+{
+ mpi_ec_deinit(ec);
+
+ memset(ec, 0, sizeof(*ec));
+}
+EXPORT_SYMBOL(ec_mpi_ctx_deinit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tianjia Zhang <[email protected]>");
diff --git a/crypto/ec_mpi.h b/crypto/ec_mpi.h
new file mode 100644
index 0000000..e1f6d3aa
--- /dev/null
+++ b/crypto/ec_mpi.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * EC MPI common structs.
+ *
+ * Copyright (c) 2020, Alibaba Group.
+ * Authors: Tianjia Zhang <[email protected]>
+ */
+
+#include <linux/mpi.h>
+
+struct ecc_domain_parms {
+ const char *desc; /* Description of the curve. */
+ unsigned int nbits; /* Number of bits. */
+ unsigned int fips:1; /* True if this is a FIPS140-2 approved curve */
+
+ /* The model describing this curve. This is mainly used to select
+ * the group equation.
+ */
+ enum gcry_mpi_ec_models model;
+
+ /* The actual ECC dialect used. This is used for curve specific
+ * optimizations and to select encodings etc.
+ */
+ enum ecc_dialects dialect;
+
+ const char *p; /* The prime defining the field. */
+ const char *a, *b; /* The coefficients. For Twisted Edwards
+ * Curves b is used for d. For Montgomery
+ * Curves (a,b) has ((A-2)/4,B^-1).
+ */
+ const char *n; /* The order of the base point. */
+ const char *g_x, *g_y; /* Base point. */
+ unsigned int h; /* Cofactor. */
+};
+
+int ec_mpi_ctx_init(struct mpi_ec_ctx *ec, const struct ecc_domain_parms *ecp);
+void ec_mpi_ctx_deinit(struct mpi_ec_ctx *ec);
diff --git a/crypto/sm2.c b/crypto/sm2.c
index db8a4a2..ea1676b 100644
--- a/crypto/sm2.c
+++ b/crypto/sm2.c
@@ -9,42 +9,17 @@
*/

#include <linux/module.h>
-#include <linux/mpi.h>
#include <crypto/internal/akcipher.h>
#include <crypto/akcipher.h>
#include <crypto/hash.h>
#include <crypto/sm3_base.h>
#include <crypto/rng.h>
#include <crypto/sm2.h>
+#include "ec_mpi.h"
#include "sm2signature.asn1.h"

#define MPI_NBYTES(m) ((mpi_get_nbits(m) + 7) / 8)

-struct ecc_domain_parms {
- const char *desc; /* Description of the curve. */
- unsigned int nbits; /* Number of bits. */
- unsigned int fips:1; /* True if this is a FIPS140-2 approved curve */
-
- /* The model describing this curve. This is mainly used to select
- * the group equation.
- */
- enum gcry_mpi_ec_models model;
-
- /* The actual ECC dialect used. This is used for curve specific
- * optimizations and to select encodings etc.
- */
- enum ecc_dialects dialect;
-
- const char *p; /* The prime defining the field. */
- const char *a, *b; /* The coefficients. For Twisted Edwards
- * Curves b is used for d. For Montgomery
- * Curves (a,b) has ((A-2)/4,B^-1).
- */
- const char *n; /* The order of the base point. */
- const char *g_x, *g_y; /* Base point. */
- unsigned int h; /* Cofactor. */
-};
-
static const struct ecc_domain_parms sm2_ecp = {
.desc = "sm2p256v1",
.nbits = 256,
@@ -60,73 +35,6 @@ struct ecc_domain_parms {
.h = 1
};

-static int sm2_ec_ctx_init(struct mpi_ec_ctx *ec)
-{
- const struct ecc_domain_parms *ecp = &sm2_ecp;
- MPI p, a, b;
- MPI x, y;
- int rc = -EINVAL;
-
- p = mpi_scanval(ecp->p);
- a = mpi_scanval(ecp->a);
- b = mpi_scanval(ecp->b);
- if (!p || !a || !b)
- goto free_p;
-
- x = mpi_scanval(ecp->g_x);
- y = mpi_scanval(ecp->g_y);
- if (!x || !y)
- goto free;
-
- rc = -ENOMEM;
-
- ec->Q = mpi_point_new(0);
- if (!ec->Q)
- goto free;
-
- /* mpi_ec_setup_elliptic_curve */
- ec->G = mpi_point_new(0);
- if (!ec->G) {
- mpi_point_release(ec->Q);
- goto free;
- }
-
- mpi_set(ec->G->x, x);
- mpi_set(ec->G->y, y);
- mpi_set_ui(ec->G->z, 1);
-
- rc = -EINVAL;
- ec->n = mpi_scanval(ecp->n);
- if (!ec->n) {
- mpi_point_release(ec->Q);
- mpi_point_release(ec->G);
- goto free;
- }
-
- ec->h = ecp->h;
- ec->name = ecp->desc;
- mpi_ec_init(ec, ecp->model, ecp->dialect, 0, p, a, b);
-
- rc = 0;
-
-free:
- mpi_free(x);
- mpi_free(y);
-free_p:
- mpi_free(p);
- mpi_free(a);
- mpi_free(b);
-
- return rc;
-}
-
-static void sm2_ec_ctx_deinit(struct mpi_ec_ctx *ec)
-{
- mpi_ec_deinit(ec);
-
- memset(ec, 0, sizeof(*ec));
-}
-
/* RESULT must have been initialized and is set on success to the
* point given by VALUE.
*/
@@ -416,14 +324,14 @@ static int sm2_init_tfm(struct crypto_akcipher *tfm)
{
struct mpi_ec_ctx *ec = akcipher_tfm_ctx(tfm);

- return sm2_ec_ctx_init(ec);
+ return ec_mpi_ctx_init(ec, &sm2_ecp);
}

static void sm2_exit_tfm(struct crypto_akcipher *tfm)
{
struct mpi_ec_ctx *ec = akcipher_tfm_ctx(tfm);

- sm2_ec_ctx_deinit(ec);
+ ec_mpi_ctx_deinit(ec);
}

static struct akcipher_alg sm2 = {
--
1.8.3.1

2021-05-12 14:07:34

by Hongbo Li

[permalink] [raw]
Subject: [PATCH 6/7] crypto: ed25519 cert verification

From: Hongbo Li <[email protected]>

This patch adds the support of eddsa(currently ed25519) which is described
in RFC8032 section 5.1.7 [1].

[1]: https://tools.ietf.org/html/rfc8032#section-5.1.7

Signed-off-by: Hongbo Li <[email protected]>
---
crypto/Kconfig | 11 ++
crypto/Makefile | 3 +
crypto/eddsa.c | 326 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 340 insertions(+)
create mode 100644 crypto/eddsa.c

diff --git a/crypto/Kconfig b/crypto/Kconfig
index 75ae7d3..6463c85 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -268,6 +268,17 @@ config CRYPTO_ECRDSA
config CRYPTO_EC_MPI
tristate

+config CRYPTO_EDDSA
+ tristate "EDDSA (ed25519) algorithm"
+ select CRYPTO_ECC
+ select CRYPTO_EC_MPI
+ select CRYPTO_AKCIPHER
+ select ASN1
+ help
+ Edwards-curve Digital Signature Algorithm (ed25519) is a variant
+ of Schnorr's signature system with (possibly twisted) Edwards curves.
+ Only signature verification is implemented.
+
config CRYPTO_SM2
tristate "SM2 algorithm"
select CRYPTO_SM3
diff --git a/crypto/Makefile b/crypto/Makefile
index 8afb393..2bbdfad 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -56,6 +56,9 @@ ecdsa_generic-y += ecdsa.o
ecdsa_generic-y += ecdsasignature.asn1.o
obj-$(CONFIG_CRYPTO_ECDSA) += ecdsa_generic.o

+eddsa_generic-y += eddsa.o
+obj-$(CONFIG_CRYPTO_EDDSA) += eddsa_generic.o
+
crypto_acompress-y := acompress.o
crypto_acompress-y += scompress.o
obj-$(CONFIG_CRYPTO_ACOMP2) += crypto_acompress.o
diff --git a/crypto/eddsa.c b/crypto/eddsa.c
new file mode 100644
index 0000000..06e86be
--- /dev/null
+++ b/crypto/eddsa.c
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EDDSA generic algorithm.
+ *
+ * Copyright (c) 2021 Hongbo Li <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/mpi.h>
+#include <linux/module.h>
+#include <linux/oid_registry.h>
+#include <crypto/hash.h>
+#include <crypto/sha2.h>
+#include <crypto/ecdh.h>
+#include <crypto/curve25519.h>
+#include <crypto/internal/akcipher.h>
+#include "ec_mpi.h"
+
+struct eddsa_ctx {
+ enum OID algo_oid;
+ struct mpi_ec_ctx ec_ctx;
+};
+
+static MPI p58;
+static MPI seven;
+static MPI m1;
+
+static const struct ecc_domain_parms ed25519_domain_params = {
+ .desc = "ed25519",
+ .nbits = 256,
+ .fips = 0,
+ .model = MPI_EC_EDWARDS,
+ .dialect = ECC_DIALECT_ED25519,
+ .p = "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED",
+ .a = "-0x01",
+ .b = "-0x2DFC9311D490018C7338BF8688861767FF8FF5B2BEBE27548A14B235ECA6874A",
+ .n = "0x1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED",
+ .g_x = "0x216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A",
+ .g_y = "0x6666666666666666666666666666666666666666666666666666666666666658",
+ .h = 8,
+};
+
+static void reverse_buffer(u8 *buffer, u32 length)
+{
+ u32 tmp, i;
+
+ for (i = 0; i < length / 2; i++) {
+ tmp = buffer[i];
+ buffer[i] = buffer[length - 1 - i];
+ buffer[length - 1 - i] = tmp;
+ }
+}
+
+static int eddsa_encode_x_y(MPI x, MPI y, u8 *buf, u32 key_size)
+{
+ memcpy(buf, y->d, key_size);
+ if (mpi_test_bit(x, 0))
+ buf[key_size - 1] |= 0x80;
+
+ return 0;
+}
+
+int ecc_eddsa_encodepoint(MPI_POINT point, struct mpi_ec_ctx *ec,
+ MPI x, MPI y, u8 *buf, u32 key_size)
+{
+ if (mpi_ec_get_affine(x, y, point, ec))
+ return -EINVAL;
+
+ return eddsa_encode_x_y(x, y, buf, key_size);
+}
+
+/* Recover X from Y and SIGN (which actually is a parity bit). */
+static int eddsa_recover_x(MPI x, MPI y, int sign, struct mpi_ec_ctx *ec)
+{
+ MPI u, v, v3, t;
+ int ret = 0;
+
+ if (ec->dialect != ECC_DIALECT_ED25519)
+ return -ENOPKG;
+
+ u = mpi_new(0);
+ v = mpi_new(0);
+ v3 = mpi_new(0);
+ t = mpi_new(0);
+
+ /* Compute u and v */
+ /* u = y^2 */
+ mpi_mulm(u, y, y, ec->p);
+ /* v = b*y^2 */
+ mpi_mulm(v, ec->b, u, ec->p);
+ /* u = y^2-1 */
+ mpi_sub_ui(u, u, 1);
+ /* v = b*y^2+1 */
+ mpi_add_ui(v, v, 1);
+
+ /* Compute sqrt(u/v) */
+ /* v3 = v^3 */
+ mpi_powm(v3, v, mpi_const(MPI_C_THREE), ec->p);
+ /* t = v3 * v3 * u * v = u * v^7 */
+ mpi_powm(t, v, seven, ec->p);
+ mpi_mulm(t, t, u, ec->p);
+ /* t = t^((p-5)/8) = (u * v^7)^((p-5)/8) */
+ mpi_powm(t, t, p58, ec->p);
+ /* x = t * u * v^3 = (u * v^3) * (u * v^7)^((p-5)/8) */
+ mpi_mulm(t, t, u, ec->p);
+ mpi_mulm(x, t, v3, ec->p);
+
+ /* Adjust if needed. */
+ /* t = v * x^2 */
+ mpi_mulm(t, x, x, ec->p);
+ mpi_mulm(t, t, v, ec->p);
+ /* -t == u ? x = x * sqrt(-1) */
+ mpi_sub(t, ec->p, t);
+ if (!mpi_cmp(t, u)) {
+ mpi_mulm(x, x, m1, ec->p);
+ /* t = v * x^2 */
+ mpi_mulm(t, x, x, ec->p);
+ mpi_mulm(t, t, v, ec->p);
+ /* -t == u ? x = x * sqrt(-1) */
+ mpi_sub(t, ec->p, t);
+ if (!mpi_cmp(t, u))
+ ret = -EINVAL;
+ }
+
+ /* Choose the desired square root according to parity */
+ if (mpi_test_bit(x, 0) != !!sign)
+ mpi_sub(x, ec->p, x);
+
+ mpi_free(t);
+ mpi_free(v3);
+ mpi_free(v);
+ mpi_free(u);
+
+ return ret;
+}
+
+static int ecc_eddsa_decodepoint(const u8 *pk, int key_size,
+ struct mpi_ec_ctx *ec, MPI_POINT result)
+{
+ MPI y;
+ u8 *rawmpi;
+ int sign, ret = 0;
+
+ rawmpi = kmalloc(key_size, GFP_KERNEL);
+ if (!rawmpi)
+ return -ENOMEM;
+ memcpy(rawmpi, pk, key_size);
+ reverse_buffer(rawmpi, key_size);
+
+ sign = !!(rawmpi[0] & 0x80);
+ rawmpi[0] &= 0x7f;
+
+ y = mpi_read_raw_data(rawmpi, key_size);
+ if (!y) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ mpi_normalize(y);
+ mpi_set(result->y, y);
+ mpi_free(y);
+
+ ret = eddsa_recover_x(result->x, result->y, sign, ec);
+ mpi_set_ui(result->z, 1);
+out:
+ kfree(rawmpi);
+ return ret;
+}
+
+int eddsa_verify(struct akcipher_request *req)
+{
+ struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+ struct eddsa_ctx *ctx = akcipher_tfm_ctx(tfm);
+ struct mpi_ec_ctx *ec = &ctx->ec_ctx;
+ struct gcry_mpi_point sb, ka;
+ MPI s = NULL;
+ MPI k = NULL;
+ u8 sig[CURVE25519_KEY_SIZE * 2], digest[SHA512_DIGEST_SIZE];
+ u8 *buf;
+ u32 key_size;
+ int ret = 0;
+
+ if (ctx->algo_oid != OID_ed25519)
+ return -ENOPKG;
+
+ key_size = CURVE25519_KEY_SIZE;
+
+ if (!ec->Q || req->src_len != key_size * 2)
+ return -EINVAL;
+
+ 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);
+
+ reverse_buffer(digest, SHA512_DIGEST_SIZE);
+ k = mpi_read_raw_data(digest, SHA512_DIGEST_SIZE);
+
+ reverse_buffer(sig + key_size, key_size);
+ s = mpi_read_raw_data(sig + key_size, key_size);
+
+ mpi_point_init(&sb);
+ mpi_point_init(&ka);
+
+ mpi_ec_mul_point(&sb, s, ec->G, ec);
+ mpi_ec_mul_point(&ka, k, ec->Q, ec);
+ mpi_sub(ka.x, ec->p, ka.x);
+ mpi_ec_add_points(&sb, &sb, &ka, ec);
+
+ buf = kmalloc(key_size, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = ecc_eddsa_encodepoint(&sb, ec, s, k, buf, key_size);
+ if (ret)
+ goto out;
+
+ if (memcmp(buf, sig, key_size))
+ ret = -EKEYREJECTED;
+
+out:
+ mpi_point_free_parts(&sb);
+ mpi_point_free_parts(&ka);
+ mpi_free(k);
+ mpi_free(s);
+ kfree(buf);
+ return ret;
+}
+
+static int eddsa_set_pub_key(struct crypto_akcipher *tfm, const void *key,
+ unsigned int keylen)
+{
+ struct eddsa_ctx *ctx = akcipher_tfm_ctx(tfm);
+ struct mpi_ec_ctx *ec = &ctx->ec_ctx;
+ const u8 *pk = key;
+
+ if (ctx->algo_oid != OID_ed25519)
+ return -ENOPKG;
+
+ if (keylen != CURVE25519_KEY_SIZE)
+ return -EINVAL;
+
+ return ecc_eddsa_decodepoint(pk, keylen, ec, ec->Q);
+}
+
+u32 eddsa_max_size(struct crypto_akcipher *tfm)
+{
+ struct eddsa_ctx *ctx = akcipher_tfm_ctx(tfm);
+
+ if (ctx->algo_oid == OID_ed25519)
+ return CURVE25519_KEY_SIZE;
+
+ return 0;
+}
+
+static int eddsa_25519_init_tfm(struct crypto_akcipher *tfm)
+{
+ struct eddsa_ctx *ctx = akcipher_tfm_ctx(tfm);
+
+ ctx->algo_oid = OID_ed25519;
+ p58 = mpi_scanval("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD");
+ if (!p58)
+ return -ENOMEM;
+
+ m1 = mpi_scanval("2B8324804FC1DF0B2B4D00993DFBD7A72F431806AD2FE478C4EE1B274A0EA0B0");
+ if (!m1)
+ return -ENOMEM;
+
+ seven = mpi_set_ui(NULL, 7);
+
+ return ec_mpi_ctx_init(&ctx->ec_ctx, &ed25519_domain_params);
+}
+
+static void eddsa_exit_tfm(struct crypto_akcipher *tfm)
+{
+ struct eddsa_ctx *ctx = akcipher_tfm_ctx(tfm);
+
+ ec_mpi_ctx_deinit(&ctx->ec_ctx);
+ mpi_free(p58);
+ mpi_free(seven);
+ mpi_free(m1);
+}
+
+
+static struct akcipher_alg eddsa_25519 = {
+ .verify = eddsa_verify,
+ .set_pub_key = eddsa_set_pub_key,
+ .max_size = eddsa_max_size,
+ .init = eddsa_25519_init_tfm,
+ .exit = eddsa_exit_tfm,
+ .base = {
+ .cra_name = "eddsa-25519",
+ .cra_driver_name = "eddsa-25519-generic",
+ .cra_priority = 100,
+ .cra_module = THIS_MODULE,
+ .cra_ctxsize = sizeof(struct eddsa_ctx),
+ },
+};
+
+static int eddsa_mod_init(void)
+{
+ return crypto_register_akcipher(&eddsa_25519);
+}
+
+static void eddsa_mod_exit(void)
+{
+ crypto_unregister_akcipher(&eddsa_25519);
+}
+
+module_init(eddsa_mod_init);
+module_exit(eddsa_mod_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Hongbo Li <[email protected]>");
+MODULE_ALIAS_CRYPTO("eddsa");
+MODULE_ALIAS_CRYPTO("eddsa-generic");
+MODULE_DESCRIPTION("EDDSA generic algorithm");
--
1.8.3.1

2021-05-12 14:08:04

by Hongbo Li

[permalink] [raw]
Subject: [PATCH 7/7] crypto: add eddsa test vector

From: Hongbo Li <[email protected]>

This patch adds the test vector for ed25519.

Signed-off-by: Hongbo Li <[email protected]>
---
crypto/testmgr.c | 6 ++++++
crypto/testmgr.h | 32 ++++++++++++++++++++++++++++++++
2 files changed, 38 insertions(+)

diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index 10c5b3b..498d186 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -4939,6 +4939,12 @@ static int alg_test_null(const struct alg_test_desc *desc,
.akcipher = __VECS(ecrdsa_tv_template)
}
}, {
+ .alg = "eddsa-25519",
+ .test = alg_test_akcipher,
+ .suite = {
+ .akcipher = __VECS(eddsa_25519_tv_template)
+ }
+ }, {
.alg = "essiv(authenc(hmac(sha256),cbc(aes)),sha256)",
.test = alg_test_aead,
.fips_allowed = 1,
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index 34e4a3d..11807a3 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -1145,6 +1145,38 @@ struct kpp_testvec {
};

/*
+ * EDDSA test vectors.
+ * From RFC8032 section 7.1
+ */
+static const struct akcipher_testvec eddsa_25519_tv_template[] = {
+ {
+ .key =
+ "\x3d\x40\x17\xc3\xe8\x43\x89\x5a\x92\xb7\x0a\xa7\x4d\x1b\x7e\xbc"
+ "\x9c\x98\x2c\xcf\x2e\xc4\x96\x8c\xc0\xcd\x55\xf1\x2a\xf4\x66\x0c",
+ .key_len = 32,
+ /*
+ * RFC8032 section 5.1.7. m is SHA512(dom2(F, C) || R || A || PH(M))
+ * M is 0x72
+ */
+ .m =
+ "\xa2\x71\xdf\x0d\x2b\x0d\x03\xbd\x17\xb4\xed\x9a\x4b\x6a\xfd\xdf"
+ "\x2e\x73\x28\x7f\xd6\x30\xf1\xa1\x37\xd8\x7c\xe8\x73\xa5\x91\xcc"
+ "\x31\xb6\xdd\x85\x2a\x98\xb5\xdd\x12\x26\xfe\x99\x3d\x82\x28\x27"
+ "\x8c\xeb\xa2\x1f\x80\xb8\xfc\x95\x98\x6a\x70\xd7\x1e\xdf\x3f\xaf",
+ .m_size = 64,
+ .c =
+ "\x92\xa0\x09\xa9\xf0\xd4\xca\xb8\x72\x0e\x82\x0b\x5f\x64\x25\x40"
+ "\xa2\xb2\x7b\x54\x16\x50\x3f\x8f\xb3\x76\x22\x23\xeb\xdb\x69\xda"
+ "\x08\x5a\xc1\xe4\x3e\x15\x99\x6e\x45\x8f\x36\x13\xd0\xf1\x1d\x8c"
+ "\x38\x7b\x2e\xae\xb4\x30\x2a\xee\xb0\x0d\x29\x16\x12\xbb\x0c\x00",
+ .c_size = 64,
+ .algo = OID_ed25519,
+ .public_key_vec = true,
+ .siggen_sigver_test = true,
+ }
+};
+
+/*
* PKCS#1 RSA test vectors. Obtained from CAVS testing.
*/
static const struct akcipher_testvec pkcs1pad_rsa_tv_template[] = {
--
1.8.3.1

2021-05-12 21:10:23

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH 0/7] crypto: add eddsa support for x509

On Wed, May 12, 2021 at 10:04:07PM +0800, Hongbo Li wrote:
> From: Hongbo Li <[email protected]>
>
> This series of patches add support for x509 cert signed by eddsa,
> which is described in RFC8032 [1], currently ed25519 only.

It would be helpful to explain how this is related to the kernel's existing
Curve25519 support.

- Eric