From e42fd29fff4c91368a9e002268e960d37b57d6c8 Mon Sep 17 00:00:00 2001 From: Joseph Lefkovitz <85521235+lefkovitzj@users.noreply.github.com> Date: Thu, 22 Aug 2024 16:25:10 -0400 Subject: [PATCH 01/18] Update ecc.rst Correct "SyntaxError: unterminated string literal" in documentation example for public key export with ECC. Close string literal for open "mode" parameter. Should be "wbt" not "wbt. --- Doc/src/public_key/ecc.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/src/public_key/ecc.rst b/Doc/src/public_key/ecc.rst index 5260aead..fe3c34b4 100644 --- a/Doc/src/public_key/ecc.rst +++ b/Doc/src/public_key/ecc.rst @@ -38,7 +38,7 @@ and reimport it later:: You can also export the public key, which is not sensitive:: - >>> with open("mypublickey.pem", "wbt) as f: + >>> with open("mypublickey.pem", "wbt") as f: >>> data = mykey.public_key().export_key() .. _ECC table: From 71f91314391f49f0d7a7eaed3088529402ac4697 Mon Sep 17 00:00:00 2001 From: Helder Eijs Date: Sat, 24 Aug 2024 16:35:29 +0200 Subject: [PATCH 02/18] Rename mont_number() to mont_new_number() --- src/ec_ws.c | 38 +++++++++++++++++++------------------- src/ed448.c | 26 +++++++++++++------------- src/modexp.c | 12 ++++++------ src/mont.c | 22 +++++++++++----------- src/mont.h | 2 +- src/test/README.txt | 12 ++++++++++++ src/test/test_ec_ws.c | 36 ++++++++++++++++++------------------ src/test/test_ed448.c | 8 ++++---- src/test/test_mont.c | 4 ++-- 9 files changed, 86 insertions(+), 74 deletions(-) create mode 100644 src/test/README.txt diff --git a/src/ec_ws.c b/src/ec_ws.c index 77426a78..e0f4e093 100644 --- a/src/ec_ws.c +++ b/src/ec_ws.c @@ -77,29 +77,29 @@ STATIC Workplace *new_workplace(const MontContext *ctx) if (NULL == wp) return NULL; - res = mont_number(&wp->a, 1, ctx); + res = mont_new_number(&wp->a, 1, ctx); if (res) goto cleanup; - res = mont_number(&wp->b, 1, ctx); + res = mont_new_number(&wp->b, 1, ctx); if (res) goto cleanup; - res = mont_number(&wp->c, 1, ctx); + res = mont_new_number(&wp->c, 1, ctx); if (res) goto cleanup; - res = mont_number(&wp->d, 1, ctx); + res = mont_new_number(&wp->d, 1, ctx); if (res) goto cleanup; - res = mont_number(&wp->e, 1, ctx); + res = mont_new_number(&wp->e, 1, ctx); if (res) goto cleanup; - res = mont_number(&wp->f, 1, ctx); + res = mont_new_number(&wp->f, 1, ctx); if (res) goto cleanup; - res = mont_number(&wp->g, 1, ctx); + res = mont_new_number(&wp->g, 1, ctx); if (res) goto cleanup; - res = mont_number(&wp->h, 1, ctx); + res = mont_new_number(&wp->h, 1, ctx); if (res) goto cleanup; - res = mont_number(&wp->i, 1, ctx); + res = mont_new_number(&wp->i, 1, ctx); if (res) goto cleanup; - res = mont_number(&wp->j, 1, ctx); + res = mont_new_number(&wp->j, 1, ctx); if (res) goto cleanup; - res = mont_number(&wp->k, 1, ctx); + res = mont_new_number(&wp->k, 1, ctx); if (res) goto cleanup; - res = mont_number(&wp->scratch, SCRATCHPAD_NR, ctx); + res = mont_new_number(&wp->scratch, SCRATCHPAD_NR, ctx); if (res) goto cleanup; return wp; @@ -1024,7 +1024,7 @@ EXPORT_SYM int ec_ws_new_point(EcPoint **pecp, if (res) goto cleanup; res = mont_from_bytes(&ecp->y, y, len, ctx); if (res) goto cleanup; - res = mont_number(&ecp->z, 1, ctx); + res = mont_new_number(&ecp->z, 1, ctx); if (res) goto cleanup; mont_set(ecp->z, 1, ctx); @@ -1107,9 +1107,9 @@ EXPORT_SYM int ec_ws_get_xy(uint8_t *x, uint8_t *y, size_t len, const EcPoint *e if (NULL == wp) return ERR_MEMORY; - res = mont_number(&xw, 1, ctx); + res = mont_new_number(&xw, 1, ctx); if (res) goto cleanup; - res = mont_number(&yw, 1, ctx); + res = mont_new_number(&yw, 1, ctx); if (res) goto cleanup; ec_projective_to_affine(xw, yw, ecp->x, ecp->y, ecp->z, wp, ctx); @@ -1421,15 +1421,15 @@ EXPORT_SYM int ec_ws_clone(EcPoint **pecp2, const EcPoint *ecp) ecp2->ec_ctx = ecp->ec_ctx; - res = mont_number(&ecp2->x, 1, ctx); + res = mont_new_number(&ecp2->x, 1, ctx); if (res) goto cleanup; mont_copy(ecp2->x, ecp->x, ctx); - res = mont_number(&ecp2->y, 1, ctx); + res = mont_new_number(&ecp2->y, 1, ctx); if (res) goto cleanup; mont_copy(ecp2->y, ecp->y, ctx); - res = mont_number(&ecp2->z, 1, ctx); + res = mont_new_number(&ecp2->z, 1, ctx); if (res) goto cleanup; mont_copy(ecp2->z, ecp->z, ctx); @@ -1496,7 +1496,7 @@ EXPORT_SYM int ec_ws_neg(EcPoint *p) return ERR_NULL; ctx = p->ec_ctx->mont_ctx; - res = mont_number(&tmp, SCRATCHPAD_NR, ctx); + res = mont_new_number(&tmp, SCRATCHPAD_NR, ctx); if (res) return res; diff --git a/src/ed448.c b/src/ed448.c index f7fb6b8b..e96ad7d8 100644 --- a/src/ed448.c +++ b/src/ed448.c @@ -60,19 +60,19 @@ STATIC WorkplaceEd448 *new_workplace(const MontContext *ctx) if (NULL == wp) return NULL; - res = mont_number(&wp->a, 1, ctx); + res = mont_new_number(&wp->a, 1, ctx); if (res) goto cleanup; - res = mont_number(&wp->b, 1, ctx); + res = mont_new_number(&wp->b, 1, ctx); if (res) goto cleanup; - res = mont_number(&wp->c, 1, ctx); + res = mont_new_number(&wp->c, 1, ctx); if (res) goto cleanup; - res = mont_number(&wp->d, 1, ctx); + res = mont_new_number(&wp->d, 1, ctx); if (res) goto cleanup; - res = mont_number(&wp->e, 1, ctx); + res = mont_new_number(&wp->e, 1, ctx); if (res) goto cleanup; - res = mont_number(&wp->f, 1, ctx); + res = mont_new_number(&wp->f, 1, ctx); if (res) goto cleanup; - res = mont_number(&wp->scratch, SCRATCHPAD_NR, ctx); + res = mont_new_number(&wp->scratch, SCRATCHPAD_NR, ctx); if (res) goto cleanup; return wp; @@ -396,7 +396,7 @@ EXPORT_SYM int ed448_new_point(PointEd448 **pecp, if (res) goto cleanup; res = mont_from_bytes(&ecp->y, y, len, ctx); if (res) goto cleanup; - res = mont_number(&ecp->z, 1, ctx); + res = mont_new_number(&ecp->z, 1, ctx); if (res) goto cleanup; mont_set(ecp->z, 1, ctx); @@ -465,9 +465,9 @@ EXPORT_SYM int ed448_get_xy(uint8_t *x, uint8_t *y, size_t len, const PointEd448 if (len < ctx->modulus_len) return ERR_NOT_ENOUGH_DATA; - res = mont_number(&xw, 1, ctx); + res = mont_new_number(&xw, 1, ctx); if (res) goto cleanup; - res = mont_number(&yw, 1, ctx); + res = mont_new_number(&yw, 1, ctx); if (res) goto cleanup; ed448_projective_to_affine(xw, yw, ecp->x, ecp->y, ecp->z, ecp->wp, ctx); @@ -558,15 +558,15 @@ EXPORT_SYM int ed448_clone(PointEd448 **pecp2, const PointEd448 *ecp) ecp2->wp = new_workplace(ctx); if (NULL == ecp2->wp) goto cleanup; - res = mont_number(&ecp2->x, 1, ctx); + res = mont_new_number(&ecp2->x, 1, ctx); if (res) goto cleanup; mont_copy(ecp2->x, ecp->x, ctx); - res = mont_number(&ecp2->y, 1, ctx); + res = mont_new_number(&ecp2->y, 1, ctx); if (res) goto cleanup; mont_copy(ecp2->y, ecp->y, ctx); - res = mont_number(&ecp2->z, 1, ctx); + res = mont_new_number(&ecp2->z, 1, ctx); if (res) goto cleanup; mont_copy(ecp2->z, ecp->z, ctx); diff --git a/src/modexp.c b/src/modexp.c index f4df96ac..a85d3d81 100644 --- a/src/modexp.c +++ b/src/modexp.c @@ -95,20 +95,20 @@ EXPORT_SYM int monty_pow( return res; for (i=0; i<(1 << WINDOW_SIZE); i++) { - res = mont_number(powers+i, 1, ctx); + res = mont_new_number(powers+i, 1, ctx); if (res) goto cleanup; } - res = mont_number(&power_idx, 1, ctx); + res = mont_new_number(&power_idx, 1, ctx); if (res) goto cleanup; res = mont_from_bytes(&mont_base, base, len, ctx); if (res) goto cleanup; - res = mont_number(&x, 1, ctx); + res = mont_new_number(&x, 1, ctx); if (res) goto cleanup; - res = mont_number(&scratchpad, SCRATCHPAD_NR, ctx); + res = mont_new_number(&scratchpad, SCRATCHPAD_NR, ctx); if (res) goto cleanup; buf_out = (uint8_t*)calloc(1, mont_bytes(ctx)); @@ -222,10 +222,10 @@ EXPORT_SYM int monty_multiply( res = mont_from_bytes(&mont_term2, term2, len, ctx); if (res) goto cleanup; - res = mont_number(&mont_output, 1, ctx); + res = mont_new_number(&mont_output, 1, ctx); if (res) goto cleanup; - res = mont_number(&scratchpad, SCRATCHPAD_NR, ctx); + res = mont_new_number(&scratchpad, SCRATCHPAD_NR, ctx); if (res) goto cleanup; /* Multiply, then transform result back into big-endian, byte form **/ diff --git a/src/mont.c b/src/mont.c index 9cac791d..ab977aa3 100644 --- a/src/mont.c +++ b/src/mont.c @@ -147,7 +147,7 @@ STATIC void rsquare(uint64_t *r2_mod_n, uint64_t *n, size_t nw) * @param b The second term (already in Montgomery form, b*R mod N) * @param n The modulus (in normal form), such that R>N * @param m0 Least-significant word of the opposite of the inverse of n modulo R, that is, -n[0]⁻¹ mod R - * @param t Temporary, internal result; it must have been created with mont_number(&p,SCRATCHPAD_NR,ctx). + * @param t Temporary, internal result; it must have been created with mont_new_number(&p,SCRATCHPAD_NR,ctx). * @param nw Number of words making up the 3 integers: out, a, and b. * It also defines R as 2^(64*nw). * @@ -747,7 +747,7 @@ size_t mont_bytes(const MontContext *ctx) * @return 0 if successful, the relevant error code otherwise. * */ -int mont_number(uint64_t **out, unsigned count, const MontContext *ctx) +int mont_new_number(uint64_t **out, unsigned count, const MontContext *ctx) { if (NULL == out || NULL == ctx) return ERR_NULL; @@ -765,7 +765,7 @@ int mont_random_number(uint64_t **out, unsigned count, uint64_t seed, const Mont unsigned i; uint64_t *number; - res = mont_number(out, count, ctx); + res = mont_new_number(out, count, ctx); if (res) return res; @@ -902,10 +902,10 @@ int mont_to_bytes(uint8_t *number, size_t len, const uint64_t* mont_number, cons /* * Add two numbers in Montgomery representation. * - * @param out The location where the result will be stored; it must have been created with mont_number(&p,1,ctx). + * @param out The location where the result will be stored; it must have been created with mont_new_number(&p,1,ctx). * @param a The first term. * @param b The second term. - * @param tmp Temporary, internal result; it must have been created with mont_number(&p,SCRATCHPAD_NR,ctx). + * @param tmp Temporary, internal result; it must have been created with mont_new_number(&p,SCRATCHPAD_NR,ctx). * @param ctx The Montgomery context. * @return 0 for success, the relevant error code otherwise. */ @@ -920,10 +920,10 @@ int mont_add(uint64_t* out, const uint64_t* a, const uint64_t* b, uint64_t *tmp, /* * Multiply two numbers in Montgomery representation. * - * @param out The location where the result will be stored at; it must have been created with mont_number(&p,1,ctx) + * @param out The location where the result will be stored at; it must have been created with mont_new_number(&p,1,ctx) * @param a The first term. * @param b The second term. - * @param tmp Temporary, internal result; it must have been created with mont_number(&p,SCRATCHPAD_NR,ctx). + * @param tmp Temporary, internal result; it must have been created with mont_new_number(&p,SCRATCHPAD_NR,ctx). * @param ctx The Montgomery context. * @return 0 for success, the relevant error code otherwise. */ @@ -958,11 +958,11 @@ int mont_mult(uint64_t* out, const uint64_t* a, const uint64_t *b, uint64_t *tmp /* * Subtract integer b from a. * - * @param out The location where the result is stored at; it must have been created with mont_number(&p,1,ctx). + * @param out The location where the result is stored at; it must have been created with mont_new_number(&p,1,ctx). * It can be the same as either a or b. * @param a The number it will be subtracted from. * @param b The number to subtract. - * @param tmp Temporary, internal result; it must have been created with mont_number(&p,2,ctx). + * @param tmp Temporary, internal result; it must have been created with mont_new_number(&p,2,ctx). * @param ctx The Montgomery context. * @return 0 for success, the relevant error code otherwise. */ @@ -980,7 +980,7 @@ int mont_sub(uint64_t *out, const uint64_t *a, const uint64_t *b, uint64_t *tmp, * Condition: the modulus defining the Montgomery context MUST BE a non-secret prime number. * * @param out The location where the result will be stored at; it must have - * been allocated with mont_number(&p, 1, ctx). + * been allocated with mont_new_number(&p, 1, ctx). * @param a The number to compute the modular inverse of, already in Montgomery form. * @param ctx The Montgomery context. * @return 0 for success, the relevant error code otherwise. @@ -1049,7 +1049,7 @@ int mont_inv_prime(uint64_t *out, uint64_t *a, const MontContext *ctx) /* * Assign a value to a number in Montgomery form. * - * @param out The location where the result is stored at; it must have been created with mont_number(&p,1,ctx). + * @param out The location where the result is stored at; it must have been created with mont_new_number(&p,1,ctx). * @param x The value to set. * @param ctx The Montgomery context. * @return 0 for success, the relevant error code otherwise. diff --git a/src/mont.h b/src/mont.h index b35abe46..9c24612f 100644 --- a/src/mont.h +++ b/src/mont.h @@ -27,7 +27,7 @@ int mont_context_init(MontContext **out, const uint8_t *modulus, size_t mod_len) void mont_context_free(MontContext *ctx); size_t mont_bytes(const MontContext *ctx); -int mont_number(uint64_t **out, unsigned count, const struct mont_context *ctx); +int mont_new_number(uint64_t **out, unsigned count, const struct mont_context *ctx); int mont_random_number(uint64_t **out, unsigned count, uint64_t seed, const struct mont_context *ctx); int mont_from_bytes(uint64_t **out, const uint8_t *number, size_t len, const MontContext *ctx); diff --git a/src/test/README.txt b/src/test/README.txt new file mode 100644 index 00000000..774709b2 --- /dev/null +++ b/src/test/README.txt @@ -0,0 +1,12 @@ +In order to compile and run the tests you need cmake. + +In Linux, do:: + + cmake -B build -DSSE=1 + make -C build -j 8 all test + +In Windows, do:: + + cmake -B build -G "NMake Makefiles" + cd build + nmake all test diff --git a/src/test/test_ec_ws.c b/src/test/test_ec_ws.c index 9be20c37..31603c21 100644 --- a/src/test/test_ec_ws.c +++ b/src/test/test_ec_ws.c @@ -57,10 +57,10 @@ static int matches(const uint64_t *x1, const uint64_t *y1, const uint64_t *z1, uint64_t *xa, *ya, *xb, *yb; int result; - mont_number(&xa, 1, ctx); - mont_number(&ya, 1, ctx); - mont_number(&xb, 1, ctx); - mont_number(&yb, 1, ctx); + mont_new_number(&xa, 1, ctx); + mont_new_number(&ya, 1, ctx); + mont_new_number(&xb, 1, ctx); + mont_new_number(&yb, 1, ctx); ec_projective_to_affine(xa, ya, x1, y1, z1, wp, ctx); ec_projective_to_affine(xb, yb, x2, y2, z2, wp, ctx); @@ -248,11 +248,11 @@ void test_ec_mix_add_2(void) mont_from_bytes(&b, (uint8_t*)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32, ctx); /* Projective input point is point-at-infinity */ - mont_number(&x1, 1, ctx); + mont_new_number(&x1, 1, ctx); mont_set(x1, 0, ctx); - mont_number(&y1, 1, ctx); + mont_new_number(&y1, 1, ctx); mont_set(y1, 1, ctx); - mont_number(&z1, 1, ctx); + mont_new_number(&z1, 1, ctx); mont_set(z1, 0, ctx); mont_from_bytes(&x2, (uint8_t*)"\xf2\x49\x10\x4d\x0e\x6f\x8f\x29\xe6\x01\x62\x77\x78\x0c\xda\x84\xdc\x84\xb8\x3b\xc3\xd8\x99\xdf\xb7\x36\xca\x08\x31\xfb\xe8\xcf", 32, ctx); @@ -449,9 +449,9 @@ void test_ec_full_add(void) mont_set(x2, 0, ctx); mont_set(y2, 1, ctx); mont_set(z2, 0, ctx); - mont_number(&x3, 1, ctx); - mont_number(&y3, 1, ctx); - mont_number(&z3, 1, ctx); + mont_new_number(&x3, 1, ctx); + mont_new_number(&y3, 1, ctx); + mont_new_number(&z3, 1, ctx); ec_full_add(x3, y3, z3, x1, y1, z1, x2, y2, z2, b, wp, ctx); @@ -495,9 +495,9 @@ void test_ec_scalar(void) mont_from_bytes(&b, (uint8_t*)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32, ctx); /* 1*G */ - mont_number(&x1, 1, ctx); - mont_number(&y1, 1, ctx); - mont_number(&z1, 1, ctx); + mont_new_number(&x1, 1, ctx); + mont_new_number(&y1, 1, ctx); + mont_new_number(&z1, 1, ctx); mont_from_bytes(&x2, (uint8_t*)"\x2e\xee\x33\x80\xcb\xba\x96\xcb\xb7\x61\x04\xf5\xe4\x6a\x89\x78\xa6\x22\xe7\x07\xcb\x30\x04\x49\x8e\x4c\x3c\xba\x75\xf7\x99\xe0", 32, ctx); mont_from_bytes(&y2, (uint8_t*)"\x1e\xe0\x9c\xe0\xed\x08\xfc\x10\x95\x0f\x30\xe8\xd8\x9c\x2c\xdd\xb6\x0e\x01\x67\x2f\xed\xb4\x13\xf5\x1e\x84\x12\x2d\x79\x33\x95", 32, ctx); mont_from_bytes(&z2, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); @@ -589,13 +589,13 @@ void test_ec_scalar_g_p256(void) mont_from_bytes(&Gx_mont, Gx, sizeof Gx, ctx); mont_from_bytes(&Gy_mont, Gy, sizeof Gy, ctx); - mont_number(&xw, 1, ctx); - mont_number(&yw, 1, ctx); + mont_new_number(&xw, 1, ctx); + mont_new_number(&yw, 1, ctx); /* 1*G */ - mont_number(&x1, 1, ctx); - mont_number(&y1, 1, ctx); - mont_number(&z1, 1, ctx); + mont_new_number(&x1, 1, ctx); + mont_new_number(&y1, 1, ctx); + mont_new_number(&z1, 1, ctx); res = ec_scalar_g_p256(x1, y1, z1, b, (uint8_t*)"\x01", 1, 0x4545, wp1, wp2, prot_g, ctx); assert(res == 0); ec_projective_to_affine(xw, yw, x1, y1, z1, wp1, ctx); diff --git a/src/test/test_ed448.c b/src/test/test_ed448.c index 5d475fa2..fc26f7f3 100644 --- a/src/test/test_ed448.c +++ b/src/test/test_ed448.c @@ -176,8 +176,8 @@ void test_point_add_3(void) assert(res == 0); /** Let's scale Z coords by different factors, so that they are not equal nor 1 **/ - mont_number(&scale1, 1, ec_ctx->mont_ctx); - mont_number(&scale2, 1, ec_ctx->mont_ctx); + mont_new_number(&scale1, 1, ec_ctx->mont_ctx); + mont_new_number(&scale2, 1, ec_ctx->mont_ctx); mont_set(scale1, 0xAABBCCDDEEFF, ec_ctx->mont_ctx); mont_set(scale2, 0xFFEEDDCCBBAA, ec_ctx->mont_ctx); scale(G, scale1, ec_ctx); @@ -276,8 +276,8 @@ void test_cmp_1(void) res = ed448_new_point(&G2, G2x, G2y, 56, ec_ctx); assert(res == 0); - mont_number(&scale1, 1, ec_ctx->mont_ctx); - mont_number(&scale2, 1, ec_ctx->mont_ctx); + mont_new_number(&scale1, 1, ec_ctx->mont_ctx); + mont_new_number(&scale2, 1, ec_ctx->mont_ctx); mont_set(scale1, 0xAABBCCDDEEFF, ec_ctx->mont_ctx); mont_set(scale2, 0xFFEEDDCCBBAA, ec_ctx->mont_ctx); scale(G, scale1, ec_ctx); diff --git a/src/test/test_mont.c b/src/test/test_mont.c index 597505ab..43b32d77 100644 --- a/src/test/test_mont.c +++ b/src/test/test_mont.c @@ -168,7 +168,7 @@ void test_mont_add(void) uint64_t out[2]; mont_context_init(&ctx, modulus, 16); - mont_number(&tmp, 2, ctx); + mont_new_number(&tmp, 2, ctx); res = mont_add(NULL, a, b, tmp, ctx); assert(res == ERR_NULL); @@ -242,7 +242,7 @@ void test_mont_sub(void) uint64_t *tmp; mont_context_init(&ctx, modulus, 16); - mont_number(&tmp, 2, ctx); + mont_new_number(&tmp, 2, ctx); res = mont_sub(NULL, a, b, tmp, ctx); assert(res == ERR_NULL); From eb828aff6ca56bd62e7762fae9d16ac75eedd307 Mon Sep 17 00:00:00 2001 From: Helder Eijs Date: Sat, 24 Aug 2024 16:37:14 +0200 Subject: [PATCH 03/18] Rename mont_random_number() to mont_new_random_number() --- src/ec_ws.c | 2 +- src/mont.c | 2 +- src/mont.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ec_ws.c b/src/ec_ws.c index e0f4e093..b575ef8a 100644 --- a/src/ec_ws.c +++ b/src/ec_ws.c @@ -1358,7 +1358,7 @@ EXPORT_SYM int ec_ws_scalar(EcPoint *ecp, const uint8_t *k, size_t len, uint64_t uint64_t *factor=NULL; /* Create the blinding factor for the base point */ - res = mont_random_number(&factor, 1, seed, ctx); + res = mont_new_random_number(&factor, 1, seed, ctx); if (res) goto cleanup; diff --git a/src/mont.c b/src/mont.c index ab977aa3..a8644e35 100644 --- a/src/mont.c +++ b/src/mont.c @@ -759,7 +759,7 @@ int mont_new_number(uint64_t **out, unsigned count, const MontContext *ctx) return 0; } -int mont_random_number(uint64_t **out, unsigned count, uint64_t seed, const MontContext *ctx) +int mont_new_random_number(uint64_t **out, unsigned count, uint64_t seed, const MontContext *ctx) { int res; unsigned i; diff --git a/src/mont.h b/src/mont.h index 9c24612f..25369d0f 100644 --- a/src/mont.h +++ b/src/mont.h @@ -28,7 +28,7 @@ void mont_context_free(MontContext *ctx); size_t mont_bytes(const MontContext *ctx); int mont_new_number(uint64_t **out, unsigned count, const struct mont_context *ctx); -int mont_random_number(uint64_t **out, unsigned count, uint64_t seed, const struct mont_context *ctx); +int mont_new_random_number(uint64_t **out, unsigned count, uint64_t seed, const struct mont_context *ctx); int mont_from_bytes(uint64_t **out, const uint8_t *number, size_t len, const MontContext *ctx); int mont_to_bytes(uint8_t *number, size_t len, const uint64_t* mont_number, const MontContext *ctx); From 456684168f22ee49e92b07c1ae11763277ba59a0 Mon Sep 17 00:00:00 2001 From: Helder Eijs Date: Sat, 24 Aug 2024 16:40:24 +0200 Subject: [PATCH 04/18] Rename mont_from_bytes() to mont_new_from_bytes() --- src/ec_ws.c | 6 +-- src/ed448.c | 6 +-- src/modexp.c | 6 +-- src/mont.c | 2 +- src/mont.h | 2 +- src/test/test_ec_ws.c | 116 +++++++++++++++++++++--------------------- src/test/test_mont.c | 22 ++++---- 7 files changed, 80 insertions(+), 80 deletions(-) diff --git a/src/ec_ws.c b/src/ec_ws.c index b575ef8a..38dba22a 100644 --- a/src/ec_ws.c +++ b/src/ec_ws.c @@ -907,7 +907,7 @@ EXPORT_SYM int ec_ws_new_context(EcContext **pec_ctx, if (res) goto cleanup; ctx = ec_ctx->mont_ctx; - res = mont_from_bytes(&ec_ctx->b, b, len, ctx); + res = mont_new_from_bytes(&ec_ctx->b, b, len, ctx); if (res) goto cleanup; order_words = ((unsigned)len+7)/8; @@ -1020,9 +1020,9 @@ EXPORT_SYM int ec_ws_new_point(EcPoint **pecp, ecp->ec_ctx = ec_ctx; - res = mont_from_bytes(&ecp->x, x, len, ctx); + res = mont_new_from_bytes(&ecp->x, x, len, ctx); if (res) goto cleanup; - res = mont_from_bytes(&ecp->y, y, len, ctx); + res = mont_new_from_bytes(&ecp->y, y, len, ctx); if (res) goto cleanup; res = mont_new_number(&ecp->z, 1, ctx); if (res) goto cleanup; diff --git a/src/ed448.c b/src/ed448.c index e96ad7d8..fc2b1a3c 100644 --- a/src/ed448.c +++ b/src/ed448.c @@ -330,7 +330,7 @@ EXPORT_SYM int ed448_new_context(EcContext **pec_ctx) if (res) goto cleanup; ctx = ec_ctx->mont_ctx; - res = mont_from_bytes(&ec_ctx->d, d448_be, sizeof(d448_be), ctx); + res = mont_new_from_bytes(&ec_ctx->d, d448_be, sizeof(d448_be), ctx); if (res) goto cleanup; return 0; @@ -392,9 +392,9 @@ EXPORT_SYM int ed448_new_point(PointEd448 **pecp, ecp->ec_ctx = ec_ctx; /** No need to treat PAI (x=0, y=1) in a special way **/ - res = mont_from_bytes(&ecp->x, x, len, ctx); + res = mont_new_from_bytes(&ecp->x, x, len, ctx); if (res) goto cleanup; - res = mont_from_bytes(&ecp->y, y, len, ctx); + res = mont_new_from_bytes(&ecp->y, y, len, ctx); if (res) goto cleanup; res = mont_new_number(&ecp->z, 1, ctx); if (res) goto cleanup; diff --git a/src/modexp.c b/src/modexp.c index a85d3d81..4bcffab8 100644 --- a/src/modexp.c +++ b/src/modexp.c @@ -102,7 +102,7 @@ EXPORT_SYM int monty_pow( res = mont_new_number(&power_idx, 1, ctx); if (res) goto cleanup; - res = mont_from_bytes(&mont_base, base, len, ctx); + res = mont_new_from_bytes(&mont_base, base, len, ctx); if (res) goto cleanup; res = mont_new_number(&x, 1, ctx); @@ -216,10 +216,10 @@ EXPORT_SYM int monty_multiply( if (res) return res; - res = mont_from_bytes(&mont_term1, term1, len, ctx); + res = mont_new_from_bytes(&mont_term1, term1, len, ctx); if (res) goto cleanup; - res = mont_from_bytes(&mont_term2, term2, len, ctx); + res = mont_new_from_bytes(&mont_term2, term2, len, ctx); if (res) goto cleanup; res = mont_new_number(&mont_output, 1, ctx); diff --git a/src/mont.c b/src/mont.c index a8644e35..0e3de9ec 100644 --- a/src/mont.c +++ b/src/mont.c @@ -789,7 +789,7 @@ int mont_new_random_number(uint64_t **out, unsigned count, uint64_t seed, const * smaller than the output of mont_bytes(ctx)). * @return 0 in case of success, the relevant error code otherwise. */ -int mont_from_bytes(uint64_t **out, const uint8_t *number, size_t len, const MontContext *ctx) +int mont_new_from_bytes(uint64_t **out, const uint8_t *number, size_t len, const MontContext *ctx) { uint64_t *encoded = NULL; uint64_t *tmp1 = NULL; diff --git a/src/mont.h b/src/mont.h index 25369d0f..d850a0a9 100644 --- a/src/mont.h +++ b/src/mont.h @@ -29,8 +29,8 @@ size_t mont_bytes(const MontContext *ctx); int mont_new_number(uint64_t **out, unsigned count, const struct mont_context *ctx); int mont_new_random_number(uint64_t **out, unsigned count, uint64_t seed, const struct mont_context *ctx); +int mont_new_from_bytes(uint64_t **out, const uint8_t *number, size_t len, const MontContext *ctx); -int mont_from_bytes(uint64_t **out, const uint8_t *number, size_t len, const MontContext *ctx); int mont_to_bytes(uint8_t *number, size_t len, const uint64_t* mont_number, const MontContext *ctx); int mont_add(uint64_t* out, const uint64_t* a, const uint64_t* b, uint64_t *tmp, const MontContext *ctx); int mont_mult(uint64_t* out, const uint64_t* a, const uint64_t *b, uint64_t *tmp, const MontContext *ctx); diff --git a/src/test/test_ec_ws.c b/src/test/test_ec_ws.c index 31603c21..a2791d50 100644 --- a/src/test/test_ec_ws.c +++ b/src/test/test_ec_ws.c @@ -92,9 +92,9 @@ void test_ec_projective_to_affine(void) wp = new_workplace(ctx); /** Arbitrary point on the curve with Z=10 **/ - mont_from_bytes(&x, (uint8_t*)"\xc6\x4c\x90\xad\x8d\x5c\x1d\x96\xd6\x4b\x63\x46\x4a\x8b\x57\x91\xbf\x48\xa6\xb4\xb9\xbc\xd6\xad\x79\xc6\x3a\x13\xbf\xb7\x78\x5b", 32, ctx); - mont_from_bytes(&y, (uint8_t*)"\xe4\x98\x64\xd0\x22\x85\x75\x8a\x11\x79\x68\x2e\x06\x92\x3d\xf7\x62\xa8\x85\xea\xda\xe6\xd9\xb0\x5a\x4f\x0c\x43\x1d\x51\x77\xe4", 32, ctx); - mont_from_bytes(&z, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); + mont_new_from_bytes(&x, (uint8_t*)"\xc6\x4c\x90\xad\x8d\x5c\x1d\x96\xd6\x4b\x63\x46\x4a\x8b\x57\x91\xbf\x48\xa6\xb4\xb9\xbc\xd6\xad\x79\xc6\x3a\x13\xbf\xb7\x78\x5b", 32, ctx); + mont_new_from_bytes(&y, (uint8_t*)"\xe4\x98\x64\xd0\x22\x85\x75\x8a\x11\x79\x68\x2e\x06\x92\x3d\xf7\x62\xa8\x85\xea\xda\xe6\xd9\xb0\x5a\x4f\x0c\x43\x1d\x51\x77\xe4", 32, ctx); + mont_new_from_bytes(&z, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); ec_projective_to_affine(x, y, x, y, z, wp, ctx); mont_to_bytes(buffer, 32, x, ctx); assert(0 == memcmp(buffer, (uint8_t*)"\xfa\x3a\xdb\x43\xa7\xbc\x69\x5c\xc8\xa1\x23\x87\x07\x74\x55\x8e\x93\x20\xdd\x79\x5f\x5f\xaf\x11\x58\xfa\x39\x01\xf9\x92\x58\xd5", 32)); @@ -132,12 +132,12 @@ void test_ec_full_double(void) mont_context_init(&ctx, modulus, sizeof(modulus)); wp = new_workplace(ctx); - mont_from_bytes(&b, (uint8_t*)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32, ctx); + mont_new_from_bytes(&b, (uint8_t*)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32, ctx); /** Arbitrary point on the curve with Z=10 **/ - mont_from_bytes(&x, (uint8_t*)"\xc6\x4c\x90\xad\x8d\x5c\x1d\x96\xd6\x4b\x63\x46\x4a\x8b\x57\x91\xbf\x48\xa6\xb4\xb9\xbc\xd6\xad\x79\xc6\x3a\x13\xbf\xb7\x78\x5b", 32, ctx); - mont_from_bytes(&y, (uint8_t*)"\xe4\x98\x64\xd0\x22\x85\x75\x8a\x11\x79\x68\x2e\x06\x92\x3d\xf7\x62\xa8\x85\xea\xda\xe6\xd9\xb0\x5a\x4f\x0c\x43\x1d\x51\x77\xe4", 32, ctx); - mont_from_bytes(&z, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); + mont_new_from_bytes(&x, (uint8_t*)"\xc6\x4c\x90\xad\x8d\x5c\x1d\x96\xd6\x4b\x63\x46\x4a\x8b\x57\x91\xbf\x48\xa6\xb4\xb9\xbc\xd6\xad\x79\xc6\x3a\x13\xbf\xb7\x78\x5b", 32, ctx); + mont_new_from_bytes(&y, (uint8_t*)"\xe4\x98\x64\xd0\x22\x85\x75\x8a\x11\x79\x68\x2e\x06\x92\x3d\xf7\x62\xa8\x85\xea\xda\xe6\xd9\xb0\x5a\x4f\x0c\x43\x1d\x51\x77\xe4", 32, ctx); + mont_new_from_bytes(&z, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); ec_full_double(x, y, z, x, y, z, b, wp, ctx); mont_to_bytes(buffer, 32, x, ctx); @@ -160,9 +160,9 @@ void test_ec_full_double(void) */ free(x); free(z); - mont_from_bytes(&x, (uint8_t*)"\x2b\xad\x3f\x80\xd3\x9a\xc9\x7b\x28\xfd\x2c\xeb\x93\x2d\x82\xe1\xb7\x5b\x3b\xd1\xbc\xe2\xbe\x11\x27\xe5\xa8\x7f\x1c\xc0\xd4\x77", 32, ctx); + mont_new_from_bytes(&x, (uint8_t*)"\x2b\xad\x3f\x80\xd3\x9a\xc9\x7b\x28\xfd\x2c\xeb\x93\x2d\x82\xe1\xb7\x5b\x3b\xd1\xbc\xe2\xbe\x11\x27\xe5\xa8\x7f\x1c\xc0\xd4\x77", 32, ctx); mont_set(y, 0, ctx); - mont_from_bytes(&z, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); + mont_new_from_bytes(&z, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); ec_full_double(x, y, z, x, y, z, b, wp, ctx); assert(0 == memcmp(z, zero, 32)); @@ -190,14 +190,14 @@ void test_ec_mix_add_1(void) mont_context_init(&ctx, modulus, sizeof(modulus)); wp = new_workplace(ctx); - mont_from_bytes(&b, (uint8_t*)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32, ctx); + mont_new_from_bytes(&b, (uint8_t*)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32, ctx); /* Arbitrary points */ - mont_from_bytes(&x1, (uint8_t*)"\xc6\x4c\x90\xad\x8d\x5c\x1d\x96\xd6\x4b\x63\x46\x4a\x8b\x57\x91\xbf\x48\xa6\xb4\xb9\xbc\xd6\xad\x79\xc6\x3a\x13\xbf\xb7\x78\x5b", 32, ctx); - mont_from_bytes(&y1, (uint8_t*)"\xe4\x98\x64\xd0\x22\x85\x75\x8a\x11\x79\x68\x2e\x06\x92\x3d\xf7\x62\xa8\x85\xea\xda\xe6\xd9\xb0\x5a\x4f\x0c\x43\x1d\x51\x77\xe4", 32, ctx); - mont_from_bytes(&z1, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); - mont_from_bytes(&x2, (uint8_t*)"\xf2\x49\x10\x4d\x0e\x6f\x8f\x29\xe6\x01\x62\x77\x78\x0c\xda\x84\xdc\x84\xb8\x3b\xc3\xd8\x99\xdf\xb7\x36\xca\x08\x31\xfb\xe8\xcf", 32, ctx); - mont_from_bytes(&y2, (uint8_t*)"\xb5\x7e\x12\xfc\xdb\x03\x1f\x59\xca\xb8\x1b\x1c\x6b\x1e\x1c\x07\xe4\x51\x2e\x52\xce\x83\x2f\x1a\x0c\xed\xef\xff\x8b\x43\x40\xe9", 32, ctx); + mont_new_from_bytes(&x1, (uint8_t*)"\xc6\x4c\x90\xad\x8d\x5c\x1d\x96\xd6\x4b\x63\x46\x4a\x8b\x57\x91\xbf\x48\xa6\xb4\xb9\xbc\xd6\xad\x79\xc6\x3a\x13\xbf\xb7\x78\x5b", 32, ctx); + mont_new_from_bytes(&y1, (uint8_t*)"\xe4\x98\x64\xd0\x22\x85\x75\x8a\x11\x79\x68\x2e\x06\x92\x3d\xf7\x62\xa8\x85\xea\xda\xe6\xd9\xb0\x5a\x4f\x0c\x43\x1d\x51\x77\xe4", 32, ctx); + mont_new_from_bytes(&z1, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); + mont_new_from_bytes(&x2, (uint8_t*)"\xf2\x49\x10\x4d\x0e\x6f\x8f\x29\xe6\x01\x62\x77\x78\x0c\xda\x84\xdc\x84\xb8\x3b\xc3\xd8\x99\xdf\xb7\x36\xca\x08\x31\xfb\xe8\xcf", 32, ctx); + mont_new_from_bytes(&y2, (uint8_t*)"\xb5\x7e\x12\xfc\xdb\x03\x1f\x59\xca\xb8\x1b\x1c\x6b\x1e\x1c\x07\xe4\x51\x2e\x52\xce\x83\x2f\x1a\x0c\xed\xef\xff\x8b\x43\x40\xe9", 32, ctx); ec_mix_add(x1, y1, z1, x1, y1, z1, x2, y2, b, wp, ctx); mont_to_bytes(buffer, 32, x1, ctx); @@ -245,7 +245,7 @@ void test_ec_mix_add_2(void) mont_context_init(&ctx, modulus, sizeof(modulus)); wp = new_workplace(ctx); - mont_from_bytes(&b, (uint8_t*)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32, ctx); + mont_new_from_bytes(&b, (uint8_t*)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32, ctx); /* Projective input point is point-at-infinity */ mont_new_number(&x1, 1, ctx); @@ -255,8 +255,8 @@ void test_ec_mix_add_2(void) mont_new_number(&z1, 1, ctx); mont_set(z1, 0, ctx); - mont_from_bytes(&x2, (uint8_t*)"\xf2\x49\x10\x4d\x0e\x6f\x8f\x29\xe6\x01\x62\x77\x78\x0c\xda\x84\xdc\x84\xb8\x3b\xc3\xd8\x99\xdf\xb7\x36\xca\x08\x31\xfb\xe8\xcf", 32, ctx); - mont_from_bytes(&y2, (uint8_t*)"\xb5\x7e\x12\xfc\xdb\x03\x1f\x59\xca\xb8\x1b\x1c\x6b\x1e\x1c\x07\xe4\x51\x2e\x52\xce\x83\x2f\x1a\x0c\xed\xef\xff\x8b\x43\x40\xe9", 32, ctx); + mont_new_from_bytes(&x2, (uint8_t*)"\xf2\x49\x10\x4d\x0e\x6f\x8f\x29\xe6\x01\x62\x77\x78\x0c\xda\x84\xdc\x84\xb8\x3b\xc3\xd8\x99\xdf\xb7\x36\xca\x08\x31\xfb\xe8\xcf", 32, ctx); + mont_new_from_bytes(&y2, (uint8_t*)"\xb5\x7e\x12\xfc\xdb\x03\x1f\x59\xca\xb8\x1b\x1c\x6b\x1e\x1c\x07\xe4\x51\x2e\x52\xce\x83\x2f\x1a\x0c\xed\xef\xff\x8b\x43\x40\xe9", 32, ctx); ec_mix_add(x1, y1, z1, x1, y1, z1, x2, y2, b, wp, ctx); @@ -292,15 +292,15 @@ void test_ec_mix_add_3(void) mont_context_init(&ctx, modulus, sizeof(modulus)); wp = new_workplace(ctx); - mont_from_bytes(&b, (uint8_t*)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32, ctx); + mont_new_from_bytes(&b, (uint8_t*)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32, ctx); /* Affine and projective are actually the same point (doubling) */ - mont_from_bytes(&x1, (uint8_t*)"\xc6\x4c\x90\xad\x8d\x5c\x1d\x96\xd6\x4b\x63\x46\x4a\x8b\x57\x91\xbf\x48\xa6\xb4\xb9\xbc\xd6\xad\x79\xc6\x3a\x13\xbf\xb7\x78\x5b", 32, ctx); - mont_from_bytes(&y1, (uint8_t*)"\xe4\x98\x64\xd0\x22\x85\x75\x8a\x11\x79\x68\x2e\x06\x92\x3d\xf7\x62\xa8\x85\xea\xda\xe6\xd9\xb0\x5a\x4f\x0c\x43\x1d\x51\x77\xe4", 32, ctx); - mont_from_bytes(&z1, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); + mont_new_from_bytes(&x1, (uint8_t*)"\xc6\x4c\x90\xad\x8d\x5c\x1d\x96\xd6\x4b\x63\x46\x4a\x8b\x57\x91\xbf\x48\xa6\xb4\xb9\xbc\xd6\xad\x79\xc6\x3a\x13\xbf\xb7\x78\x5b", 32, ctx); + mont_new_from_bytes(&y1, (uint8_t*)"\xe4\x98\x64\xd0\x22\x85\x75\x8a\x11\x79\x68\x2e\x06\x92\x3d\xf7\x62\xa8\x85\xea\xda\xe6\xd9\xb0\x5a\x4f\x0c\x43\x1d\x51\x77\xe4", 32, ctx); + mont_new_from_bytes(&z1, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); - mont_from_bytes(&x2, (uint8_t*)"\xfa\x3a\xdb\x43\xa7\xbc\x69\x5c\xc8\xa1\x23\x87\x07\x74\x55\x8e\x93\x20\xdd\x79\x5f\x5f\xaf\x11\x58\xfa\x39\x01\xf9\x92\x58\xd5", 32, ctx); - mont_from_bytes(&y2, (uint8_t*)"\xe3\xa8\xd6\xe0\xd0\x40\x8b\xc1\xce\x8c\x24\x04\x9a\x41\xd2\xff\x23\x77\x40\x98\x49\x17\x15\xc4\xd5\xd4\xb4\x6d\x1c\x88\x25\x96", 32, ctx); + mont_new_from_bytes(&x2, (uint8_t*)"\xfa\x3a\xdb\x43\xa7\xbc\x69\x5c\xc8\xa1\x23\x87\x07\x74\x55\x8e\x93\x20\xdd\x79\x5f\x5f\xaf\x11\x58\xfa\x39\x01\xf9\x92\x58\xd5", 32, ctx); + mont_new_from_bytes(&y2, (uint8_t*)"\xe3\xa8\xd6\xe0\xd0\x40\x8b\xc1\xce\x8c\x24\x04\x9a\x41\xd2\xff\x23\x77\x40\x98\x49\x17\x15\xc4\xd5\xd4\xb4\x6d\x1c\x88\x25\x96", 32, ctx); ec_mix_add(x1, y1, z1, x1, y1, z1, x2, y2, b, wp, ctx); @@ -337,15 +337,15 @@ void test_ec_mix_add_4(void) mont_context_init(&ctx, modulus, sizeof(modulus)); wp = new_workplace(ctx); - mont_from_bytes(&b, (uint8_t*)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32, ctx); + mont_new_from_bytes(&b, (uint8_t*)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32, ctx); /* Affine and projective are actually the same point (doubling) */ - mont_from_bytes(&x1, (uint8_t*)"\xc6\x4c\x90\xad\x8d\x5c\x1d\x96\xd6\x4b\x63\x46\x4a\x8b\x57\x91\xbf\x48\xa6\xb4\xb9\xbc\xd6\xad\x79\xc6\x3a\x13\xbf\xb7\x78\x5b", 32, ctx); - mont_from_bytes(&y1, (uint8_t*)"\xe4\x98\x64\xd0\x22\x85\x75\x8a\x11\x79\x68\x2e\x06\x92\x3d\xf7\x62\xa8\x85\xea\xda\xe6\xd9\xb0\x5a\x4f\x0c\x43\x1d\x51\x77\xe4", 32, ctx); - mont_from_bytes(&z1, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); + mont_new_from_bytes(&x1, (uint8_t*)"\xc6\x4c\x90\xad\x8d\x5c\x1d\x96\xd6\x4b\x63\x46\x4a\x8b\x57\x91\xbf\x48\xa6\xb4\xb9\xbc\xd6\xad\x79\xc6\x3a\x13\xbf\xb7\x78\x5b", 32, ctx); + mont_new_from_bytes(&y1, (uint8_t*)"\xe4\x98\x64\xd0\x22\x85\x75\x8a\x11\x79\x68\x2e\x06\x92\x3d\xf7\x62\xa8\x85\xea\xda\xe6\xd9\xb0\x5a\x4f\x0c\x43\x1d\x51\x77\xe4", 32, ctx); + mont_new_from_bytes(&z1, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); - mont_from_bytes(&x2, (uint8_t*)"\xfa\x3a\xdb\x43\xa7\xbc\x69\x5c\xc8\xa1\x23\x87\x07\x74\x55\x8e\x93\x20\xdd\x79\x5f\x5f\xaf\x11\x58\xfa\x39\x01\xf9\x92\x58\xd5", 32, ctx); - mont_from_bytes(&y2, (uint8_t*)"\xe3\xa8\xd6\xe0\xd0\x40\x8b\xc1\xce\x8c\x24\x04\x9a\x41\xd2\xff\x23\x77\x40\x98\x49\x17\x15\xc4\xd5\xd4\xb4\x6d\x1c\x88\x25\x96", 32, ctx); + mont_new_from_bytes(&x2, (uint8_t*)"\xfa\x3a\xdb\x43\xa7\xbc\x69\x5c\xc8\xa1\x23\x87\x07\x74\x55\x8e\x93\x20\xdd\x79\x5f\x5f\xaf\x11\x58\xfa\x39\x01\xf9\x92\x58\xd5", 32, ctx); + mont_new_from_bytes(&y2, (uint8_t*)"\xe3\xa8\xd6\xe0\xd0\x40\x8b\xc1\xce\x8c\x24\x04\x9a\x41\xd2\xff\x23\x77\x40\x98\x49\x17\x15\xc4\xd5\xd4\xb4\x6d\x1c\x88\x25\x96", 32, ctx); ec_mix_add(x1, y1, z1, x1, y1, z1, x2, y2, b, wp, ctx); @@ -383,15 +383,15 @@ void test_ec_full_add(void) mont_context_init(&ctx, modulus, sizeof(modulus)); wp = new_workplace(ctx); - mont_from_bytes(&b, (uint8_t*)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32, ctx); + mont_new_from_bytes(&b, (uint8_t*)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32, ctx); /* Arbitrary points */ - mont_from_bytes(&x1, (uint8_t*)"\xc6\x4c\x90\xad\x8d\x5c\x1d\x96\xd6\x4b\x63\x46\x4a\x8b\x57\x91\xbf\x48\xa6\xb4\xb9\xbc\xd6\xad\x79\xc6\x3a\x13\xbf\xb7\x78\x5b", 32, ctx); - mont_from_bytes(&y1, (uint8_t*)"\xe4\x98\x64\xd0\x22\x85\x75\x8a\x11\x79\x68\x2e\x06\x92\x3d\xf7\x62\xa8\x85\xea\xda\xe6\xd9\xb0\x5a\x4f\x0c\x43\x1d\x51\x77\xe4", 32, ctx); - mont_from_bytes(&z1, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); - mont_from_bytes(&x2, (uint8_t*)"\x15\xa0\x46\x37\xa6\x49\xfc\x67\x7a\xb4\xd0\x33\x25\x56\x7d\x14\xb9\xe8\x3a\xbf\x1a\xd1\xe4\x4e\xfa\x1c\x41\xc8\x2f\xb6\x76\x7e", 32, ctx); - mont_from_bytes(&y2, (uint8_t*)"\x4b\x3f\xda\x5a\xa0\xaa\xd1\x9f\x4c\xb6\x60\xa8\x24\x50\xf8\xa3\x7a\x8b\x43\x9e\xf0\x93\x35\x53\xe6\x0f\x54\xa6\xae\xd6\x4a\x83", 32, ctx); - mont_from_bytes(&z2, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); + mont_new_from_bytes(&x1, (uint8_t*)"\xc6\x4c\x90\xad\x8d\x5c\x1d\x96\xd6\x4b\x63\x46\x4a\x8b\x57\x91\xbf\x48\xa6\xb4\xb9\xbc\xd6\xad\x79\xc6\x3a\x13\xbf\xb7\x78\x5b", 32, ctx); + mont_new_from_bytes(&y1, (uint8_t*)"\xe4\x98\x64\xd0\x22\x85\x75\x8a\x11\x79\x68\x2e\x06\x92\x3d\xf7\x62\xa8\x85\xea\xda\xe6\xd9\xb0\x5a\x4f\x0c\x43\x1d\x51\x77\xe4", 32, ctx); + mont_new_from_bytes(&z1, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); + mont_new_from_bytes(&x2, (uint8_t*)"\x15\xa0\x46\x37\xa6\x49\xfc\x67\x7a\xb4\xd0\x33\x25\x56\x7d\x14\xb9\xe8\x3a\xbf\x1a\xd1\xe4\x4e\xfa\x1c\x41\xc8\x2f\xb6\x76\x7e", 32, ctx); + mont_new_from_bytes(&y2, (uint8_t*)"\x4b\x3f\xda\x5a\xa0\xaa\xd1\x9f\x4c\xb6\x60\xa8\x24\x50\xf8\xa3\x7a\x8b\x43\x9e\xf0\x93\x35\x53\xe6\x0f\x54\xa6\xae\xd6\x4a\x83", 32, ctx); + mont_new_from_bytes(&z2, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); ec_full_add(x1, y1, z1, x1, y1, z1, x2, y2, z2, b, wp, ctx); mont_to_bytes(buffer, 32, x1, ctx); @@ -403,12 +403,12 @@ void test_ec_full_add(void) /* Same point (doubling) */ free(x1); free(y1); free(z1); free(x2); free(y2); free(z2); - mont_from_bytes(&x1, (uint8_t*)"\xc6\x4c\x90\xad\x8d\x5c\x1d\x96\xd6\x4b\x63\x46\x4a\x8b\x57\x91\xbf\x48\xa6\xb4\xb9\xbc\xd6\xad\x79\xc6\x3a\x13\xbf\xb7\x78\x5b", 32, ctx); - mont_from_bytes(&y1, (uint8_t*)"\xe4\x98\x64\xd0\x22\x85\x75\x8a\x11\x79\x68\x2e\x06\x92\x3d\xf7\x62\xa8\x85\xea\xda\xe6\xd9\xb0\x5a\x4f\x0c\x43\x1d\x51\x77\xe4", 32, ctx); - mont_from_bytes(&z1, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); - mont_from_bytes(&x2, (uint8_t*)"\xfa\x3a\xdb\x43\xa7\xbc\x69\x5c\xc8\xa1\x23\x87\x07\x74\x55\x8e\x93\x20\xdd\x79\x5f\x5f\xaf\x11\x58\xfa\x39\x01\xf9\x92\x58\xd5", 32, ctx); - mont_from_bytes(&y2, (uint8_t*)"\xe3\xa8\xd6\xe0\xd0\x40\x8b\xc1\xce\x8c\x24\x04\x9a\x41\xd2\xff\x23\x77\x40\x98\x49\x17\x15\xc4\xd5\xd4\xb4\x6d\x1c\x88\x25\x96", 32, ctx); - mont_from_bytes(&z2, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 32, ctx); + mont_new_from_bytes(&x1, (uint8_t*)"\xc6\x4c\x90\xad\x8d\x5c\x1d\x96\xd6\x4b\x63\x46\x4a\x8b\x57\x91\xbf\x48\xa6\xb4\xb9\xbc\xd6\xad\x79\xc6\x3a\x13\xbf\xb7\x78\x5b", 32, ctx); + mont_new_from_bytes(&y1, (uint8_t*)"\xe4\x98\x64\xd0\x22\x85\x75\x8a\x11\x79\x68\x2e\x06\x92\x3d\xf7\x62\xa8\x85\xea\xda\xe6\xd9\xb0\x5a\x4f\x0c\x43\x1d\x51\x77\xe4", 32, ctx); + mont_new_from_bytes(&z1, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); + mont_new_from_bytes(&x2, (uint8_t*)"\xfa\x3a\xdb\x43\xa7\xbc\x69\x5c\xc8\xa1\x23\x87\x07\x74\x55\x8e\x93\x20\xdd\x79\x5f\x5f\xaf\x11\x58\xfa\x39\x01\xf9\x92\x58\xd5", 32, ctx); + mont_new_from_bytes(&y2, (uint8_t*)"\xe3\xa8\xd6\xe0\xd0\x40\x8b\xc1\xce\x8c\x24\x04\x9a\x41\xd2\xff\x23\x77\x40\x98\x49\x17\x15\xc4\xd5\xd4\xb4\x6d\x1c\x88\x25\x96", 32, ctx); + mont_new_from_bytes(&z2, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 32, ctx); ec_full_add(x1, y1, z1, x1, y1, z1, x2, y2, z2, b, wp, ctx); @@ -427,9 +427,9 @@ void test_ec_full_add(void) /* Point at infinity (first term) */ free(x1); free(y1); free(z1); - mont_from_bytes(&x1, (uint8_t*)"\xf3\x91\x4a\x3a\xf2\x1b\x11\x44\x58\x3e\xf2\xf8\x54\x01\x4b\x72\xfa\x94\x05\x8d\xf9\x7c\x32\x4f\x1a\xef\x49\x37\x3c\xe8\x5b\xef", 32, ctx); - mont_from_bytes(&y1, (uint8_t*)"\x23\xaa\x65\x85\x4c\xc5\xbc\x53\x0d\x4f\xe7\x3e\xd9\x58\x95\x67\xb2\xea\x79\x1a\x7c\x9b\xe5\xf6\x78\x8c\xd5\xbe\xd8\x55\x0d\xe7", 32, ctx); - mont_from_bytes(&z1, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); + mont_new_from_bytes(&x1, (uint8_t*)"\xf3\x91\x4a\x3a\xf2\x1b\x11\x44\x58\x3e\xf2\xf8\x54\x01\x4b\x72\xfa\x94\x05\x8d\xf9\x7c\x32\x4f\x1a\xef\x49\x37\x3c\xe8\x5b\xef", 32, ctx); + mont_new_from_bytes(&y1, (uint8_t*)"\x23\xaa\x65\x85\x4c\xc5\xbc\x53\x0d\x4f\xe7\x3e\xd9\x58\x95\x67\xb2\xea\x79\x1a\x7c\x9b\xe5\xf6\x78\x8c\xd5\xbe\xd8\x55\x0d\xe7", 32, ctx); + mont_new_from_bytes(&z1, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); mont_set(x2, 0, ctx); mont_set(y2, 1, ctx); mont_set(z2, 0, ctx); @@ -443,9 +443,9 @@ void test_ec_full_add(void) assert(mont_is_equal(y1, y2, ctx)); free(x1); free(y1); free(z1); - mont_from_bytes(&x1, (uint8_t*)"\xf3\x91\x4a\x3a\xf2\x1b\x11\x44\x58\x3e\xf2\xf8\x54\x01\x4b\x72\xfa\x94\x05\x8d\xf9\x7c\x32\x4f\x1a\xef\x49\x37\x3c\xe8\x5b\xef", 32, ctx); - mont_from_bytes(&y1, (uint8_t*)"\x23\xaa\x65\x85\x4c\xc5\xbc\x53\x0d\x4f\xe7\x3e\xd9\x58\x95\x67\xb2\xea\x79\x1a\x7c\x9b\xe5\xf6\x78\x8c\xd5\xbe\xd8\x55\x0d\xe7", 32, ctx); - mont_from_bytes(&z1, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); + mont_new_from_bytes(&x1, (uint8_t*)"\xf3\x91\x4a\x3a\xf2\x1b\x11\x44\x58\x3e\xf2\xf8\x54\x01\x4b\x72\xfa\x94\x05\x8d\xf9\x7c\x32\x4f\x1a\xef\x49\x37\x3c\xe8\x5b\xef", 32, ctx); + mont_new_from_bytes(&y1, (uint8_t*)"\x23\xaa\x65\x85\x4c\xc5\xbc\x53\x0d\x4f\xe7\x3e\xd9\x58\x95\x67\xb2\xea\x79\x1a\x7c\x9b\xe5\xf6\x78\x8c\xd5\xbe\xd8\x55\x0d\xe7", 32, ctx); + mont_new_from_bytes(&z1, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); mont_set(x2, 0, ctx); mont_set(y2, 1, ctx); mont_set(z2, 0, ctx); @@ -492,15 +492,15 @@ void test_ec_scalar(void) mont_context_init(&ctx, modulus, sizeof(modulus)); wp1 = new_workplace(ctx); wp2 = new_workplace(ctx); - mont_from_bytes(&b, (uint8_t*)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32, ctx); + mont_new_from_bytes(&b, (uint8_t*)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32, ctx); /* 1*G */ mont_new_number(&x1, 1, ctx); mont_new_number(&y1, 1, ctx); mont_new_number(&z1, 1, ctx); - mont_from_bytes(&x2, (uint8_t*)"\x2e\xee\x33\x80\xcb\xba\x96\xcb\xb7\x61\x04\xf5\xe4\x6a\x89\x78\xa6\x22\xe7\x07\xcb\x30\x04\x49\x8e\x4c\x3c\xba\x75\xf7\x99\xe0", 32, ctx); - mont_from_bytes(&y2, (uint8_t*)"\x1e\xe0\x9c\xe0\xed\x08\xfc\x10\x95\x0f\x30\xe8\xd8\x9c\x2c\xdd\xb6\x0e\x01\x67\x2f\xed\xb4\x13\xf5\x1e\x84\x12\x2d\x79\x33\x95", 32, ctx); - mont_from_bytes(&z2, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); + mont_new_from_bytes(&x2, (uint8_t*)"\x2e\xee\x33\x80\xcb\xba\x96\xcb\xb7\x61\x04\xf5\xe4\x6a\x89\x78\xa6\x22\xe7\x07\xcb\x30\x04\x49\x8e\x4c\x3c\xba\x75\xf7\x99\xe0", 32, ctx); + mont_new_from_bytes(&y2, (uint8_t*)"\x1e\xe0\x9c\xe0\xed\x08\xfc\x10\x95\x0f\x30\xe8\xd8\x9c\x2c\xdd\xb6\x0e\x01\x67\x2f\xed\xb4\x13\xf5\x1e\x84\x12\x2d\x79\x33\x95", 32, ctx); + mont_new_from_bytes(&z2, (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a", 32, ctx); ec_scalar(x1, y1, z1, x2, y2, z2, b, (uint8_t*)"\x01", 1, 0x4545, wp1, wp2, ctx); @@ -533,8 +533,8 @@ void test_ec_scalar(void) /* arb */ free(x2); free(y2); - mont_from_bytes(&x2, (uint8_t*)"\xde\x24\x44\xbe\xbc\x8d\x36\xe6\x82\xed\xd2\x7e\x0f\x27\x15\x08\x61\x75\x19\xb3\x22\x1a\x8f\xa0\xb7\x7c\xab\x39\x89\xda\x97\xc9", 32, ctx); - mont_from_bytes(&y2, (uint8_t*)"\xc0\x93\xae\x7f\xf3\x6e\x53\x80\xfc\x01\xa5\xaa\xd1\xe6\x66\x59\x70\x2d\xe8\x0f\x53\xce\xc5\x76\xb6\x35\x0b\x24\x30\x42\xa2\x56", 32, ctx); + mont_new_from_bytes(&x2, (uint8_t*)"\xde\x24\x44\xbe\xbc\x8d\x36\xe6\x82\xed\xd2\x7e\x0f\x27\x15\x08\x61\x75\x19\xb3\x22\x1a\x8f\xa0\xb7\x7c\xab\x39\x89\xda\x97\xc9", 32, ctx); + mont_new_from_bytes(&y2, (uint8_t*)"\xc0\x93\xae\x7f\xf3\x6e\x53\x80\xfc\x01\xa5\xaa\xd1\xe6\x66\x59\x70\x2d\xe8\x0f\x53\xce\xc5\x76\xb6\x35\x0b\x24\x30\x42\xa2\x56", 32, ctx); mont_set(z2, 1, ctx); ec_scalar(x1, y1, z1, x2, y2, z2, b, (uint8_t*)"\xc5\x1e\x47\x53\xaf\xde\xc1\xe6\xb6\xc6\xa5\xb9\x92\xf4\x3f\x8d\xd0\xc7\xa8\x93\x30\x72\x70\x8b\x65\x22\x46\x8b\x2f\xfb\x06\xfd", 32, 0x4545, wp1, wp2, ctx); ec_projective_to_affine(x1, y1, x1, y1, z1, wp1, ctx); @@ -584,11 +584,11 @@ void test_ec_scalar_g_p256(void) mont_context_init(&ctx, modulus, sizeof(modulus)); wp1 = new_workplace(ctx); wp2 = new_workplace(ctx); - mont_from_bytes(&b, (uint8_t*)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32, ctx); + mont_new_from_bytes(&b, (uint8_t*)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32, ctx); prot_g = ec_scramble_g_p256(ctx, 0x1010); - mont_from_bytes(&Gx_mont, Gx, sizeof Gx, ctx); - mont_from_bytes(&Gy_mont, Gy, sizeof Gy, ctx); + mont_new_from_bytes(&Gx_mont, Gx, sizeof Gx, ctx); + mont_new_from_bytes(&Gy_mont, Gy, sizeof Gy, ctx); mont_new_number(&xw, 1, ctx); mont_new_number(&yw, 1, ctx); diff --git a/src/test/test_mont.c b/src/test/test_mont.c index 43b32d77..fde038f1 100644 --- a/src/test/test_mont.c +++ b/src/test/test_mont.c @@ -83,7 +83,7 @@ void test_mont_context_init(void) mont_context_free(ctx); } -void test_mont_from_bytes(void) +void test_mont_new_from_bytes(void) { int res; MontContext *ctx; @@ -94,19 +94,19 @@ void test_mont_from_bytes(void) res = mont_context_init(&ctx, modulus, 16); assert(res == 0); - res = mont_from_bytes(NULL, number, 2, ctx); + res = mont_new_from_bytes(NULL, number, 2, ctx); assert(res == ERR_NULL); - res = mont_from_bytes(&output, NULL, 2, ctx); + res = mont_new_from_bytes(&output, NULL, 2, ctx); assert(res == ERR_NULL); - res = mont_from_bytes(&output, number, 2, NULL); + res = mont_new_from_bytes(&output, number, 2, NULL); assert(res == ERR_NULL); - res = mont_from_bytes(&output, number, 0, ctx); + res = mont_new_from_bytes(&output, number, 0, ctx); assert(res == ERR_NOT_ENOUGH_DATA); - res = mont_from_bytes(&output, number, 2, ctx); + res = mont_new_from_bytes(&output, number, 2, ctx); assert(res == 0); assert(output != NULL); assert(output[0] == 18446744073709420033UL); @@ -115,7 +115,7 @@ void test_mont_from_bytes(void) number[0] = 0; number[1] = 0; - res = mont_from_bytes(&output, number, 2, ctx); + res = mont_new_from_bytes(&output, number, 2, ctx); assert(res == 0); assert(output != NULL); assert(output[0] == 0); @@ -314,7 +314,7 @@ void test_mont_inv_prime(void) assert(sizeof buf == mont_bytes(ctx)); /* 2^{-1} mod N = 0x8000000000000001 */ - res = mont_from_bytes(&p, (uint8_t*)"\x00\x02", 2, ctx); + res = mont_new_from_bytes(&p, (uint8_t*)"\x00\x02", 2, ctx); assert(res == 0); res = mont_inv_prime(out, p, ctx); assert(res == 0); @@ -324,7 +324,7 @@ void test_mont_inv_prime(void) free(p); /* 3^{-1} mod N = 0x287cbedc41008ca6 */ - res = mont_from_bytes(&p, (uint8_t*)"\x00\x03", 2, ctx); + res = mont_new_from_bytes(&p, (uint8_t*)"\x00\x03", 2, ctx); assert(res == 0); res = mont_inv_prime(out, p, ctx); assert(res == 0); @@ -337,7 +337,7 @@ void test_mont_inv_prime(void) /* --- */ mont_context_init(&ctx, modulus_p521, sizeof modulus_p521); - res = mont_from_bytes(&p, (uint8_t*)"\x01\xE9\xF3\x4F\x60\xAD\x5C\x4B\x98\x8A\xB4\x3A\x0C\x1C\x40\xFB\x5C\xB0\xFD\x1A\x1A\x6F\x4E\x81\xEB\x33\xDE\x7D\x95\x2E\xE2\x62\x0D\x76\x08\x3B\xA2\x28\xCC\x56\xA4\xFE\xD2\xF6\x08\xF3\x17\x1E\x59\x41\xB7\xE1\x6D\x20\x05\xEB\x9F\x55\x6B\x6B\xA1\x36\x0E\xC2\x35\x8C", 66, ctx); + res = mont_new_from_bytes(&p, (uint8_t*)"\x01\xE9\xF3\x4F\x60\xAD\x5C\x4B\x98\x8A\xB4\x3A\x0C\x1C\x40\xFB\x5C\xB0\xFD\x1A\x1A\x6F\x4E\x81\xEB\x33\xDE\x7D\x95\x2E\xE2\x62\x0D\x76\x08\x3B\xA2\x28\xCC\x56\xA4\xFE\xD2\xF6\x08\xF3\x17\x1E\x59\x41\xB7\xE1\x6D\x20\x05\xEB\x9F\x55\x6B\x6B\xA1\x36\x0E\xC2\x35\x8C", 66, ctx); assert(res == 0); res = mont_inv_prime(out_p521, p, ctx); @@ -433,7 +433,7 @@ int main(void) { test_sub(); test_rsquare(); test_mont_context_init(); - test_mont_from_bytes(); + test_mont_new_from_bytes(); test_mont_to_bytes(); test_mont_add(); test_mont_sub(); From 2b2be9733ef14b7c00d10a84cb293581f8cad2ce Mon Sep 17 00:00:00 2001 From: Helder Eijs Date: Sat, 24 Aug 2024 16:57:26 +0200 Subject: [PATCH 05/18] Add mont_new_from_uint64() --- src/mont.c | 10 ++++++++++ src/mont.h | 1 + src/test/test_mont.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/src/mont.c b/src/mont.c index 0e3de9ec..bc35e2da 100644 --- a/src/mont.c +++ b/src/mont.c @@ -759,6 +759,16 @@ int mont_new_number(uint64_t **out, unsigned count, const MontContext *ctx) return 0; } +int mont_new_from_uint64(uint64_t **out, uint64_t x, const MontContext *ctx) +{ + int res; + + res = mont_new_number(out, 1, ctx); + if (res) return res; + + return mont_set(*out, x, ctx); +} + int mont_new_random_number(uint64_t **out, unsigned count, uint64_t seed, const MontContext *ctx) { int res; diff --git a/src/mont.h b/src/mont.h index d850a0a9..75b3befd 100644 --- a/src/mont.h +++ b/src/mont.h @@ -30,6 +30,7 @@ size_t mont_bytes(const MontContext *ctx); int mont_new_number(uint64_t **out, unsigned count, const struct mont_context *ctx); int mont_new_random_number(uint64_t **out, unsigned count, uint64_t seed, const struct mont_context *ctx); int mont_new_from_bytes(uint64_t **out, const uint8_t *number, size_t len, const MontContext *ctx); +int mont_new_from_uint64(uint64_t **out, uint64_t x, const MontContext *ctx); int mont_to_bytes(uint8_t *number, size_t len, const uint64_t* mont_number, const MontContext *ctx); int mont_add(uint64_t* out, const uint64_t* a, const uint64_t* b, uint64_t *tmp, const MontContext *ctx); diff --git a/src/test/test_mont.c b/src/test/test_mont.c index fde038f1..29f0d755 100644 --- a/src/test/test_mont.c +++ b/src/test/test_mont.c @@ -379,6 +379,35 @@ void test_mont_set(void) mont_context_free(ctx); } +void test_mont_new_from_uint64(void) +{ + int res; + MontContext *ctx; + uint8_t modulus[16] = { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; // 0x01000001000000000000000000000001 + uint64_t *out; + + mont_context_init(&ctx, modulus, 16); + + res = mont_new_from_uint64(NULL, 0x1000, ctx); + assert(res == ERR_NULL); + res = mont_new_from_uint64(&out, 0x1000, NULL); + assert(res == ERR_NULL); + + res = mont_new_from_uint64(&out, 0, ctx); + assert(res == 0); + assert(out[0] == 0); + assert(out[1] == 0); + free(out); + + res = mont_new_from_uint64(&out, 0x1000, ctx); + assert(res == 0); + assert(out[0] == 0xfffffffffff00001UL); + assert(out[1] == 0xf00000ffffffffUL); + free(out); + + mont_context_free(ctx); +} + void test_mod_select(void) { int res; @@ -439,6 +468,7 @@ int main(void) { test_mont_sub(); test_mont_inv_prime(); test_mont_set(); + test_mont_new_from_uint64(); test_mod_select(); return 0; } From eec41c5e57257f8b65746136bda82c567364e1ed Mon Sep 17 00:00:00 2001 From: Helder Eijs Date: Sat, 24 Aug 2024 20:34:12 +0200 Subject: [PATCH 06/18] Add curve25519_perf --- src/Makefile | 6 +++--- src/curve25519.c | 10 +++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Makefile b/src/Makefile index 9bd54621..b8917766 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,7 +4,7 @@ CFLAGS=-Werror -Wall -O3 -g -Wno-unused-const-variable -Wconversion -Wsign-conve CFLAGS += -fanalyzer -all:: modexp ec_ws_p256 ec_ws_p384 ec_ws_p521 ed25519_perf ed448_perf +all:: modexp ec_ws_p256 ec_ws_p384 ec_ws_p521 ed25519_perf ed448_perf curve25519_perf ec_ws_p256: ec_ws_p256.c mont.c p256_table.c p384_table.c p521_table.c $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ -DSYS_BITS=64 -DMAIN @@ -36,8 +36,8 @@ p384_table.c: make_ecc_table.py p521_table.c: make_ecc_table.py python make_ecc_table.py p521 4 p521_table -x25519: x25519.c multiply_64.c +curve25519_perf: curve25519.c multiply_64.c $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ -DSYS_BITS=64 -DPROFILE clean:: - rm -f ec_ws_p256 ec_ws_p384 ec_ws_p521 mont.o modexp x25519 ed25519_perf ed448_perf + rm -f ec_ws_p256 ec_ws_p384 ec_ws_p521 mont.o modexp x25519 ed25519_perf ed448_perf curve25519_perf diff --git a/src/curve25519.c b/src/curve25519.c index 2122d734..014d271a 100644 --- a/src/curve25519.c +++ b/src/curve25519.c @@ -241,8 +241,8 @@ int main(void) { uint8_t pubkey[32]; uint8_t secret[32]; - uint8_t out[32]; unsigned i; + int res; Point *Pin; Point Pout; @@ -251,9 +251,13 @@ int main(void) secret[i] = pubkey[i] = (uint8_t)((secret[i-1] << 1) | (secret[i-1] >> 7)); } - curve25519_new_point(&Pin, pubkey, 32); + res = curve25519_new_point(&Pin, pubkey, 32); + if (res) { + printf("Error: %d\n", res); + return res; + } - for (i=0; i<10000; i++) { + for (i=0; i<10000 && res == 0; i++) { curve25519_scalar_internal(&Pout, secret, sizeof secret, Pin); } From dafcda3351bc12d8c6c674d752a11dd27546a951 Mon Sep 17 00:00:00 2001 From: Helder Eijs Date: Sat, 24 Aug 2024 22:29:52 +0200 Subject: [PATCH 07/18] Initial Curve448 implementation --- lib/Crypto/PublicKey/ECC.py | 117 ++++- lib/Crypto/PublicKey/_montgomery.py | 57 ++- lib/Crypto/PublicKey/_point.py | 47 +- lib/Crypto/SelfTest/PublicKey/__init__.py | 2 + .../SelfTest/PublicKey/test_ECC_Curve25519.py | 3 + .../SelfTest/PublicKey/test_ECC_Curve448.py | 236 +++++++++ setup.py | 9 +- src/Makefile | 7 +- src/curve25519.c | 19 +- src/curve25519.h | 3 +- src/curve448.c | 483 ++++++++++++++++++ src/curve448.h | 35 ++ src/mont3.c | 6 + src/test/CMakeLists.txt | 4 + src/test/test_curve25519.c | 2 +- src/test/test_curve448.c | 178 +++++++ 16 files changed, 1155 insertions(+), 53 deletions(-) create mode 100644 lib/Crypto/SelfTest/PublicKey/test_ECC_Curve448.py create mode 100644 src/curve448.c create mode 100644 src/curve448.h create mode 100644 src/mont3.c create mode 100644 src/test/test_curve448.c diff --git a/lib/Crypto/PublicKey/ECC.py b/lib/Crypto/PublicKey/ECC.py index 0684fe98..9db8cd38 100644 --- a/lib/Crypto/PublicKey/ECC.py +++ b/lib/Crypto/PublicKey/ECC.py @@ -73,8 +73,8 @@ class EccKey(object): :vartype d: integer :ivar seed: A seed that representats the private component - in EdDSA curves - (Ed25519, 32 bytes; Ed448, 57 bytes). + in Ed22519 (32 bytes), Curve25519 (32 bytes), + Curve448 (56 bytes), Ed448 (57 bytes). :vartype seed: bytes """ @@ -88,8 +88,8 @@ def __init__(self, **kwargs): Mandatory for a private key one NIST P curves. It must be in the range ``[1..order-1]``. seed : bytes - Mandatory for a private key on the Ed25519 (32 bytes), - Curve25519 (32 bytes) or Ed448 (57 bytes) curve. + Mandatory for a private key on Ed25519 (32 bytes), + Curve25519 (32 bytes), Curve448 (56 bytes) or Ed448 (57 bytes). point : EccPoint or EccXPoint Mandatory for a public key. If provided for a private key, the implementation will NOT check whether it matches ``d``. @@ -159,6 +159,16 @@ def __init__(self, **kwargs): tmp[0] &= 0xF8 tmp[31] = (tmp[31] & 0x7F) | 0x40 self._d = Integer.from_bytes(tmp, byteorder='little') + elif self._curve.id == _CurveID.CURVE448: + if self._d is not None: + raise ValueError("Parameter d can only be used with NIST P curves") + if len(self._seed) != 56: + raise ValueError("Parameter seed must be 56 bytes long for Curve448") + tmp = bytearray(self._seed) + tmp[0] &= 0xFC + tmp[55] |= 0x80 + self._d = Integer.from_bytes(tmp, byteorder='little') + else: if self._seed is not None: raise ValueError("Parameter 'seed' cannot be used with NIST P-curves") @@ -183,7 +193,8 @@ def __repr__(self): extra = ", d=%d" % int(self._d) else: extra = "" - if self._curve.id == _CurveID.CURVE25519: + if self._curve.id in (_CurveID.CURVE25519, + _CurveID.CURVE448): x = self.pointQ.x result = "EccKey(curve='%s', point_x=%d%s)" % (self._curve.canonical, x, extra) else: @@ -290,7 +301,8 @@ def _export_montgomery_public(self): if not self._curve.is_montgomery: raise ValueError("Not a Montgomery key to export") x = self.pointQ.x - result = bytearray(x.to_bytes(32, byteorder='little')) + field_size = self.pointQ.size_in_bytes() + result = bytearray(x.to_bytes(field_size, byteorder='little')) return bytes(result) def _export_subjectPublicKeyInfo(self, compress): @@ -439,9 +451,9 @@ def export_key(self, **kwargs): without any metadata. * For NIST P-curves: equivalent to ``'SEC1'``. - * For Ed25519 and Ed448 curves: ``bytes`` in the format + * For Ed25519 and Ed448: ``bytes`` in the format defined in `RFC8032`_. - * For Curve25519 curves: ``bytes`` in the format + * For Curve25519 and Curve448: ``bytes`` in the format defined in `RFC7748`_. passphrase (bytes or string): @@ -452,7 +464,7 @@ def export_key(self, **kwargs): (*Private keys only*) If ``True`` (default and recommended), the `PKCS#8`_ representation will be used. - It must be ``True`` for Ed25519, Ed488, and Curve25519 curves. + It must be ``True`` for Ed25519, Ed448, Curve25519, and Curve448. If ``False`` and a passphrase is present, the obsolete PEM encryption will be used. @@ -471,8 +483,8 @@ def export_key(self, **kwargs): If ``False`` (default), the method returns the full public key. - This parameter is ignored for EdDSA curves, as compression is - mandatory. + This parameter is ignored for Ed25519/Ed448/Curve25519/Curve448, + as compression is mandatory. prot_params (dict): When a private key is exported with password-protection @@ -596,6 +608,10 @@ def generate(**kwargs): seed = randfunc(32) new_key = EccKey(curve=curve_name, seed=seed) _validate_x25519_public_key(new_key) + elif _curves[curve_name].id == _CurveID.CURVE448: + seed = randfunc(56) + new_key = EccKey(curve=curve_name, seed=seed) + _validate_x448_public_key(new_key) else: d = Integer.random_range(min_inclusive=1, max_exclusive=curve.order, @@ -622,8 +638,8 @@ def construct(**kwargs): It must be an integer in the range ``[1..order-1]``. seed (bytes): - Mandatory for a private key and curves Ed25519, Ed448, and Curve25519. - It must be 32 bytes for Ed25519 or Curve25519, and 57 bytes for Ed448. + Mandatory for a private key and curves Ed25519 (32 bytes), + Curve25519 (32 bytes), Curve448 (56 bytes) and Ed448 (57 bytes). point_x (integer): The X coordinate (affine) of the ECC point. @@ -631,7 +647,8 @@ def construct(**kwargs): point_y (integer): The Y coordinate (affine) of the ECC point. - Mandatory for a public key, except for Curve25519. + Mandatory for a public key, + except for Curve25519 and Curve448. Returns: :class:`EccKey` : a new ECC key object @@ -650,9 +667,15 @@ def construct(**kwargs): if point_x is not None: kwargs["point"] = EccXPoint(point_x, curve_name) new_key = EccKey(**kwargs) - _validate_x25519_public_key(new_key) + elif curve.id == _CurveID.CURVE448: + + if point_x is not None: + kwargs["point"] = EccXPoint(point_x, curve_name) + new_key = EccKey(**kwargs) + _validate_x448_public_key(new_key) + else: if None not in (point_x, point_y): @@ -742,6 +765,10 @@ def _import_subjectPublicKeyInfo(encoded, *kwargs): "1.3.101.112": ("Ed25519", _import_ed25519_public_key), # id-Ed25519 "1.3.101.113": ("Ed448", _import_ed448_public_key) # id-Ed448 } + xdh_oids = { + "1.3.101.110": ("Curve25519", _import_curve25519_public_key), # id-X25519 + "1.3.101.111": ("Curve448", _import_curve448_public_key), # id-X448 + } if oid in nist_p_oids: # See RFC5480 @@ -774,13 +801,15 @@ def _import_subjectPublicKeyInfo(encoded, *kwargs): x, y = import_eddsa_public_key(ec_point) return construct(point_x=x, point_y=y, curve=curve_name) - elif oid == "1.3.101.110": + elif oid in xdh_oids: + curve_name, import_xdh_public_key = xdh_oids[oid] + # Parameters must be absent if params: raise ValueError("Unexpected ECC parameters for ECC OID %s" % oid) - x = _import_curve25519_public_key(ec_point) - return construct(point_x=x, curve="Curve25519") + x = import_xdh_public_key(ec_point) + return construct(point_x=x, curve=curve_name) else: raise UnsupportedEccFeature("Unsupported ECC OID: %s" % oid) @@ -861,6 +890,7 @@ def _import_pkcs8(encoded, passphrase): } xdh_oids = { "1.3.101.110": "Curve25519", # id-X25519 + "1.3.101.111": "Curve448", # id-X448 } if algo_oid in nist_p_oids: @@ -873,8 +903,10 @@ def _import_pkcs8(encoded, passphrase): seed = DerOctetString().decode(private_key).payload return construct(curve=eddsa_oids[algo_oid], seed=seed) elif algo_oid in xdh_oids: + curve_name = xdh_oids[algo_oid] if params is not None: - raise ValueError("X25519 ECC private key must not have parameters") + raise ValueError("%s ECC private key must not have parameters" % + curve_name) curve_oid = None seed = DerOctetString().decode(private_key).payload return construct(curve=xdh_oids[algo_oid], seed=seed) @@ -1085,7 +1117,7 @@ def _import_curve25519_public_key(encoded): """ if len(encoded) != 32: - raise ValueError("Incorrect length. Only Curve25519 public keys are supported.") + raise ValueError("Incorrect Curve25519 key length") x = bytearray(encoded) # RFC 7741, Section 5 @@ -1095,6 +1127,31 @@ def _import_curve25519_public_key(encoded): return point_x +def _import_curve448_public_key(encoded): + """Import a Curve448 ECC public key, + encoded as raw bytes as described in RFC7748_. + + Args: + encoded (bytes): + The Curve448 public key to import. It must be 56 bytes long. + + Returns: + x (integer) + + Raises: + ValueError: when the given key cannot be parsed. + + .. _RFC7748: https://datatracker.ietf.org/doc/html/rfc7748 + """ + + if len(encoded) != 56: + raise ValueError("Incorrect Curve448 key length") + + point_x = Integer.from_bytes(encoded, byteorder='little') + + return point_x + + def _validate_x25519_public_key(new_key): p = _curves['curve25519'].p @@ -1122,6 +1179,11 @@ def _validate_x25519_public_key(new_key): raise ValueError("Invalid Curve25519 public key") +def _validate_x448_public_key(new_key): + #: TODO + pass + + def _import_ed448_public_key(encoded): """Import an Ed448 ECC public key, encoded as raw bytes as described in RFC8032_. @@ -1142,8 +1204,8 @@ def _import_ed448_public_key(encoded): if len(encoded) != 57: raise ValueError("Incorrect length. Only Ed448 public keys are supported.") - p = Integer(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff) # 2**448 - 2**224 - 1 - d = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffff6756 + p = _curves['curve448'].p + d = p - 39081 y = encoded[:56] x_lsb = bord(encoded[56]) >> 7 @@ -1187,7 +1249,8 @@ def import_key(encoded, passphrase=None, curve_name=None): * A binary ``ECPrivateKey`` structure, as defined in `RFC5915`_ (DER). NIST P curves only. - * A `PKCS#8`_ structure (or the more recent Asymmetric Key Package, RFC5958_): binary (DER) or ASCII (PEM). + * A `PKCS#8`_ structure (or the more recent Asymmetric Key + Package, RFC5958_): binary (DER) or ASCII (PEM). * `OpenSSH 6.5`_ and newer versions (ASCII). Private keys can be in the clear or password-protected. @@ -1213,10 +1276,12 @@ def import_key(encoded, passphrase=None, curve_name=None): .. note:: - To import X25519 private and public keys, when encoded as raw ``bytes``, use: + To import X25519/X448 private and public keys, when encoded as raw ``bytes``, use: - * :func:`Crypto.Protocol.DH.import_x25519_public_key`, or - * :func:`Crypto.Protocol.DH.import_x25519_private_key`. + * :func:`Crypto.Protocol.DH.import_x25519_public_key` + * :func:`Crypto.Protocol.DH.import_x25519_private_key` + * :func:`Crypto.Protocol.DH.import_x448_public_key` + * :func:`Crypto.Protocol.DH.import_x448_private_key` Returns: :class:`EccKey` : a new ECC key object diff --git a/lib/Crypto/PublicKey/_montgomery.py b/lib/Crypto/PublicKey/_montgomery.py index 0d90dbf4..daae6aa4 100644 --- a/lib/Crypto/PublicKey/_montgomery.py +++ b/lib/Crypto/PublicKey/_montgomery.py @@ -3,7 +3,8 @@ from ._curve import _Curve from Crypto.Math.Numbers import Integer -from Crypto.Util._raw_api import load_pycryptodome_raw_lib +from Crypto.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, + SmartPointer) def curve25519_curve(): @@ -15,7 +16,8 @@ def curve25519_curve(): int curve25519_new_point(Point **out, const uint8_t x[32], - size_t modsize); + size_t modsize, + const void* context); int curve25519_clone(Point **P, const Point *Q); void curve25519_free_point(Point *p); int curve25519_get_x(uint8_t *xb, size_t modsize, Point *p); @@ -44,3 +46,54 @@ class EcLib(object): None, EcLib) return curve25519 + + +def curve448_curve(): + p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff # 2**448 - 2**224 - 1 + order = 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3 + + _curve448_lib = load_pycryptodome_raw_lib("Crypto.PublicKey._curve448", """ +typedef void Curve448Context; +typedef void Curve448Point; + +int curve448_new_context(Curve448Context **pec_ctx); +void curve448_free_context(Curve448Context *ec_ctx); +int curve448_new_point(Curve448Point **out, + const uint8_t *x, + size_t len, + const Curve448Context *ec_ctx); +void curve448_free_point(Curve448Point *p); +int curve448_clone(Curve448Point **P, const Curve448Point *Q); +int curve448_get_x(uint8_t *xb, size_t modsize, const Curve448Point *p); +int curve448_scalar(Curve448Point *P, const uint8_t *scalar, size_t scalar_len, uint64_t seed); +int curve448_cmp(const Curve448Point *ecp1, const Curve448Point *ecp2); +""") + + class EcLib(object): + new_context = _curve448_lib.curve448_new_context + free_context = _curve448_lib.curve448_free_context + new_point = _curve448_lib.curve448_new_point + clone = _curve448_lib.curve448_clone + free_point = _curve448_lib.curve448_free_point + get_x = _curve448_lib.curve448_get_x + scalar = _curve448_lib.curve448_scalar + cmp = _curve448_lib.curve448_cmp + + curve448_context = VoidPointer() + result = EcLib.new_context(curve448_context.address_of()) + if result: + raise ImportError("Error %d initializing Curve448 context" % result) + + curve448 = _Curve(Integer(p), + None, + Integer(order), + Integer(5), + None, + None, + 448, + "1.3.101.111", # RFC8410 + SmartPointer(curve448_context.get(), EcLib.free_context), + "Curve448", + None, + EcLib) + return curve448 diff --git a/lib/Crypto/PublicKey/_point.py b/lib/Crypto/PublicKey/_point.py index ba407578..ecaecd26 100644 --- a/lib/Crypto/PublicKey/_point.py +++ b/lib/Crypto/PublicKey/_point.py @@ -20,6 +20,7 @@ class CurveID(object): ED25519 = 6 ED448 = 7 CURVE25519 = 8 + CURVE448 = 9 class _Curves(object): @@ -40,8 +41,10 @@ class _Curves(object): ed25519_names = ["ed25519", "Ed25519"] ed448_names = ["ed448", "Ed448"] curve25519_names = ["curve25519", "Curve25519", "X25519"] + curve448_names = ["curve448", "Curve448", "X448"] - all_names = p192_names + p224_names + p256_names + p384_names + p521_names + ed25519_names + ed448_names + curve25519_names + all_names = p192_names + p224_names + p256_names + p384_names + p521_names + \ + ed25519_names + ed448_names + curve25519_names + curve448_names def __contains__(self, item): return item in self.all_names @@ -90,6 +93,11 @@ def load(self, name): curve25519 = _montgomery.curve25519_curve() curve25519.id = CurveID.CURVE25519 self.curves.update(dict.fromkeys(self.curve25519_names, curve25519)) + elif name in self.curve448_names: + from . import _montgomery + curve448 = _montgomery.curve448_curve() + curve448.id = CurveID.CURVE448 + self.curves.update(dict.fromkeys(self.curve448_names, curve448)) else: raise ValueError("Unsupported curve '%s'" % name) return self.curves[name] @@ -99,12 +107,13 @@ def __getitem__(self, name): curve = self.curves.get(name) if curve is None: curve = self.load(name) - if name in self.curve25519_names: + if name in self.curve25519_names or name in self.curve448_names: curve.G = EccXPoint(curve.Gx, name) else: curve.G = EccPoint(curve.Gx, curve.Gy, name) curve.is_edwards = curve.id in (CurveID.ED25519, CurveID.ED448) - curve.is_montgomery = curve.id in (CurveID.CURVE25519,) + curve.is_montgomery = curve.id in (CurveID.CURVE25519, + CurveID.CURVE448) curve.is_weierstrass = not (curve.is_edwards or curve.is_montgomery) return curve @@ -354,22 +363,32 @@ def __init__(self, x, curve): raise ValueError("Unknown curve name %s" % str(curve)) self.curve = self._curve.canonical - if self._curve.id != CurveID.CURVE25519: - raise ValueError("EccXPoint can only be created for Curve25519") - - modulus_bytes = self.size_in_bytes() - - xb = long_to_bytes(x, modulus_bytes) - if len(xb) != modulus_bytes: - raise ValueError("Incorrect coordinate length") + if self._curve.id not in (CurveID.CURVE25519, CurveID.CURVE448): + raise ValueError("EccXPoint can only be created for Curve25519/Curve448") new_point = self._curve.rawlib.new_point free_func = self._curve.rawlib.free_point + self._point = VoidPointer() + try: + context = self._curve.context.get() + except AttributeError: + context = null_pointer + + modulus_bytes = self.size_in_bytes() + + if x is None: + xb = null_pointer + else: + xb = c_uint8_ptr(long_to_bytes(x, modulus_bytes)) + if len(xb) != modulus_bytes: + raise ValueError("Incorrect coordinate length") + self._point = VoidPointer() result = new_point(self._point.address_of(), - c_uint8_ptr(xb), - c_size_t(modulus_bytes)) + xb, + c_size_t(modulus_bytes), + context) if result == 15: raise ValueError("The EC point does not belong to the curve") @@ -424,7 +443,7 @@ def is_point_at_infinity(self): def point_at_infinity(self): """Return the *point-at-infinity* for the curve.""" - return EccXPoint(self._curve.Gx, self.curve) * 0 + return EccXPoint(None, self.curve) @property def x(self): diff --git a/lib/Crypto/SelfTest/PublicKey/__init__.py b/lib/Crypto/SelfTest/PublicKey/__init__.py index 967532ad..fa034158 100644 --- a/lib/Crypto/SelfTest/PublicKey/__init__.py +++ b/lib/Crypto/SelfTest/PublicKey/__init__.py @@ -30,6 +30,7 @@ test_ECC_Ed25519, test_ECC_Curve25519, test_ECC_Ed448, + test_ECC_Curve448, test_import_DSA, test_import_RSA, test_import_ECC, test_ElGamal, test_import_Curve25519) @@ -43,6 +44,7 @@ def get_tests(config={}): tests += test_ECC_Ed25519.get_tests(config=config) tests += test_ECC_Curve25519.get_tests(config=config) tests += test_ECC_Ed448.get_tests(config=config) + tests += test_ECC_Curve448.get_tests(config=config) tests += test_import_DSA.get_tests(config=config) tests += test_import_RSA.get_tests(config=config) diff --git a/lib/Crypto/SelfTest/PublicKey/test_ECC_Curve25519.py b/lib/Crypto/SelfTest/PublicKey/test_ECC_Curve25519.py index cda859eb..c637bb37 100644 --- a/lib/Crypto/SelfTest/PublicKey/test_ECC_Curve25519.py +++ b/lib/Crypto/SelfTest/PublicKey/test_ECC_Curve25519.py @@ -97,6 +97,9 @@ def test_pai(self): pai = point1.point_at_infinity() self.assertTrue(pai.point_at_infinity()) + point2 = EccXPoint(None, "curve25519") + self.assertTrue(point2.point_at_infinity()) + def test_scalar_multiply(self): base = EccXPoint(9, "curve25519") diff --git a/lib/Crypto/SelfTest/PublicKey/test_ECC_Curve448.py b/lib/Crypto/SelfTest/PublicKey/test_ECC_Curve448.py new file mode 100644 index 00000000..85c65ea4 --- /dev/null +++ b/lib/Crypto/SelfTest/PublicKey/test_ECC_Curve448.py @@ -0,0 +1,236 @@ +# This file is licensed under the BSD 2-Clause License. +# See https://opensource.org/licenses/BSD-2-Clause for details. + +import unittest +from binascii import unhexlify + +from Crypto.SelfTest.st_common import list_test_cases +from Crypto.Math.Numbers import Integer +from Crypto.Hash import SHAKE128 + +from Crypto.PublicKey import ECC +from Crypto.PublicKey.ECC import EccKey, EccXPoint, _curves + +CURVE448_P = 2**448 - 2**224 - 1 +CURVE448_ORDER = 2**446 - 0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d + +# Test vectors for scalar multiplication using point with X=5 as base +# Each tuple is (exponent, X-coordinate) +scalar_base5_test = [ + (1, 5), + (2, 0x6391322257cae3d49aef4665d8bd5cccac9abefb511e83d75f3c766616266fc1bf3747f1da00ed7125e8f0255a1208087d32a4bc1c743cb6), + (3, 0x1fbe4b3584cab86170c14b9325840b8a2429b61fb93c42492c002a2807a4e7ea63138ea59bf95652ce9a7d13d0321c7511e3314d0553f34c), + (4, 0x93b44a7b78726ba8d0b048bd7144074f8bdad24ef9d0a6c8264f6c00b135ffcea11545e80d18364acc8ebfbcc45358e0da5fd5e5146e2b1), + (6, 0x693d165f453bd62871e5e53845f33e9e5b18b24d79c1f9102608aa7ba6f18ac24864012171d64c90b698f5ce5631cd02cee4e4336b1ad88c), + (9, 0xb970d576e7d9aa427dbf7cb9b7dd65170721d04ee060c9ea8d499dc361d4cfde1ceb19068eae853bac8f5d92827bdbf3d94c22de2fb42dae), + (129, 0x9fbdb50a1450438fe656aa32aa1bb2548d077d5c3a5d327689093a2996a4f94eacd1fb4f90315edb2afe41908a759f0d6db83fa791df80db), + (255, 0x31bc3e9385dfd12e1238927061eb0c911466da394e459bf058ba3b08260a258a3c392b0f85ddbd23828657137b88577a85b83774139fab9e), + (256, 0x735c7f30e6872e5e4215c0147c8a112d697f668c9bd0f92f5f1e4e6badc128a0b654e697cd4bae2144d54e726b54c1fa63a09b00dd3c17f), + (257, 0x95c1b0ce01286dc047aeb5922a5e62b3effb5b9296273a5004eb456f592728dd494a6fb5996a2ea7011ae6423874a48c2927bfa62d8ce8b0), + (0x10101, 0x113bb172c9dc52ab45bd665dd9751ed44e33b8596f943c6cb2f8dd329160ece802960b3eb0d2c21ef3a3ac12c20fccbc2a271fc2f061c1b2), + (0xAA55CC, 0xcf42585d2e0b1e45c0bfd601c91af4b137d7faf139fc761178c7ded432417c307ee1759af2deec6a14dbaf6b868eb13a6039fbdde4b61898), + (0x1B29A0E579E0A000567, 0x7bd9ec9775a664f4d860d82d6be60895113a7c36f92db25583dbba5dc17f09c136ec27e14857bfd6a705311327030aa657dd036325fad330), + (CURVE448_ORDER + 1, 5), +] + + +class TestEccPoint_Curve448(unittest.TestCase): + + v1 = 0x09fa78b39b00a72930bcd8039be789a0997830bb99f79aeeb93493715390b4e8 + v2 = 0x15210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493 + + def test_init(self): + EccXPoint(5, "curve448") + EccXPoint(CURVE448_P - 5, "curve448") + + def test_curve_attribute(self): + point = EccXPoint(5, "curve448") + self.assertEqual(point.curve, "Curve448") + + def test_init_fail(self): + self.assertRaises(ValueError, EccXPoint, 3*CURVE448_P, "curve448") + self.assertRaises(ValueError, EccXPoint, 3, "curve449") + + def test_equal_set(self): + point1 = EccXPoint(self.v1, "curve448") + point2 = EccXPoint(self.v2, "curve448") + + self.assertEqual(point1, point1) + self.assertNotEqual(point1, point2) + + point2.set(point1) + self.assertEqual(point1.x, point2.x) + + def test_copy(self): + point1 = EccXPoint(self.v1, "curve448") + point2 = point1.copy() + self.assertEqual(point1.x, point2.x) + + def test_pai(self): + point1 = EccXPoint(self.v1, "curve448") + pai = point1.point_at_infinity() + self.assertTrue(pai.point_at_infinity()) + + point2 = EccXPoint(None, "curve448") + self.assertTrue(point2.point_at_infinity()) + + def test_scalar_multiply(self): + base = EccXPoint(5, "curve448") + + pointH = 0 * base + self.assertTrue(pointH.point_at_infinity()) + + pointH = CURVE448_ORDER * base + self.assertTrue(pointH.point_at_infinity()) + + pointH = base * 1 + self.assertEqual(pointH.x, 5) + + for d, result in scalar_base5_test: + pointH = d * base + self.assertEqual(pointH.x, result) + + def test_sizes(self): + point = EccXPoint(5, "curve448") + self.assertEqual(point.size_in_bits(), 448) + self.assertEqual(point.size_in_bytes(), 56) + + +class TestEccKey_Curve448(unittest.TestCase): + + def test_private_key(self): + # RFC7748 Section 6.2 - Alice + alice_priv = unhexlify("9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b") + alice_pub = unhexlify("9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0") + alice_pub_x = Integer.from_bytes(alice_pub, byteorder='little') + + key = EccKey(curve="Curve448", seed=alice_priv) + self.assertEqual(key.seed, alice_priv) + self.assertTrue(key.has_private()) + self.assertEqual(key.pointQ.x, alice_pub_x) + + # RFC7748 Section 6.2 - Bob + bob_priv = unhexlify("1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d") + bob_pub = unhexlify("3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609") + bob_pub_x = Integer.from_bytes(bob_pub, byteorder='little') + + key = EccKey(curve="Curve448", seed=bob_priv) + self.assertEqual(key.seed, bob_priv) + self.assertTrue(key.has_private()) + self.assertEqual(key.pointQ.x, bob_pub_x) + + # Other names + key = EccKey(curve="curve448", seed=alice_priv) + + # Must not accept d parameter + self.assertRaises(ValueError, EccKey, curve="curve448", d=1) + + def test_public_key(self): + point = EccXPoint(_curves['curve448'].Gx, + curve='curve448') + key = EccKey(curve="curve448", point=point) + self.assertFalse(key.has_private()) + self.assertEqual(key.pointQ, point) + + def test_public_key_derived(self): + priv_key = EccKey(curve="curve448", seed=b'H'*56) + pub_key = priv_key.public_key() + self.assertFalse(pub_key.has_private()) + self.assertEqual(priv_key.pointQ, pub_key.pointQ) + + def test_invalid_seed(self): + self.assertRaises(ValueError, lambda: EccKey(curve="curve448", + seed=b'H' * 55)) + + def test_equality(self): + private_key = ECC.construct(seed=b'H'*56, curve="Curve448") + private_key2 = ECC.construct(seed=b'H'*56, curve="curve448") + private_key3 = ECC.construct(seed=b'C'*56, curve="Curve448") + + public_key = private_key.public_key() + public_key2 = private_key2.public_key() + public_key3 = private_key3.public_key() + + self.assertEqual(private_key, private_key2) + self.assertNotEqual(private_key, private_key3) + + self.assertEqual(public_key, public_key2) + self.assertNotEqual(public_key, public_key3) + + self.assertNotEqual(public_key, private_key) + + def test_name_consistency(self): + key = ECC.generate(curve='curve448') + self.assertIn("curve='Curve448'", repr(key)) + self.assertEqual(key.curve, 'Curve448') + self.assertEqual(key.public_key().curve, 'Curve448') + + +class TestEccModule_Curve448(unittest.TestCase): + + def test_generate(self): + key = ECC.generate(curve="Curve448") + self.assertTrue(key.has_private()) + point = EccXPoint(_curves['Curve448'].Gx, curve="Curve448") * key.d + self.assertEqual(key.pointQ, point) + + # Always random + key2 = ECC.generate(curve="Curve448") + self.assertNotEqual(key, key2) + + # Other names + ECC.generate(curve="curve448") + + # Random source + key1 = ECC.generate(curve="Curve448", randfunc=SHAKE128.new().read) + key2 = ECC.generate(curve="Curve448", randfunc=SHAKE128.new().read) + self.assertEqual(key1, key2) + + def test_construct(self): + seed = unhexlify("9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b") + point_hex = unhexlify("9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0") + Px = Integer.from_bytes(point_hex, byteorder='little') + point = EccXPoint(Px, curve="Curve448") + + # Private key only + key = ECC.construct(curve="Curve448", seed=seed) + self.assertEqual(key.pointQ, point) + self.assertTrue(key.has_private()) + + # Public key only + key = ECC.construct(curve="Curve448", point_x=Px) + self.assertEqual(key.pointQ, point) + self.assertFalse(key.has_private()) + + # Private and public key + key = ECC.construct(curve="Curve448", seed=seed, point_x=Px) + self.assertEqual(key.pointQ, point) + self.assertTrue(key.has_private()) + + # Other names + key = ECC.construct(curve="curve448", seed=seed) + + def test_negative_construct(self): + coordG = dict(point_x=_curves['curve448'].Gx) + + self.assertRaises(ValueError, ECC.construct, curve="Curve448", + d=2, **coordG) + self.assertRaises(ValueError, ECC.construct, curve="Curve448", + seed=b'H'*55) + + # Verify you cannot construct weak keys (small-order points) + # TODO + + +def get_tests(config={}): + tests = [] + tests += list_test_cases(TestEccPoint_Curve448) + tests += list_test_cases(TestEccKey_Curve448) + tests += list_test_cases(TestEccModule_Curve448) + return tests + + +if __name__ == '__main__': + def suite(): + return unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') diff --git a/setup.py b/setup.py index 9214c655..2bbe833f 100644 --- a/setup.py +++ b/setup.py @@ -451,6 +451,11 @@ def create_cryptodome_lib(): sources=['src/curve25519.c'], py_limited_api=True, ), + Extension("Crypto.PublicKey._curve448", + include_dirs=['src/'], + sources=['src/curve448.c', 'src/mont1.c'], + py_limited_api=True, + ), Extension("Crypto.PublicKey._ed25519", include_dirs=['src/'], sources=['src/ed25519.c'], @@ -458,14 +463,14 @@ def create_cryptodome_lib(): ), Extension("Crypto.PublicKey._ed448", include_dirs=['src/'], - sources=['src/ed448.c', 'src/mont1.c'], + sources=['src/ed448.c', 'src/mont2.c'], py_limited_api=True, ), # Math Extension("Crypto.Math._modexp", include_dirs=['src/'], - sources=['src/modexp.c', 'src/mont2.c'], + sources=['src/modexp.c', 'src/mont3.c'], py_limited_api=True, ), ] diff --git a/src/Makefile b/src/Makefile index b8917766..73e095af 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,7 +4,7 @@ CFLAGS=-Werror -Wall -O3 -g -Wno-unused-const-variable -Wconversion -Wsign-conve CFLAGS += -fanalyzer -all:: modexp ec_ws_p256 ec_ws_p384 ec_ws_p521 ed25519_perf ed448_perf curve25519_perf +all:: modexp ec_ws_p256 ec_ws_p384 ec_ws_p521 ed25519_perf ed448_perf curve25519_perf curve448_perf ec_ws_p256: ec_ws_p256.c mont.c p256_table.c p384_table.c p521_table.c $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ -DSYS_BITS=64 -DMAIN @@ -39,5 +39,8 @@ p521_table.c: make_ecc_table.py curve25519_perf: curve25519.c multiply_64.c $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ -DSYS_BITS=64 -DPROFILE +curve448_perf: curve448.c mont.c + $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ -DSYS_BITS=64 -DPROFILE + clean:: - rm -f ec_ws_p256 ec_ws_p384 ec_ws_p521 mont.o modexp x25519 ed25519_perf ed448_perf curve25519_perf + rm -f ec_ws_p256 ec_ws_p384 ec_ws_p521 mont.o modexp x25519 ed25519_perf ed448_perf curve25519_perf curve448_perf diff --git a/src/curve25519.c b/src/curve25519.c index 014d271a..e2b31cb7 100644 --- a/src/curve25519.c +++ b/src/curve25519.c @@ -154,20 +154,29 @@ STATIC void curve25519_scalar_internal(Point *Pout, EXPORT_SYM int curve25519_new_point(Point **out, const uint8_t x[32], - size_t modsize) + size_t modsize, + const void *context) { - if ((NULL == out) || (NULL == x)) + if (NULL == out) return ERR_NULL; - if (modsize != 32) + if (context != NULL) + return ERR_UNKNOWN; + + if ((modsize != 32) && (modsize != 0)) return ERR_MODULUS; *out = calloc(1, sizeof(Point)); if (NULL == *out) return ERR_MEMORY; - convert_be8_to_le25p5((*out)->X, x); - (*out)->Z[0] = 1; + if ((x != NULL) && (modsize == 32)) { + convert_be8_to_le25p5((*out)->X, x); + (*out)->Z[0] = 1; + } else { + /** PAI **/ + (*out)->X[0] = 1; + } /* No need to verify if the point is on the Curve25519 curve */ diff --git a/src/curve25519.h b/src/curve25519.h index db7a70b3..a60e00ae 100644 --- a/src/curve25519.h +++ b/src/curve25519.h @@ -8,7 +8,8 @@ typedef struct Point { EXPORT_SYM int curve25519_new_point(Point **out, const uint8_t x[32], - size_t modsize); + size_t modsize, + const void *context); EXPORT_SYM int curve25519_clone(Point **P, const Point *Q); EXPORT_SYM void curve25519_free_point(Point *p); EXPORT_SYM int curve25519_get_x(uint8_t *xb, size_t modsize, const Point *p); diff --git a/src/curve448.c b/src/curve448.c new file mode 100644 index 00000000..d0fb59ac --- /dev/null +++ b/src/curve448.c @@ -0,0 +1,483 @@ +/* Copyright (C) 2024 by Helder Eijs -- All rights reserved. + * This code is licensed under the BSD 2-Clause license. See LICENSE.rst file for info. */ + +/* + * curve448 is a Montgomery curve with equation: + * + * y² = x³ + Ax² + x + * + * over the prime field 2⁴⁴⁸ - 2²²⁴ - 1 with A = 156326. + * It has cofactor 4 and order 2⁴⁴⁶ - 0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d. + * Also, it is birationally equivalent to the untwisted Edwards curve ed448. + * + * A point is represented by coordinates (X, Z) so that x = X/Z for a + * non-zero Z, with two possible y-coordinates. + * + * In this implementation, Z is always 1. + * + * The PAI (or neutral point) is (X, 0). + */ + +#include "common.h" +#include "endianess.h" +#include "mont.h" +#include "curve448.h" + +FAKE_INIT(curve448) + +STATIC void free_workplace(WorkplaceCurve448 *wp) +{ + if (wp) { + free(wp->a); + free(wp->b); + free(wp->scratch); + free(wp); + } +} + +STATIC WorkplaceCurve448 *new_workplace(const MontContext *ctx) +{ + WorkplaceCurve448 *wp = NULL; + int res; + + wp = calloc(1, sizeof(WorkplaceCurve448)); + if (NULL == wp) + return NULL; + + res = mont_new_number(&wp->a, 1, ctx); + if (res) goto cleanup; + res = mont_new_number(&wp->b, 1, ctx); + if (res) goto cleanup; + res = mont_new_number(&wp->scratch, SCRATCHPAD_NR, ctx); + if (res) goto cleanup; + return wp; + +cleanup: + free_workplace(wp); + return NULL; +} + +/* + * Swap arguments a/c and b/d when condition is NOT ZERO. + * If the condition IS ZERO, no swapping takes place. + */ +STATIC void cswap(uint64_t a[7], uint64_t b[7], uint64_t c[7], uint64_t d[7], unsigned swap) +{ + uint64_t mask, i, e, f; + + mask = (uint64_t)(0 - (swap!=0)); /* 0 if swap is 0, all 1s if swap is !=0 */ + for (i=0; i<7; i++) { + e = mask & (a[i] ^ c[i]); + a[i] ^= e; + c[i] ^= e; + f = mask & (b[i] ^ d[i]); + b[i] ^= f; + d[i] ^= f; + } +} + +/* + * Perform a step of the Montgomery ladder. + * It is based on the function f() such that: + * + * P_{m+n} = f(P_m, P_n, P_{n-m}) + * + * limited to the cases: + * + * P_{2n} = f(P_n, P_n, P_0) + * P_{2n+1} = f(P_n, P_{n+1}, P_1) + * + * so that: + * + * P_2 = f(P_1, P_1, P_0) + * P_3 = f(P_1, P_2, P_1) + * P_4 = f(P_2, P_2, P_0) + * P_5 = f(P_2, P_3, P_1) + * P_6 = f(P_3, P_3, P_0) + * ... + * + * Here we efficiently combine two computations of f(): + * + * P_{2n} = f(P_n, P_n, P_0) + * P_{2n+1} = f(P_n, P_{n+1}, P_1) + * + * Ref: https://eprint.iacr.org/2017/293.pdf + * + * @param[in,out] P2 In input, the point to double. + * In output, the double of the original P2. + * @param[in,out] P3 In input, the point to double (P2) + P1. + * In output, the double of the original P2 + P1. + * @param[in] P1 The fixed P1 point (Z1=1). + */ +STATIC void curve448_ladder_step(Curve448Point *P2, Curve448Point *P3, const Curve448Point *P1) +{ + const MontContext *ctx = P2->ec_ctx->mont_ctx; + const uint64_t *a24 = P2->ec_ctx->a24; + uint64_t *t0 = P2->wp->a; + uint64_t *t1 = P2->wp->b; + uint64_t *x2 = P2->x; + uint64_t *z2 = P2->z; + uint64_t *x3 = P3->x; + uint64_t *z3 = P3->z; + const uint64_t *xp = P1->x; + uint64_t *scratch = P2->wp->scratch; + + /** https://www.hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#ladder-mladd-1987-m **/ + + mont_sub(t0, x3, z3, scratch, ctx); /* t0 = D = X3 - Z3 */ + mont_sub(t1, x2, z2, scratch, ctx); /* t1 = B = X2 - Z2 */ + mont_add(x2, x2, z2, scratch, ctx); /* x2 = A = X2 + Z2 */ + mont_add(z2, x3, z3, scratch, ctx); /* z2 = C = X3 - Z3 */ + mont_mult(z3, t0, x2, scratch, ctx); /* z3 = DA */ + mont_mult(z2, z2, t1, scratch, ctx); /* z2 = CB */ + mont_add(x3, z3, z2, scratch, ctx); /* x3 = DA+CB */ + mont_sub(z2, z3, z2, scratch, ctx); /* z2 = DA-CB */ + mont_mult(x3, x3, x3, scratch, ctx); /* x3 = X5 = (DA+CB)² */ + mont_mult(z2, z2, z2, scratch, ctx); /* z2 = (DA-CB)² */ + mont_mult(t0, t1, t1, scratch, ctx); /* t0 = BB = B² */ + mont_mult(t1, x2, x2, scratch, ctx); /* t1 = AA = A² */ + mont_sub(x2, t1, t0, scratch, ctx); /* x2 = E = AA-BB */ + mont_mult(z3, xp, z2, scratch, ctx); /* z3 = Z5 = X1*(DA-CB)² */ + mont_mult(z2, a24, x2, scratch, ctx); /* z2 = a24*E */ + mont_add(z2, t0, z2, scratch, ctx); /* z2 = BB+a24*E */ + mont_mult(z2, x2, z2, scratch, ctx); /* z2 = Z4 = E*(BB+a24*E) */ + mont_mult(x2, t1, t0, scratch, ctx); /* x2 = X4 = AA*BB */ +} + +/* + * Scalar multiplication Q = k*B + * + * @param[out] Pout The output point Q. + * @param[in] k The scalar encoded in big-endian mode. + * @param[in] len Length of the scalar in bytes. + * @param[in] Pin The input point B. + */ +STATIC int curve448_scalar_internal(Curve448Point *Pout, + const uint8_t *k, + size_t len, + const Curve448Point *Pin) +{ + Curve448Point *P2 = NULL; + Curve448Point *P3 = NULL; + const Curve448Context *ec_ctx = Pin->ec_ctx; + const MontContext *mont_ctx = ec_ctx->mont_ctx; + unsigned bit_idx, swap; + size_t scan; + int res; + + /* P2 = PAI */ + res = curve448_new_point(&P2, NULL, 0, Pin->ec_ctx); + if (res) goto cleanup; + + /* P3 = Pin */ + res = curve448_clone(&P3, Pin); + if (res) goto cleanup; + + /* + * https://eprint.iacr.org/2020/956.pdf + * https://www.ams.org/journals/mcom/1987-48-177/S0025-5718-1987-0866113-7/S0025-5718-1987-0866113-7.pdf + */ + + /* Scan all bits from MSB to LSB */ + bit_idx = 7; + swap = 0; + scan = 0; + while (scan> bit_idx) & 1; + swap ^= bit; + cswap(P2->x, P2->z, P3->x, P3->z, swap); + curve448_ladder_step(P2, P3, Pin); + swap = bit; + if (bit_idx-- == 0) { + bit_idx = 7; + scan++; + } + } + cswap(P2->x, P2->z, P3->x, P3->z, swap); + + /* P2 is the result */ + + if (mont_is_zero(P2->z, mont_ctx)) { + mont_set(Pout->x, 1, mont_ctx); + mont_set(Pout->z, 0, mont_ctx); + } else { + uint64_t *invz = Pout->wp->a; + + /** TODO: replace with add chain **/ + res = mont_inv_prime(invz, P2->z, mont_ctx); + if (res) goto cleanup; + res = mont_mult(Pout->x, P2->x, invz, P2->wp->scratch, mont_ctx); + if (res) goto cleanup; + mont_set(Pout->z, 1, mont_ctx); + } + res = 0; + +cleanup: + curve448_free_point(P2); + curve448_free_point(P3); + return res; +} + +/* ------------------------------------- */ + +/* + * Create an Elliptic Curve context for Curve448 + * + * @param pec_ctx The memory area where the pointer to the newly allocated + * EC context will be stored. + * @return 0 for success, the appropriate error code otherwise + */ +EXPORT_SYM int curve448_new_context(Curve448Context **pec_ctx) +{ + Curve448Context *ec_ctx = NULL; + int res; + MontContext *ctx; + const uint8_t mod448_be[56] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + if (NULL == pec_ctx) + return ERR_NULL; + + *pec_ctx = ec_ctx = (Curve448Context*)calloc(1, sizeof(Curve448Context)); + if (NULL == ec_ctx) + return ERR_MEMORY; + + res = mont_context_init(&ec_ctx->mont_ctx, mod448_be, sizeof(mod448_be)); + if (res) goto cleanup; + ctx = ec_ctx->mont_ctx; + + /* a24 = (a+2)/4 */ + res = mont_new_from_uint64(&ec_ctx->a24, 39082, ctx); + if (res) goto cleanup; + + return 0; + +cleanup: + free(ec_ctx->a24); + mont_context_free(ec_ctx->mont_ctx); + free(ec_ctx); + return res; +} + +EXPORT_SYM void curve448_free_context(Curve448Context *ec_ctx) +{ + if (NULL != ec_ctx) { + free(ec_ctx->a24); + mont_context_free(ec_ctx->mont_ctx); + free(ec_ctx); + } +} + +/* + * Create a new EC point on the Curve448 curve. + * + * @param out The memory area where the pointer to the newly allocated EC + * point will be stored. + * Use curve448_free_point() for deallocating it. + * @param x The X-coordinate (affine, big-endian, smaller than modulus) + * @param len The length of x in bytes (max 56 bytes) + * @param ec_ctx The EC context + * @return 0 for success, the appropriate error code otherwise + * + * If x is NULL or len is 0, the point will be the point at infinity. + */ +EXPORT_SYM int curve448_new_point(Curve448Point **out, + const uint8_t *x, + size_t len, + const Curve448Context *ec_ctx) +{ + int res; + Curve448Point *ecp = NULL; + const MontContext *mont_ctx; + + if (NULL == out || NULL == ec_ctx) + return ERR_NULL; + + if (len > ec_ctx->mont_ctx->bytes) { + return ERR_VALUE; + } + + *out = ecp = (Curve448Point*)calloc(1, sizeof(Curve448Point)); + if (NULL == ecp) + return ERR_MEMORY; + + ecp->ec_ctx = (Curve448Context*) ec_ctx; + mont_ctx = ec_ctx->mont_ctx; + + if ((NULL == x) || (0 == len)) { + res = mont_new_from_uint64(&ecp->x, 1, mont_ctx); + if (res) goto cleanup; + res = mont_new_from_uint64(&ecp->z, 0, mont_ctx); + if (res) goto cleanup; + } else { + res = mont_new_from_bytes(&ecp->x, x, len, mont_ctx); + if (res) goto cleanup; + res = mont_new_from_uint64(&ecp->z, 1, mont_ctx); + if (res) goto cleanup; + } + + ecp->wp = new_workplace(mont_ctx); + if (NULL == ecp->wp) { + res = ERR_MEMORY; + goto cleanup; + } + + /* No need to verify if the point is on the Curve448 curve */ + return 0; + +cleanup: + free(ecp->x); + free(ecp->z); + free(ecp->wp); + free(ecp); + *out = NULL; + return res; +} + +EXPORT_SYM void curve448_free_point(Curve448Point *ecp) +{ + /* The EC context (ecp->ecp_ctx) is allocated once and shared by all + * points on the same surve, so we will not free it here. + */ + if (ecp) { + free_workplace(ecp->wp); + free(ecp->x); + free(ecp->z); + free(ecp); + } +} + +EXPORT_SYM int curve448_clone(Curve448Point **pecp2, const Curve448Point *ecp) +{ + int res = -1; + Curve448Point *ecp2; + MontContext *ctx; + + if (NULL == pecp2 || NULL == ecp) + return ERR_NULL; + ctx = ecp->ec_ctx->mont_ctx; + + *pecp2 = ecp2 = (Curve448Point*)calloc(1, sizeof(Curve448Point)); + if (NULL == ecp2) + return ERR_MEMORY; + + ecp2->ec_ctx = ecp->ec_ctx; + + ecp2->wp = new_workplace(ctx); + if (NULL == ecp2->wp) goto cleanup; + + res = mont_new_number(&ecp2->x, 1, ctx); + if (res) goto cleanup; + res = mont_copy(ecp2->x, ecp->x, ctx); + if (res) goto cleanup; + + res = mont_new_number(&ecp2->z, 1, ctx); + if (res) goto cleanup; + res = mont_copy(ecp2->z, ecp->z, ctx); + if (res) goto cleanup; + + return 0; + +cleanup: + free_workplace(ecp2->wp); + free(ecp2->x); + free(ecp2->z); + free(ecp2); + *pecp2 = NULL; + return res; +} + +EXPORT_SYM int curve448_get_x(uint8_t *xb, size_t modsize, const Curve448Point *p) +{ + MontContext *mont_ctx; + + if ((NULL == xb) || (NULL == p)) + return ERR_NULL; + + mont_ctx = p->ec_ctx->mont_ctx; + + if (modsize != 56) + return ERR_MODULUS; + + if (mont_is_zero(p->z, mont_ctx)) + return ERR_EC_PAI; + + /** p->Z == 1 **/ + + return mont_to_bytes(xb, modsize, p->x, mont_ctx); +} + +EXPORT_SYM int curve448_scalar(Curve448Point *P, const uint8_t *scalar, size_t scalar_len, uint64_t seed) +{ + if ((NULL == P) || (NULL == scalar)) + return ERR_NULL; + + curve448_scalar_internal(P, scalar, scalar_len, P); + return 0; +} + +EXPORT_SYM int curve448_cmp(const Curve448Point *p1, const Curve448Point *p2) +{ + MontContext *ctx; + WorkplaceCurve448 *wp; + uint64_t *scratch; + int res; + + if (NULL == p1 || NULL == p2) + return ERR_NULL; + + if (p1->ec_ctx != p2->ec_ctx) + return ERR_EC_CURVE; + + ctx = p1->ec_ctx->mont_ctx; + wp = p1->wp; + scratch = wp->scratch; + + mont_mult(wp->a, p1->x, p2->z, scratch, ctx); + mont_mult(wp->b, p1->z, p2->x, scratch, ctx); + res = mont_is_equal(wp->a, wp->b, ctx); + + return res ? 0 : ERR_VALUE; +} + +#ifdef PROFILE +int main(void) +{ + uint8_t pubkey[56]; + uint8_t secret[56]; + unsigned i; + Curve448Context *ec_ctx; + Curve448Point *Pin = NULL; + Curve448Point *Pout = NULL; + int res; + + secret[0] = pubkey[0] = 0xAA; + for (i=1; i<56; i++) { + secret[i] = pubkey[i] = (uint8_t)((secret[i-1] << 1) | (secret[i-1] >> 7)); + } + + res = curve448_new_context(&ec_ctx); + assert(res == 0); + + res = curve448_new_point(&Pin, pubkey, 56, ec_ctx); + assert(res == 0); + + res = curve448_new_point(&Pout, NULL, 56, ec_ctx); + assert(res == 0); + + for (i=0; i<10000; i++) { + res = curve448_scalar_internal(Pout, secret, sizeof secret, Pin); + } + + curve448_free_point(Pin); + curve448_free_point(Pout); + curve448_free_context(ec_ctx); +} +#endif diff --git a/src/curve448.h b/src/curve448.h new file mode 100644 index 00000000..754299b2 --- /dev/null +++ b/src/curve448.h @@ -0,0 +1,35 @@ +#ifndef _CURVE448_H +#define _CURVE448_H + +#include "mont.h" + +typedef struct _WorkplaceCurve448 { + uint64_t *a, *b; + uint64_t *scratch; +} WorkplaceCurve448; + +typedef struct _Curve448Context { + MontContext *mont_ctx; + uint64_t *a24; /* encoded in Montgomery form */ +} Curve448Context; + +typedef struct Curve448Point { + Curve448Context *ec_ctx; + WorkplaceCurve448 *wp; + uint64_t *x; + uint64_t *z; +} Curve448Point; + +EXPORT_SYM int curve448_new_context(Curve448Context **pec_ctx); +EXPORT_SYM void curve448_free_context(Curve448Context *ec_ctx); +EXPORT_SYM int curve448_new_point(Curve448Point **out, + const uint8_t *x, + size_t len, + const Curve448Context *ec_ctx); +EXPORT_SYM void curve448_free_point(Curve448Point *p); +EXPORT_SYM int curve448_clone(Curve448Point **P, const Curve448Point *Q); +EXPORT_SYM int curve448_get_x(uint8_t *xb, size_t modsize, const Curve448Point *p); +EXPORT_SYM int curve448_scalar(Curve448Point *P, const uint8_t *scalar, size_t scalar_len, uint64_t seed); +EXPORT_SYM int curve448_cmp(const Curve448Point *ecp1, const Curve448Point *ecp2); + +#endif diff --git a/src/mont3.c b/src/mont3.c new file mode 100644 index 00000000..558bcad6 --- /dev/null +++ b/src/mont3.c @@ -0,0 +1,6 @@ +/* + This file is used to workaround a bug in distutils that causes race + conditions when the same source file is used in multiple extensions. + */ + +#include "mont.c" diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 828b711b..d8ad392d 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -196,6 +196,10 @@ add_test(NAME test_ed25519 COMMAND test_ed25519) add_executable(test_ed448 test_ed448.c ../ed448.c $) add_test(NAME test_ed448 COMMAND test_ed448) +# curve448 +add_executable(test_curve448 test_curve448.c ../curve448.c $) +add_test(NAME test_curve448 COMMAND test_curve448) + # aesni if (AESNI) add_executable(test_aesni test_aesni.c ../AESNI.c) diff --git a/src/test/test_curve25519.c b/src/test/test_curve25519.c index bb16e599..27532643 100644 --- a/src/test/test_curve25519.c +++ b/src/test/test_curve25519.c @@ -43,7 +43,7 @@ void test_ladder_1(void) Point *Pin; Point Pout; - curve25519_new_point(&Pin, pubkey, 32); + curve25519_new_point(&Pin, pubkey, 32, NULL); /* Clamping BE/LE */ scalar[31-0] &= 248; diff --git a/src/test/test_curve448.c b/src/test/test_curve448.c new file mode 100644 index 00000000..d31270b6 --- /dev/null +++ b/src/test/test_curve448.c @@ -0,0 +1,178 @@ +#include "endianess.h" +#include "curve448.h" +#include "mont.h" +#include + +#if 0 +void print_point(Curve448Point *p) +{ + mont_printf("X=", p->x, p->ec_ctx->mont_ctx); + mont_printf("Z=", p->z, p->ec_ctx->mont_ctx); +} +#endif + +void test_ladder_1(void) +{ + Curve448Context *ec_ctx; + Curve448Point *point; + int res; + + const uint8_t x[56] = { 0x86, 0xa0, 0xf8, 0x4e, 0xfb, 0xa7, 0xa7, 0x8a, + 0xa1, 0xad, 0x94, 0xdb, 0x29, 0x54, 0xfa, 0x83, + 0x25, 0xda, 0xc6, 0x19, 0x8c, 0xc3, 0xbd, 0xdd, + 0x31, 0xc0, 0x4d, 0x81, 0xf9, 0x08, 0x0f, 0x02, + 0x7f, 0x43, 0x07, 0xbd, 0x4c, 0x33, 0x88, 0xad, + 0x8a, 0x3f, 0x26, 0xd5, 0xf2, 0x6c, 0x5f, 0xda, + 0xbf, 0x87, 0x34, 0xfa, 0x40, 0xe6, 0xfc, 0x06 }; + + const uint8_t scalar[] = { 0xd3, 0x0a, 0x60, 0x1c, 0x4f, 0x9a, 0x25, 0x29, + 0x4b, 0xf5, 0x68, 0xa3, 0xeb, 0x43, 0x49, 0xf4, + 0xbf, 0x8f, 0xd7, 0xcd, 0xf8, 0x24, 0x4c, 0x98, + 0x9c, 0x77, 0x0a, 0x70, 0x21, 0xe1, 0xaa, 0xd1, + 0xd0, 0x04, 0x51, 0x04, 0xef, 0xac, 0x82, 0x88, + 0xd2, 0x34, 0x9a, 0xa1, 0xfe, 0x66, 0x52, 0x49, + 0x88, 0x8e, 0xec, 0xf9, 0xdd, 0x2f, 0x26, 0x3c }; + + const uint8_t expected[56] = { 0x6f, 0x6b, 0xd9, 0x3d, 0xf7, 0x82, 0x62, 0x76, + 0x21, 0x1e, 0x11, 0x61, 0x39, 0x22, 0x98, 0x9d, + 0x77, 0xb0, 0x01, 0x6a, 0xc6, 0x5f, 0x44, 0xeb, + 0xad, 0xba, 0x4f, 0xe1, 0x9f, 0x23, 0x5f, 0x6d, + 0x54, 0xd7, 0x12, 0x24, 0x0a, 0xb5, 0x79, 0xdf, + 0xfb, 0x6a, 0x5e, 0xd8, 0xb1, 0x1d, 0xda, 0x97, + 0x66, 0xdc, 0x60, 0x5a, 0xf9, 0x4f, 0x3e, 0xce }; + + uint8_t result_x[56]; + + res = curve448_new_context(&ec_ctx); + assert(res == 0); + + res = curve448_new_point(&point, x, sizeof(x), ec_ctx); + assert(res == 0); + + res = curve448_scalar(point, scalar, sizeof(scalar), 0); + + assert(mont_is_one(point->z, point->ec_ctx->mont_ctx)); + res = mont_to_bytes(result_x, sizeof(result_x), point->x, point->ec_ctx->mont_ctx); + assert(res == 0); + assert(memcmp(expected, result_x, sizeof(expected)) == 0); + + curve448_free_point(point); + curve448_free_context(ec_ctx); +} + +void test_ladder_2(void) +{ + Curve448Context *ec_ctx; + Curve448Point *point; + int res; + uint8_t x[] = { 5 }; + uint8_t scalar[] = { 0 }; + + res = curve448_new_context(&ec_ctx); + assert(res == 0); + + res = curve448_new_point(&point, x, sizeof(x), ec_ctx); + assert(res == 0); + + res = curve448_scalar(point, scalar, sizeof(scalar), 0); + + /** PAI **/ + assert(mont_is_one(point->x, point->ec_ctx->mont_ctx)); + assert(mont_is_zero(point->z, point->ec_ctx->mont_ctx)); + + curve448_free_point(point); + curve448_free_context(ec_ctx); +} + +void test_cmp_1(void) +{ + uint8_t c1[56] = { 0xd7, 0x41, 0x07, 0x7b, 0xae, 0x25, 0x76, 0x75, + 0xdb, 0xb5, 0x43, 0x55, 0x0d, 0x6f, 0x27, 0xda, + 0x32, 0x89, 0x21, 0xfd, 0xb9, 0x9b, 0xf5, 0x4e, + 0xbe, 0x9d, 0x4d, 0x0b, 0xcc, 0x58, 0xe9, 0x67, + 0xff, 0x6f, 0xd1, 0xe1, 0x18, 0x2b, 0x22, 0x0f, + 0xa0, 0x05, 0x7f, 0x0b, 0x0d, 0x3b, 0xc8, 0x3f, + 0x86, 0xae, 0x38, 0xef, 0xb3, 0x5f, 0x5a, 0x35 }; + + Curve448Context *ec_ctx; + Curve448Point *G, *P; + int res; + + res = curve448_new_context(&ec_ctx); + assert(res == 0); + + // G = (C1, 1) + res = curve448_new_point(&G, c1, sizeof(c1), ec_ctx); + assert(res == 0); + + // P = (C1*C2, C2) + res = curve448_clone(&P, G); + assert(res == 0); + + mont_set(P->z, 0x12345678U, P->ec_ctx->mont_ctx); + mont_mult(P->x, G->x, P->z, P->wp->scratch, P->ec_ctx->mont_ctx); + + res = curve448_cmp(G, P); + assert(res == 0); + + mont_set(G->z, 2, G->ec_ctx->mont_ctx); + + // G = (C1, 2) + res = curve448_cmp(G, P); + assert(res != 0); + + curve448_free_point(P); + curve448_free_point(G); + curve448_free_context(ec_ctx); +} + +void test_cmp_2(void) +{ + const uint8_t c1[56] = { 0xd7, 0x41, 0x07, 0x7b, 0xae, 0x25, 0x76, 0x75, + 0xdb, 0xb5, 0x43, 0x55, 0x0d, 0x6f, 0x27, 0xda, + 0x32, 0x89, 0x21, 0xfd, 0xb9, 0x9b, 0xf5, 0x4e, + 0xbe, 0x9d, 0x4d, 0x0b, 0xcc, 0x58, 0xe9, 0x67, + 0xff, 0x6f, 0xd1, 0xe1, 0x18, 0x2b, 0x22, 0x0f, + 0xa0, 0x05, 0x7f, 0x0b, 0x0d, 0x3b, 0xc8, 0x3f, + 0x86, 0xae, 0x38, 0xef, 0xb3, 0x5f, 0x5a, 0x35 }; + const uint8_t scalar[] = { 0 }; + + Curve448Context *ec_ctx; + Curve448Point *G, *P, *Q; + int res; + + res = curve448_new_context(&ec_ctx); + assert(res == 0); + + /** 3 different ways to create API **/ + + res = curve448_new_point(&G, 0, sizeof(c1), ec_ctx); + assert(res == 0); + + res = curve448_new_point(&P, c1, 0, ec_ctx); + assert(res == 0); + + res = curve448_new_point(&Q, c1, sizeof(c1), ec_ctx); + assert(res == 0); + res = curve448_scalar(Q, scalar, sizeof(scalar), 0); + assert(res == 0); + + res = curve448_cmp(G, P); + assert(res == 0); + res = curve448_cmp(G, Q); + assert(res == 0); + + curve448_free_point(Q); + curve448_free_point(P); + curve448_free_point(G); + curve448_free_context(ec_ctx); +} + +int main(void) +{ + test_ladder_1(); + test_ladder_2(); + test_cmp_1(); + test_cmp_2(); + return 0; +} From 569845f6f7a90b830abb3eba149a3c276f556663 Mon Sep 17 00:00:00 2001 From: Helder Eijs Date: Sat, 7 Sep 2024 10:03:09 +0200 Subject: [PATCH 08/18] mont_new_from_bytes() can take numbers larger than the modulus --- src/bignum.c | 2 +- src/mont.c | 21 +++++++++++---------- src/test/test_mont.c | 21 +++++++++++++++++++-- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/bignum.c b/src/bignum.c index b4004acf..43cb824d 100644 --- a/src/bignum.c +++ b/src/bignum.c @@ -238,7 +238,7 @@ STATIC void add_mod(uint64_t* out, const uint64_t* a, const uint64_t* b, const u /* * Subtract two multi-word numbers with modulo arithmetic. * - * @param out The locaton where the multi-word result (nw words) is stored + * @param out The location where the multi-word result (nw words) is stored * @param a The number it will be subtracted from (nw words) * @param b The number to subtract (nw wordS) * @param modulus The modulus (nw words) diff --git a/src/mont.c b/src/mont.c index bc35e2da..d9ec41f4 100644 --- a/src/mont.c +++ b/src/mont.c @@ -794,7 +794,7 @@ int mont_new_random_number(uint64_t **out, unsigned count, uint64_t seed, const * The memory will contain the number encoded in Montgomery form. * The caller is responsible for deallocating the memory. * @param ctx Montgomery context, as created by mont_context_init(). - * @param number The big endian-encoded number to transform, strictly smaller than the modulus. + * @param number The big endian-encoded number to transform. * @param len The length of the big-endian number in bytes (this may be * smaller than the output of mont_bytes(ctx)). * @return 0 in case of success, the relevant error code otherwise. @@ -835,12 +835,6 @@ int mont_new_from_bytes(uint64_t **out, const uint8_t *number, size_t len, const } bytes_to_words(tmp1, ctx->words, number, len); - /** Make sure numbermodulus, ctx->words)) { - res = ERR_VALUE; - goto cleanup; - } - /** Scratchpad **/ scratchpad = (uint64_t*)calloc(SCRATCHPAD_NR, ctx->words*sizeof(uint64_t)); if (NULL == scratchpad) { @@ -848,10 +842,17 @@ int mont_new_from_bytes(uint64_t **out, const uint8_t *number, size_t len, const goto cleanup; } - if (ctx->modulus_type != ModulusP521) + if (ctx->modulus_type != ModulusP521) { mont_mult_generic(encoded, tmp1, ctx->r2_mod_n, ctx->modulus, ctx->m0, scratchpad, ctx->words); - else - mont_copy(encoded, tmp1, ctx); + } else { + while (ge(tmp1, ctx->modulus, ctx->words)) { + res = sub(tmp1, tmp1, ctx->modulus, ctx->words); + if (res) goto cleanup; + } + res = mont_copy(encoded, tmp1, ctx); + if (res) goto cleanup; + } + res = 0; cleanup: diff --git a/src/test/test_mont.c b/src/test/test_mont.c index 29f0d755..ce54d40e 100644 --- a/src/test/test_mont.c +++ b/src/test/test_mont.c @@ -87,8 +87,9 @@ void test_mont_new_from_bytes(void) { int res; MontContext *ctx; - uint8_t modulus[16] = { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; - uint8_t number[] = { 2, 2 }; + uint8_t modulus[16] = { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; // 0x01000001000000000000000000000001 + uint8_t number[] = { 2, 2 }; // 0x0202 + uint8_t number2[16] = { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3 }; // 0x01000001000000000000000000000001 + 0x0202 uint64_t *output; res = mont_context_init(&ctx, modulus, 16); @@ -113,6 +114,22 @@ void test_mont_new_from_bytes(void) assert(output[1] == 71492449356218367L); free(output); + res = mont_new_from_bytes(&output, number, sizeof(number), ctx); + assert(res == 0); + assert(output != NULL); + assert(output[0] == 18446744073709420033UL); + assert(output[1] == 71492449356218367L); + free(output); + + /** Same as above, but larger than modulus **/ + res = mont_new_from_bytes(&output, number2, sizeof(number2), ctx); + assert(res == 0); + assert(output != NULL); + assert(output[0] == 18446744073709420033UL); + assert(output[1] == 71492449356218367L); + free(output); + + /** 0 is still 0 in Montgomery form **/ number[0] = 0; number[1] = 0; res = mont_new_from_bytes(&output, number, 2, ctx); From a9f2ae6238a178be1e47a3902a2b4e3fa1937777 Mon Sep 17 00:00:00 2001 From: Helder Eijs Date: Sun, 8 Sep 2024 09:36:22 +0200 Subject: [PATCH 09/18] Check weak points on Curve448 --- lib/Crypto/PublicKey/ECC.py | 40 ++----------- lib/Crypto/PublicKey/_curve.py | 4 +- lib/Crypto/PublicKey/_montgomery.py | 57 ++++++++++++++++++- .../SelfTest/PublicKey/test_ECC_Curve448.py | 12 +++- 4 files changed, 73 insertions(+), 40 deletions(-) diff --git a/lib/Crypto/PublicKey/ECC.py b/lib/Crypto/PublicKey/ECC.py index 9db8cd38..7a32adff 100644 --- a/lib/Crypto/PublicKey/ECC.py +++ b/lib/Crypto/PublicKey/ECC.py @@ -607,11 +607,11 @@ def generate(**kwargs): elif _curves[curve_name].id == _CurveID.CURVE25519: seed = randfunc(32) new_key = EccKey(curve=curve_name, seed=seed) - _validate_x25519_public_key(new_key) + _curves[curve_name].validate(new_key.pointQ) elif _curves[curve_name].id == _CurveID.CURVE448: seed = randfunc(56) new_key = EccKey(curve=curve_name, seed=seed) - _validate_x448_public_key(new_key) + _curves[curve_name].validate(new_key.pointQ) else: d = Integer.random_range(min_inclusive=1, max_exclusive=curve.order, @@ -667,14 +667,14 @@ def construct(**kwargs): if point_x is not None: kwargs["point"] = EccXPoint(point_x, curve_name) new_key = EccKey(**kwargs) - _validate_x25519_public_key(new_key) + curve.validate(new_key.pointQ) elif curve.id == _CurveID.CURVE448: if point_x is not None: kwargs["point"] = EccXPoint(point_x, curve_name) new_key = EccKey(**kwargs) - _validate_x448_public_key(new_key) + curve.validate(new_key.pointQ) else: @@ -1152,38 +1152,6 @@ def _import_curve448_public_key(encoded): return point_x -def _validate_x25519_public_key(new_key): - - p = _curves['curve25519'].p - p2 = p * 2 - x1 = 325606250916557431795983626356110631294008115727848805560023387167927233504 - x2 = 39382357235489614581723060781553021112529911719440698176882885853963445705823 - - # http://cr.yp.to/ecdh.html#validate - deny_list = ( - 0, - 1, - x1, - x2, - p - 1, - p, - p + 1, - p + x1, - p + x2, - p2 - 1, - p2, - p2 + 1, - ) - - if new_key.pointQ.x in deny_list: - raise ValueError("Invalid Curve25519 public key") - - -def _validate_x448_public_key(new_key): - #: TODO - pass - - def _import_ed448_public_key(encoded): """Import an Ed448 ECC public key, encoded as raw bytes as described in RFC8032_. diff --git a/lib/Crypto/PublicKey/_curve.py b/lib/Crypto/PublicKey/_curve.py index 06749622..0027f611 100644 --- a/lib/Crypto/PublicKey/_curve.py +++ b/lib/Crypto/PublicKey/_curve.py @@ -16,11 +16,12 @@ # - canonical the canonical name of the curve # - openssh the ASCII string used in OpenSSH id files for public keys on this curve # - rawlib the reference to the dynamic libary with the low-level functions +# - validate a function that raises an exception if the the input point is invalid class _Curve(object): def __init__(self, p, b, order, Gx, Gy, G, modulus_bits, oid, context, - canonical, openssh, rawlib): + canonical, openssh, rawlib, validate=None): self.p = p self.b = b self.order = order @@ -33,3 +34,4 @@ def __init__(self, p, b, order, Gx, Gy, G, modulus_bits, oid, context, self.canonical = canonical self.openssh = openssh self.rawlib = rawlib + self.validate = validate diff --git a/lib/Crypto/PublicKey/_montgomery.py b/lib/Crypto/PublicKey/_montgomery.py index daae6aa4..bacc4ba4 100644 --- a/lib/Crypto/PublicKey/_montgomery.py +++ b/lib/Crypto/PublicKey/_montgomery.py @@ -33,6 +33,36 @@ class EcLib(object): scalar = _curve25519_lib.curve25519_scalar cmp = _curve25519_lib.curve25519_cmp + def _validate_x25519_point(point): + + p2 = p * 2 + x1 = 325606250916557431795983626356110631294008115727848805560023387167927233504 + x2 = 39382357235489614581723060781553021112529911719440698176882885853963445705823 + + # http://cr.yp.to/ecdh.html#validate + deny_list = ( + 0, + 1, + x1, + x2, + p - 1, + p, + p + 1, + p + x1, + p + x2, + p2 - 1, + p2, + p2 + 1, + ) + + try: + valid = point.x not in deny_list + except ValueError: + valid = False + + if not valid: + raise ValueError("Invalid Curve25519 public key") + curve25519 = _Curve(Integer(p), None, Integer(order), @@ -44,7 +74,10 @@ class EcLib(object): None, "Curve25519", None, - EcLib) + EcLib, + _validate_x25519_point, + ) + return curve25519 @@ -84,6 +117,23 @@ class EcLib(object): if result: raise ImportError("Error %d initializing Curve448 context" % result) + def _validate_x448_point(point): + deny_list = ( + 0, + 1, + p - 1, + p, + p + 1, + ) + + try: + valid = point.x not in deny_list + except ValueError: + valid = False + + if not valid: + raise ValueError("Invalid Curve448 public key") + curve448 = _Curve(Integer(p), None, Integer(order), @@ -95,5 +145,8 @@ class EcLib(object): SmartPointer(curve448_context.get(), EcLib.free_context), "Curve448", None, - EcLib) + EcLib, + _validate_x448_point, + ) + return curve448 diff --git a/lib/Crypto/SelfTest/PublicKey/test_ECC_Curve448.py b/lib/Crypto/SelfTest/PublicKey/test_ECC_Curve448.py index 85c65ea4..91da4501 100644 --- a/lib/Crypto/SelfTest/PublicKey/test_ECC_Curve448.py +++ b/lib/Crypto/SelfTest/PublicKey/test_ECC_Curve448.py @@ -219,7 +219,17 @@ def test_negative_construct(self): seed=b'H'*55) # Verify you cannot construct weak keys (small-order points) - # TODO + self.assertRaises(ValueError, ECC.construct, curve="Curve448", + point_x=0) + self.assertRaises(ValueError, ECC.construct, curve="Curve448", + point_x=1) + p = 2**448 - 2**224 - 1 + self.assertRaises(ValueError, ECC.construct, curve="Curve448", + point_x=p-1) + self.assertRaises(ValueError, ECC.construct, curve="Curve448", + point_x=p) + self.assertRaises(ValueError, ECC.construct, curve="Curve448", + point_x=p+1) def get_tests(config={}): From ee2cd3a2fe43ed035858c1680169fcf25710f1de Mon Sep 17 00:00:00 2001 From: Helder Eijs Date: Sun, 8 Sep 2024 09:46:25 +0200 Subject: [PATCH 10/18] Add test vectors for Curve448 --- .../PublicKey/ECC/ecc_ed448_public.pem | 4 -- .../PublicKey/ECC/ecc_x25519.txt | 19 ++++++---- .../PublicKey/ECC/ecc_x25519_private.der | Bin 48 -> 72 bytes .../PublicKey/ECC/ecc_x448.txt | 15 ++++++++ .../PublicKey/ECC/ecc_x448_private.der | Bin 0 -> 72 bytes .../PublicKey/ECC/ecc_x448_private.pem | 4 ++ .../ECC/ecc_x448_private_enc_aes128.pem | 6 +++ .../ECC/ecc_x448_private_enc_aes192.pem | 6 +++ .../ECC/ecc_x448_private_enc_aes256.pem | 6 +++ .../ECC/ecc_x448_private_enc_des3.pem | 6 +++ .../PublicKey/ECC/ecc_x448_private_p8.der | Bin 0 -> 182 bytes .../PublicKey/ECC/ecc_x448_private_p8.pem | 6 +++ .../PublicKey/ECC/ecc_x448_private_p8_2.der | Bin 0 -> 182 bytes .../PublicKey/ECC/ecc_x448_public.der | Bin 0 -> 68 bytes .../PublicKey/ECC/ecc_x448_public.pem | 4 ++ .../PublicKey/ECC/ecc_x448_x509.der | Bin 0 -> 334 bytes .../PublicKey/ECC/ecc_x448_x509.pem | 9 +++++ .../PublicKey/ECC/gen_x448.sh | 35 ++++++++++++++++++ .../PublicKey/ECC/openssl_version_x448.txt | 1 + .../pycryptodome_test_vectors/__init__.py | 2 +- 20 files changed, 110 insertions(+), 13 deletions(-) delete mode 100644 test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_ed448_public.pem create mode 100644 test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448.txt create mode 100644 test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private.der create mode 100644 test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private.pem create mode 100644 test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_enc_aes128.pem create mode 100644 test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_enc_aes192.pem create mode 100644 test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_enc_aes256.pem create mode 100644 test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_enc_des3.pem create mode 100644 test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_p8.der create mode 100644 test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_p8.pem create mode 100644 test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_p8_2.der create mode 100644 test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_public.der create mode 100644 test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_public.pem create mode 100644 test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_x509.der create mode 100644 test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_x509.pem create mode 100755 test_vectors/pycryptodome_test_vectors/PublicKey/ECC/gen_x448.sh create mode 100644 test_vectors/pycryptodome_test_vectors/PublicKey/ECC/openssl_version_x448.txt diff --git a/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_ed448_public.pem b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_ed448_public.pem deleted file mode 100644 index e88b4043..00000000 --- a/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_ed448_public.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PUBLIC KEY----- -MEMwBQYDK2VxAzoAiZAU3cCg4SYM/BCFr9+VIBnp/WM3Lj42biba0ysXZiSIQzCh -RhcjfjCB/r2dGhUGnnSZQz0vVd2A ------END PUBLIC KEY----- diff --git a/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x25519.txt b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x25519.txt index 1fb9b4f0..9d76ad08 100644 --- a/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x25519.txt +++ b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x25519.txt @@ -1,12 +1,15 @@ -----BEGIN PRIVATE KEY----- -MC4CAQAwBQYDK2VuBCIEIKidnb+OLl6wrioGG+TGdIE/r0HdYmIxWOj8d7kvuW5f +MEYCAQAwBQYDK2VvBDoEOHxZhQFreFDMrRfsYFwh4JWoFIuB3ImZp2OET4L8E1Cv +ARzv49/Z1j81t2onGC1y6PXlRaL0ynXW -----END PRIVATE KEY----- -X25519 Private-Key: +X448 Private-Key: priv: - a8:9d:9d:bf:8e:2e:5e:b0:ae:2a:06:1b:e4:c6:74: - 81:3f:af:41:dd:62:62:31:58:e8:fc:77:b9:2f:b9: - 6e:5f + 7c:59:85:01:6b:78:50:cc:ad:17:ec:60:5c:21:e0: + 95:a8:14:8b:81:dc:89:99:a7:63:84:4f:82:fc:13: + 50:af:01:1c:ef:e3:df:d9:d6:3f:35:b7:6a:27:18: + 2d:72:e8:f5:e5:45:a2:f4:ca:75:d6 pub: - ff:75:61:ef:60:c9:c8:a7:57:f6:d6:37:2e:c1:41: - 42:c9:be:20:8d:0e:71:91:36:d8:d3:c7:15:df:cf: - 7e:15 + 2c:b5:a2:77:ae:7c:3c:27:6c:c2:9d:6a:b7:d9:08: + 1a:7f:83:24:a5:a6:9f:3d:1a:a9:cb:7d:d8:49:a1: + 2c:0e:e2:01:9b:15:0f:75:4a:67:b3:81:ed:a7:1d: + 6c:37:17:91:12:0d:fa:8c:a4:40:62 diff --git a/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x25519_private.der b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x25519_private.der index 7a28a1175c75f769a2f2ad03a7dec294127de9db..1067334d076e9ea825b6643e56fa7a092420e97c 100644 GIT binary patch literal 72 zcmV-O0Jr}zMgjo>cu>r(7wlkMA>fs$6pMk}iJ7NkginI} e6Hu=K9Pi`b+15Wbw`wOCEpq7fz>`aARD E0ak<(4FCWD diff --git a/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448.txt b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448.txt new file mode 100644 index 00000000..90647e43 --- /dev/null +++ b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448.txt @@ -0,0 +1,15 @@ +-----BEGIN PRIVATE KEY----- +MEYCAQAwBQYDK2VvBDoEOJDBPevSG2MlrgTa4pql0YvTlh5Hzp6Fuavy3i6jzuSD +PRRK5A6X7BoYjM125vp4DrCU45KRCf36 +-----END PRIVATE KEY----- +X448 Private-Key: +priv: + 90:c1:3d:eb:d2:1b:63:25:ae:04:da:e2:9a:a5:d1: + 8b:d3:96:1e:47:ce:9e:85:b9:ab:f2:de:2e:a3:ce: + e4:83:3d:14:4a:e4:0e:97:ec:1a:18:8c:cd:76:e6: + fa:78:0e:b0:94:e3:92:91:09:fd:fa +pub: + e2:ab:ae:24:ab:8f:65:b0:19:69:e6:1f:84:fe:e6: + 15:b5:25:f4:13:a9:0e:3d:72:7f:71:d0:ff:e6:0f: + b1:d0:a1:a0:28:5f:2a:7f:d8:87:89:20:6e:0a:a4: + f3:e9:fc:b9:e4:ba:5d:64:4e:69:1e diff --git a/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private.der b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private.der new file mode 100644 index 0000000000000000000000000000000000000000..13656a3d171569a31bd730ffe60d4a06d612b807 GIT binary patch literal 72 zcmV-O0Jr}zMgjo0^ e&g6qV6iVa{m+TrCjLmlD`gjhol;e_-3H|y9S|Q>9 literal 0 HcmV?d00001 diff --git a/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private.pem b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private.pem new file mode 100644 index 00000000..0f996a7b --- /dev/null +++ b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private.pem @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MEYCAQAwBQYDK2VvBDoEOJDBPevSG2MlrgTa4pql0YvTlh5Hzp6Fuavy3i6jzuSD +PRRK5A6X7BoYjM125vp4DrCU45KRCf36 +-----END PRIVATE KEY----- diff --git a/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_enc_aes128.pem b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_enc_aes128.pem new file mode 100644 index 00000000..1ba16b43 --- /dev/null +++ b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_enc_aes128.pem @@ -0,0 +1,6 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIGzMF8GCSqGSIb3DQEFDTBSMDEGCSqGSIb3DQEFDDAkBBBQ7Mf5XTNvz29/8327 +CPJnAgIIADAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBAgQQcW/DczxeFx/Ce5jT +d9hwrQRQIGQh8W2qjJVKZImjlAgzcJlgy1jeaf3daRiOvIKjAI3JKmGr488V1ZAh +pMKQDETpsRcYXpVxyUiWOinRAQ7Re+u/nl7f7MOgAVi/HR3Ck4A= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_enc_aes192.pem b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_enc_aes192.pem new file mode 100644 index 00000000..1bd24efb --- /dev/null +++ b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_enc_aes192.pem @@ -0,0 +1,6 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIGzMF8GCSqGSIb3DQEFDTBSMDEGCSqGSIb3DQEFDDAkBBD/cVlQHKbtFhaFKrX2 +j7oAAgIIADAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBFgQQIq13svdowTVAzngt +pN8G4wRQIFLu09Oy3JYLarIr3ni4WzdJ6S1rYafc/F+pP2jjO+IJQPJtdwZCr9Gm +S6Y5sNmJW5JhTe1DezZfVfbEeSTaLkp2r6qMtbJpNgXx3WvXgjU= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_enc_aes256.pem b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_enc_aes256.pem new file mode 100644 index 00000000..88689f87 --- /dev/null +++ b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_enc_aes256.pem @@ -0,0 +1,6 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIGzMF8GCSqGSIb3DQEFDTBSMDEGCSqGSIb3DQEFDDAkBBAZApDfg8ejc8MGKUki +BzmSAgIIADAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBKgQQQayW1hBosjvzPXpp +gMh7JARQsNqbhRpzcDVlkNadsQeyKXCrmv+G7nijW2AThhtS6qBXMz7T8mY2hdAg +afetkM4b5t77B7NClpYlo74y0YMGAQSKBxZ9X+A6LHL+f0UmYS4= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_enc_des3.pem b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_enc_des3.pem new file mode 100644 index 00000000..8645076e --- /dev/null +++ b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_enc_des3.pem @@ -0,0 +1,6 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIGqMFYGCSqGSIb3DQEFDTBJMDEGCSqGSIb3DQEFDDAkBBCdsQ4bhTililZtF+Jg +gh5oAgIIADAMBggqhkiG9w0CCQUAMBQGCCqGSIb3DQMHBAiHdfp7KsDjKQRQ396N +vyE7Y1Miii3u5lNoAkpuxj6+s/uPkuCQ3lAZdRCq+wtXlfYmDJh//kMh+iuoyh/a +u2tsVLzHEx0RXS2LIhK1+78CnqNeOB8CBioIQKc= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_p8.der b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_private_p8.der new file mode 100644 index 0000000000000000000000000000000000000000..cac4780450ab8af43ea3c2f76062033643f96adf GIT binary patch literal 182 zcmXqL+-wlf#;Mij(e|B}k(JjV$iNW6WaVSygN)x91I3L zY#b0hOq{F?2C{6N32h#Xsmv^lS}X$ZetoZaa{6P@-kj~3=iJhKSOR8P%rnX9_m$=B zUl+N&B4D>&W<*&IYodeLkq-uZ-8$EfE$}mzF0rXdRq#AJ;~3M90;eE8NngIJF=O3inMo;g^W(kOJR#j0iuexHC z8QRh2pJkv@#9wU_#@^|^X>l?8q4!*KzcTju-&^qRnriOKjYY}@6PK-!m0-&#H%j;~ aHK)9+@>|5IsDuA~4sE|;ahXr|-7WxhzeJ$` literal 0 HcmV?d00001 diff --git a/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_public.der b/test_vectors/pycryptodome_test_vectors/PublicKey/ECC/ecc_x448_public.der new file mode 100644 index 0000000000000000000000000000000000000000..77cda5cd68d7be6e7478c221ca85e7c4a8bc97b0 GIT binary patch literal 68 zcmV-K0K5M%LNEme11n{3133WVtF9!gk7ckKY33h<{^k|6CG-=i4n1;zanS$f53$gp apeSD|f7pkKAZ`kz^XdG#z^Os+|7{4J>cdG#x8;4e#$2nUTW+nq( zLv903Hs(+kW*#PIM*}%=ULz9&O9Kl7a}y&2^C%G4&;ZUg5H#dB;Dsn>bI!?3%_}i* zGGJw6)=tf5wq$s;dY#JZ{?rYUna|`~{yh`js`^EEC7*3kec^@w&-gc9ShzqVUaS5_ zd#6Gk*OJdK|LlCSD>lV1Q*N=7frEi8&{$bM7BLo)>Bq0PC|k8JZqwd#B|W~ZRc)(}XPGny(doUQdGATIR*r@3|Cw=9_ zcLED$9@_TR Date: Sun, 8 Sep 2024 09:53:31 +0200 Subject: [PATCH 11/18] Add tests for Curve448 import --- lib/Crypto/SelfTest/PublicKey/__init__.py | 4 +- .../PublicKey/test_import_Curve448.py | 351 ++++++++++++++++++ 2 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 lib/Crypto/SelfTest/PublicKey/test_import_Curve448.py diff --git a/lib/Crypto/SelfTest/PublicKey/__init__.py b/lib/Crypto/SelfTest/PublicKey/__init__.py index fa034158..48a445ea 100644 --- a/lib/Crypto/SelfTest/PublicKey/__init__.py +++ b/lib/Crypto/SelfTest/PublicKey/__init__.py @@ -33,7 +33,8 @@ test_ECC_Curve448, test_import_DSA, test_import_RSA, test_import_ECC, test_ElGamal, - test_import_Curve25519) + test_import_Curve25519, + test_import_Curve448) def get_tests(config={}): @@ -50,6 +51,7 @@ def get_tests(config={}): tests += test_import_RSA.get_tests(config=config) tests += test_import_ECC.get_tests(config=config) tests += test_import_Curve25519.get_tests(config=config) + tests += test_import_Curve448.get_tests(config=config) tests += test_ElGamal.get_tests(config=config) return tests diff --git a/lib/Crypto/SelfTest/PublicKey/test_import_Curve448.py b/lib/Crypto/SelfTest/PublicKey/test_import_Curve448.py new file mode 100644 index 00000000..543b19d7 --- /dev/null +++ b/lib/Crypto/SelfTest/PublicKey/test_import_Curve448.py @@ -0,0 +1,351 @@ +# This file is licensed under the BSD 2-Clause License. +# See https://opensource.org/licenses/BSD-2-Clause for details. + +import os +import errno +import warnings +import unittest +from binascii import unhexlify +from unittest import SkipTest + +from Crypto.SelfTest.st_common import list_test_cases +from Crypto.Util.py3compat import tostr, FileNotFoundError +from Crypto.Util.asn1 import DerSequence, DerBitString +from Crypto.Hash import SHAKE128 + +from Crypto.PublicKey import ECC + +try: + import pycryptodome_test_vectors # type: ignore + test_vectors_available = True +except ImportError: + test_vectors_available = False + + +def load_file(file_name, mode="rb"): + results = None + + try: + if not test_vectors_available: + raise FileNotFoundError(errno.ENOENT, + os.strerror(errno.ENOENT), + file_name) + + dir_comps = ("PublicKey", "ECC") + init_dir = os.path.dirname(pycryptodome_test_vectors.__file__) + full_file_name = os.path.join(os.path.join(init_dir, *dir_comps), file_name) + with open(full_file_name, mode) as file_in: + results = file_in.read() + + except FileNotFoundError: + warnings.warn("Warning: skipping extended tests for ECC", + UserWarning, + stacklevel=2) + + if results is None: + raise SkipTest("Missing %s" % file_name) + + return results + + +def compact(lines): + ext = b"".join(lines) + return unhexlify(tostr(ext).replace(" ", "").replace(":", "")) + + +def create_ref_keys_x448(): + key_lines = load_file("ecc_x448.txt").splitlines() + seed = compact(key_lines[5:8]) + key = ECC.construct(curve="Curve448", seed=seed) + return (key, key.public_key()) + + +def get_fixed_prng(): + return SHAKE128.new().update(b"SEED").read + + +def extract_bitstring_from_spki(data): + seq = DerSequence() + seq.decode(data) + bs = DerBitString() + bs.decode(seq[1]) + return bs.value + + +class TestImport(unittest.TestCase): + + def test_empty(self): + self.assertRaises(ValueError, ECC.import_key, b"") + + def test_mismatch(self): + # Private key with X448 Object ID but X448 key + mismatch_hex = "302e020100300506032b656f042204207009906b64ec727d5cb5c23007bf0425b3fd79014c6cd62ca3dddfcf0f278f79" + mismatch = unhexlify(mismatch_hex) + self.assertRaises(ValueError, ECC.import_key, mismatch) + + +class TestImport_Curve448(unittest.TestCase): + + def __init__(self, *args, **kwargs): + super(TestImport_Curve448, self).__init__(*args, **kwargs) + self.ref_private, self.ref_public = create_ref_keys_x448() + + def test_import_public_der(self): + key_file = load_file("ecc_x448_public.der") + + key = ECC._import_subjectPublicKeyInfo(key_file) + self.assertEqual(self.ref_public, key) + + key = ECC._import_der(key_file, None) + self.assertEqual(self.ref_public, key) + + key = ECC.import_key(key_file) + self.assertEqual(self.ref_public, key) + + def test_import_pkcs8_der(self): + key_file = load_file("ecc_x448_private.der") + + key = ECC._import_der(key_file, None) + self.assertEqual(self.ref_private, key) + + key = ECC.import_key(key_file) + self.assertEqual(self.ref_private, key) + + def test_import_private_pkcs8_encrypted_1(self): + key_file = load_file("ecc_x448_private_p8.der") + + key = ECC._import_der(key_file, "secret") + self.assertEqual(self.ref_private, key) + + key = ECC.import_key(key_file, "secret") + self.assertEqual(self.ref_private, key) + + def test_import_private_pkcs8_encrypted_2(self): + key_file = load_file("ecc_x448_private_p8.pem") + + key = ECC.import_key(key_file, "secret") + self.assertEqual(self.ref_private, key) + + def test_import_private_pkcs8_encrypted_3(self): + key_file = load_file("ecc_x448_private_p8_2.der") + + key = ECC._import_der(key_file, "secret") + self.assertEqual(self.ref_private, key) + + key = ECC.import_key(key_file, "secret") + self.assertEqual(self.ref_private, key) + + def test_import_x509_der(self): + key_file = load_file("ecc_x448_x509.der") + + key = ECC._import_der(key_file, None) + self.assertEqual(self.ref_public, key) + + key = ECC.import_key(key_file) + self.assertEqual(self.ref_public, key) + + def test_import_public_pem(self): + key_file = load_file("ecc_x448_public.pem") + + key = ECC.import_key(key_file) + self.assertEqual(self.ref_public, key) + + def test_import_private_pem(self): + key_file = load_file("ecc_x448_private.pem") + + key = ECC.import_key(key_file) + self.assertEqual(self.ref_private, key) + + def test_import_private_pem_encrypted(self): + for algo in "des3", "aes128", "aes192", "aes256": + key_file = load_file("ecc_x448_private_enc_%s.pem" % algo) + + key = ECC.import_key(key_file, "secret") + self.assertEqual(self.ref_private, key) + + key = ECC.import_key(tostr(key_file), b"secret") + self.assertEqual(self.ref_private, key) + + def test_import_x509_pem(self): + key_file = load_file("ecc_x448_x509.pem") + + key = ECC.import_key(key_file) + self.assertEqual(self.ref_public, key) + + +class TestExport_Curve448(unittest.TestCase): + + def __init__(self, *args, **kwargs): + super(TestExport_Curve448, self).__init__(*args, **kwargs) + self.ref_private, self.ref_public = create_ref_keys_x448() + + def test_export_public_der(self): + key_file = load_file("ecc_x448_public.der") + + encoded = self.ref_public._export_subjectPublicKeyInfo(True) + self.assertEqual(key_file, encoded) + + encoded = self.ref_public.export_key(format="DER") + self.assertEqual(key_file, encoded) + + encoded = self.ref_public.export_key(format="DER", compress=False) + self.assertEqual(key_file, encoded) + + def test_export_private_pkcs8_clear(self): + key_file = load_file("ecc_x448_private.der") + + encoded = self.ref_private._export_pkcs8() + self.assertEqual(key_file, encoded) + + # --- + + encoded = self.ref_private.export_key(format="DER") + self.assertEqual(key_file, encoded) + + self.assertRaises(ValueError, self.ref_private.export_key, + format="DER", use_pkcs8=False) + + def test_export_private_pkcs8_encrypted(self): + encoded = self.ref_private._export_pkcs8(passphrase="secret", + protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") + + # This should prove that the output is password-protected + self.assertRaises(ValueError, ECC._import_pkcs8, encoded, None) + + decoded = ECC._import_pkcs8(encoded, "secret") + self.assertEqual(self.ref_private, decoded) + + # --- + + encoded = self.ref_private.export_key(format="DER", + passphrase="secret", + protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") + decoded = ECC.import_key(encoded, "secret") + self.assertEqual(self.ref_private, decoded) + + # --- + + encoded = self.ref_private.export_key(format="DER", + passphrase="secret", + protection="PBKDF2WithHMAC-SHA256AndAES128-CBC", + prot_params={'iteration_count': 123}) + decoded = ECC.import_key(encoded, "secret") + self.assertEqual(self.ref_private, decoded) + + def test_export_public_pem(self): + key_file_ref = load_file("ecc_x448_public.pem", "rt").strip() + key_file = self.ref_public.export_key(format="PEM").strip() + self.assertEqual(key_file_ref, key_file) + + def test_export_private_pem_clear(self): + key_file = load_file("ecc_x448_private.pem", "rt").strip() + encoded = self.ref_private.export_key(format="PEM").strip() + self.assertEqual(key_file, encoded) + + def test_export_private_pem_encrypted(self): + encoded = self.ref_private.export_key(format="PEM", + passphrase=b"secret", + protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") + + # This should prove that the output is password-protected + self.assertRaises(ValueError, ECC.import_key, encoded) + + assert "ENCRYPTED PRIVATE KEY" in encoded + + decoded = ECC.import_key(encoded, "secret") + self.assertEqual(self.ref_private, decoded) + + def test_export_raw(self): + encoded = self.ref_public.export_key(format='raw') + self.assertEqual(len(encoded), 32) + self.assertEqual(encoded, unhexlify(b'ff7561ef60c9c8a757f6d6372ec14142c9be208d0e719136d8d3c715dfcf7e15')) + + def test_prng(self): + # Test that password-protected containers use the provided PRNG + encoded1 = self.ref_private.export_key(format="PEM", + passphrase="secret", + protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", + randfunc=get_fixed_prng()) + encoded2 = self.ref_private.export_key(format="PEM", + passphrase="secret", + protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", + randfunc=get_fixed_prng()) + self.assertEqual(encoded1, encoded2) + + def test_byte_or_string_passphrase(self): + encoded1 = self.ref_private.export_key(format="PEM", + passphrase="secret", + protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", + randfunc=get_fixed_prng()) + encoded2 = self.ref_private.export_key(format="PEM", + passphrase=b"secret", + protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", + randfunc=get_fixed_prng()) + self.assertEqual(encoded1, encoded2) + + def test_error_params1(self): + # Unknown format + self.assertRaises(ValueError, self.ref_private.export_key, format="XXX") + + # Missing 'protection' parameter when PKCS#8 is used + self.assertRaises(ValueError, + self.ref_private.export_key, + format="PEM", + passphrase="secret") + + # Empty password + self.assertRaises(ValueError, + self.ref_private.export_key, + format="PEM", + passphrase="", + use_pkcs8=False) + self.assertRaises(ValueError, + self.ref_private.export_key, + format="PEM", + passphrase="", + protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") + + # No private keys with OpenSSH + self.assertRaises(ValueError, + self.ref_private.export_key, + format="OpenSSH", + passphrase="secret") + + +class TestImport_Curve448_Weak(unittest.TestCase): + + def test_weak_pem(self): + + p = 2**448 - 2**224 - 1 + weak_x = (0, + 1, + p - 1, + p, + p + 1) + + for x in weak_x: + low_order_point = ECC.EccXPoint(x, "curve448") + weak_key = ECC.EccKey(point=low_order_point, curve="curve448") + encoded = weak_key.export_key(format="PEM") + + self.assertRaises(ValueError, + ECC.import_key, + encoded) + + +def get_tests(config={}): + tests = [] + try: + tests += list_test_cases(TestImport) + tests += list_test_cases(TestImport_Curve448) + tests += list_test_cases(TestExport_Curve448) + tests += list_test_cases(TestImport_Curve448_Weak) + except SkipTest: + pass + return tests + + +if __name__ == '__main__': + def suit(): + return unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') From d5d1607fbd84f89056ceeebc5b796dffb18076cd Mon Sep 17 00:00:00 2001 From: Helder Eijs Date: Sun, 8 Sep 2024 09:55:23 +0200 Subject: [PATCH 12/18] Update Changelog --- Changelog.rst | 1 + Doc/src/features.rst | 2 +- README.rst | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Changelog.rst b/Changelog.rst index e33c52f1..a79f485d 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -9,6 +9,7 @@ Under development * GH#814: RSA keys for PSS can be imported. * GH#810: fixed negation of Ed25519 points * Add support for Curve25519 / X25519 +* Add support for Curve448 / X448 * Add attribute ``curve`` to EccPoint and EccXPoint classes, with the canonical name of the curve. * GH#781: the label for the SP800_108_Counter KDF may now diff --git a/Doc/src/features.rst b/Doc/src/features.rst index 4653dd44..dc2fbee1 100644 --- a/Doc/src/features.rst +++ b/Doc/src/features.rst @@ -65,7 +65,7 @@ A list of useful resources in that area can be found on `Matthew Green's blog`_. * Asymmetric key generation: - RSA - - ECC (NIST P-curves; Ed25519, Ed448) + - ECC (NIST P-curves; Ed25519, Ed448, Curve25519, Curve448) - DSA - ElGamal (legacy) diff --git a/README.rst b/README.rst index 5749c435..2eb2fafe 100644 --- a/README.rst +++ b/README.rst @@ -47,7 +47,7 @@ with respect to the last official version of PyCrypto (2.6.1): * Authenticated encryption modes (GCM, CCM, EAX, SIV, OCB) * Accelerated AES on Intel platforms via AES-NI * First class support for PyPy -* Elliptic curves cryptography (NIST P-curves; Ed25519, Ed448, Curve25519) +* Elliptic curves cryptography (NIST P-curves; Ed25519, Ed448, Curve25519, Curve448) * Better and more compact API (`nonce` and `iv` attributes for ciphers, automatic generation of random nonces and IVs, simplified CTR cipher mode, and more) From 8e7afd8e787bfb8b0f1e072e45844bda7d1b6d2c Mon Sep 17 00:00:00 2001 From: Helder Eijs Date: Sun, 8 Sep 2024 14:00:42 +0200 Subject: [PATCH 13/18] Add tests for import of X448 keys --- lib/Crypto/Protocol/DH.py | 46 +++++++++- lib/Crypto/SelfTest/Protocol/test_ecdh.py | 100 ++++++++++++++++++++-- 2 files changed, 138 insertions(+), 8 deletions(-) diff --git a/lib/Crypto/Protocol/DH.py b/lib/Crypto/Protocol/DH.py index 8cbe898d..f70db9d9 100644 --- a/lib/Crypto/Protocol/DH.py +++ b/lib/Crypto/Protocol/DH.py @@ -1,7 +1,8 @@ from Crypto.Util.number import long_to_bytes from Crypto.PublicKey.ECC import (EccKey, construct, - _import_curve25519_public_key) + _import_curve25519_public_key, + _import_curve448_public_key) def _compute_ecdh(key_priv, key_pub): @@ -11,6 +12,8 @@ def _compute_ecdh(key_priv, key_pub): if key_priv.curve == "Curve25519": z = bytearray(pointP.x.to_bytes(32, byteorder='little')) + elif key_priv.curve == "Curve448": + z = bytearray(pointP.x.to_bytes(56, byteorder='little')) else: # See Section 5.7.1.2 in NIST SP 800-56Ar3 z = long_to_bytes(pointP.x, pointP.size_in_bytes()) @@ -58,6 +61,47 @@ def import_x25519_private_key(encoded): return construct(seed=encoded, curve="Curve25519") +def import_x448_public_key(encoded): + """Create a new X448 public key object, + starting from the key encoded as raw ``bytes``, + in the format described in RFC7748. + + Args: + encoded (bytes): + The x448 public key to import. + It must be 56 bytes. + + Returns: + :class:`Crypto.PublicKey.EccKey` : a new ECC key object. + + Raises: + ValueError: when the given key cannot be parsed. + """ + + x = _import_curve448_public_key(encoded) + return construct(curve='Curve448', point_x=x) + + +def import_x448_private_key(encoded): + """Create a new X448 private key object, + starting from the key encoded as raw ``bytes``, + in the format described in RFC7748. + + Args: + encoded (bytes): + The X448 private key to import. + It must be 56 bytes. + + Returns: + :class:`Crypto.PublicKey.EccKey` : a new ECC key object. + + Raises: + ValueError: when the given key cannot be parsed. + """ + + return construct(seed=encoded, curve="Curve448") + + def key_agreement(**kwargs): """Perform a Diffie-Hellman key agreement. diff --git a/lib/Crypto/SelfTest/Protocol/test_ecdh.py b/lib/Crypto/SelfTest/Protocol/test_ecdh.py index d7e9e5a9..0128168d 100644 --- a/lib/Crypto/SelfTest/Protocol/test_ecdh.py +++ b/lib/Crypto/SelfTest/Protocol/test_ecdh.py @@ -358,11 +358,11 @@ def test_weak(self): # it will set the MSB to zero (as required by RFC7748, Section 5), # therefore leading to another public key (and to a point which is # not of low order anymore). - #"cdeb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b880", - #"4c9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f11d7", - #"d9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - #"daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - #"dbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + # "cdeb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b880", + # "4c9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f11d7", + # "d9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + # "daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + # "dbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", ) for x in weak_keys: @@ -370,6 +370,91 @@ def test_weak(self): DH.import_x25519_public_key, unhexlify(x)) + +class X448_Tests(unittest.TestCase): + + def test_rfc7748_1(self): + tvs = ( + ("3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3", + "06fce640fa3487bfda5f6cf2d5263f8aad88334cbd07437f020f08f9814dc031ddbdc38c19c6da2583fa5429db94ada18aa7a7fb4ef8a086", + "ce3e4ff95a60dc6697da1db1d85e6afbdf79b50a2412d7546d5f239fe14fbaadeb445fc66a01b0779d98223961111e21766282f73dd96b6f"), + ("203d494428b8399352665ddca42f9de8fef600908e0d461cb021f8c538345dd77c3e4806e25f46d3315c44e0a5b4371282dd2c8d5be3095f", + "0fbcc2f993cd56d3305b0b7d9e55d4c1a8fb5dbb52f8e9a1e9b6201b165d015894e56c4d3570bee52fe205e28a78b91cdfbde71ce8d157db", + "884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d"), + ) + + for tv1, tv2, tv3 in tvs: + priv_key = DH.import_x448_private_key(unhexlify(tv1)) + pub_key = DH.import_x448_public_key(unhexlify(tv2)) + result = key_agreement(static_pub=pub_key, + static_priv=priv_key, + kdf=lambda x: x) + self.assertEqual(result, unhexlify(tv3)) + + def test_rfc7748_2(self): + k = unhexlify("0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") + + priv_key = DH.import_x448_private_key(k) + pub_key = DH.import_x448_public_key(k) + result = key_agreement(static_pub=pub_key, + static_priv=priv_key, + kdf=lambda x: x) + self.assertEqual( + result, + unhexlify("3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113") + ) + + for _ in range(999): + priv_key = DH.import_x448_private_key(result) + pub_key = DH.import_x448_public_key(k) + k = result + result = key_agreement(static_pub=pub_key, + static_priv=priv_key, + kdf=lambda x: x) + + self.assertEqual( + result, + unhexlify("aa3b4749d55b9daf1e5b00288826c467274ce3ebbdd5c17b975e09d4af6c67cf10d087202db88286e2b79fceea3ec353ef54faa26e219f38") + ) + + def test_rfc7748_3(self): + tv1 = "9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b" + tv2 = "9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0" + tv3 = "1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d" + tv4 = "3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609" + tv5 = "07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d" + + alice_priv_key = DH.import_x448_private_key(unhexlify(tv1)) + alice_pub_key = DH.import_x448_public_key(unhexlify(tv2)) + bob_priv_key = DH.import_x448_private_key(unhexlify(tv3)) + bob_pub_key = DH.import_x448_public_key(unhexlify(tv4)) + secret = unhexlify(tv5) + + result1 = key_agreement(static_pub=alice_pub_key, + static_priv=bob_priv_key, + kdf=lambda x: x) + result2 = key_agreement(static_pub=bob_pub_key, + static_priv=alice_priv_key, + kdf=lambda x: x) + self.assertEqual(result1, secret) + self.assertEqual(result2, secret) + + def test_weak(self): + + weak_keys = ( + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "fefffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "00000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ) + + for x in weak_keys: + self.assertRaises(ValueError, + DH.import_x448_public_key, + unhexlify(x)) + + class TestVectorsXECDHWycheproof(unittest.TestCase): desc = "Wycheproof XECDH tests" @@ -444,7 +529,7 @@ def test_verify(self, tv): assert not tv.valid assert "Unsupported ECC" in str(e) return - except ValueError as e: + except ValueError: assert tv.valid assert tv.warning assert "LowOrderPublic" in tv.flags @@ -487,7 +572,7 @@ def base64url_decode(input_str): else: assert "Incorrect length" in str(e) return - except ValueError as e: + except ValueError: assert tv.valid else: raise ValueError("Unknown encoding", tv.encoding) @@ -518,6 +603,7 @@ def get_tests(config={}): tests += [TestVectorsECDHWycheproof()] tests += list_test_cases(ECDH_Tests) tests += list_test_cases(X25519_Tests) + tests += list_test_cases(X448_Tests) tests += [TestVectorsXECDHWycheproof()] slow_tests = config.get('slow_tests') From 264e4f886640119ba25a420bfb802afd9144ca3f Mon Sep 17 00:00:00 2001 From: Helder Eijs Date: Sun, 8 Sep 2024 14:01:29 +0200 Subject: [PATCH 14/18] Update typing for ECDH --- lib/Crypto/Protocol/DH.pyi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Crypto/Protocol/DH.pyi b/lib/Crypto/Protocol/DH.pyi index 65a9a5da..634a0072 100644 --- a/lib/Crypto/Protocol/DH.pyi +++ b/lib/Crypto/Protocol/DH.pyi @@ -14,4 +14,6 @@ class RequestParams(TypedDict, Generic[T]): def import_x25519_public_key(encoded: bytes) -> EccKey: ... def import_x25519_private_key(encoded: bytes) -> EccKey: ... +def import_x448_public_key(encoded: bytes) -> EccKey: ... +def import_x448_private_key(encoded: bytes) -> EccKey: ... def key_agreement(**kwargs: Unpack[RequestParams[T]]) -> T: ... From d60d018f93dff685fd501ab496bdee314cc4470d Mon Sep 17 00:00:00 2001 From: Helder Eijs Date: Sun, 8 Sep 2024 14:06:19 +0200 Subject: [PATCH 15/18] Fix docs --- Doc/src/public_key/ecc.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/src/public_key/ecc.rst b/Doc/src/public_key/ecc.rst index fe3c34b4..eefd184f 100644 --- a/Doc/src/public_key/ecc.rst +++ b/Doc/src/public_key/ecc.rst @@ -54,12 +54,13 @@ You can also export the public key, which is not sensitive:: "Ed25519", "``'Ed25519'``", "``'ed25519'``" "Ed448", "``'Ed448'``", "``'ed448'``" "Curve25519", "``'Curve25519'``", "``'curve25519'``" + "Curve448", "``'Curve448'``", "``'curve448'``" For more information about each NIST curve see `FIPS 186-4`_, Section D.1.2. -The Ed25519 and the Ed448 curves are defined in RFC8032_. +Curves Ed25519 and Ed448 are defined in RFC8032_. -The Curve25519 curve is defined in RFC7748_. +Curves Curve25519 and Curve448 are defined in RFC7748_. The ECC keys can be used to perform or verify signatures, using the modules :mod:`Crypto.Signature.DSS` (ECDSA; NIST curves only) From 6acee5eeb78e5bedee0525ee77c11757fd254e7d Mon Sep 17 00:00:00 2001 From: Helder Eijs Date: Tue, 10 Sep 2024 21:25:18 +0200 Subject: [PATCH 16/18] Fix call to curve25519_new_point() for profiling --- src/curve25519.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/curve25519.c b/src/curve25519.c index e2b31cb7..84cccff4 100644 --- a/src/curve25519.c +++ b/src/curve25519.c @@ -260,7 +260,7 @@ int main(void) secret[i] = pubkey[i] = (uint8_t)((secret[i-1] << 1) | (secret[i-1] >> 7)); } - res = curve25519_new_point(&Pin, pubkey, 32); + res = curve25519_new_point(&Pin, pubkey, 32, NULL); if (res) { printf("Error: %d\n", res); return res; From 12912faca4813c12179756db7f946fdd1fdabe9c Mon Sep 17 00:00:00 2001 From: Helder Eijs Date: Tue, 10 Sep 2024 21:25:49 +0200 Subject: [PATCH 17/18] Fix compiler warning --- src/mont.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mont.c b/src/mont.c index d9ec41f4..42a5ae67 100644 --- a/src/mont.c +++ b/src/mont.c @@ -846,7 +846,7 @@ int mont_new_from_bytes(uint64_t **out, const uint8_t *number, size_t len, const mont_mult_generic(encoded, tmp1, ctx->r2_mod_n, ctx->modulus, ctx->m0, scratchpad, ctx->words); } else { while (ge(tmp1, ctx->modulus, ctx->words)) { - res = sub(tmp1, tmp1, ctx->modulus, ctx->words); + res = (int)sub(tmp1, tmp1, ctx->modulus, ctx->words); if (res) goto cleanup; } res = mont_copy(encoded, tmp1, ctx); From d470020d85ce9a15a07787ef5449df157abd8d0f Mon Sep 17 00:00:00 2001 From: Helder Eijs Date: Tue, 10 Sep 2024 23:09:15 +0200 Subject: [PATCH 18/18] Invert in Curve448 with addition chain --- src/curve448.c | 4 +- src/mont.c | 128 ++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 102 insertions(+), 30 deletions(-) diff --git a/src/curve448.c b/src/curve448.c index d0fb59ac..d80f808a 100644 --- a/src/curve448.c +++ b/src/curve448.c @@ -204,11 +204,11 @@ STATIC int curve448_scalar_internal(Curve448Point *Pout, mont_set(Pout->z, 0, mont_ctx); } else { uint64_t *invz = Pout->wp->a; + uint64_t *scratch = P2->wp->scratch; - /** TODO: replace with add chain **/ res = mont_inv_prime(invz, P2->z, mont_ctx); if (res) goto cleanup; - res = mont_mult(Pout->x, P2->x, invz, P2->wp->scratch, mont_ctx); + res = mont_mult(Pout->x, P2->x, invz, scratch, mont_ctx); if (res) goto cleanup; mont_set(Pout->z, 1, mont_ctx); } diff --git a/src/mont.c b/src/mont.c index 42a5ae67..e408b1e4 100644 --- a/src/mont.c +++ b/src/mont.c @@ -985,38 +985,62 @@ int mont_sub(uint64_t *out, const uint64_t *a, const uint64_t *b, uint64_t *tmp, return sub_mod(out, a, b, ctx->modulus, tmp, tmp + ctx->words, ctx->words); } -/* - * Compute the modular inverse of an integer in Montgomery form. - * - * Condition: the modulus defining the Montgomery context MUST BE a non-secret prime number. - * - * @param out The location where the result will be stored at; it must have - * been allocated with mont_new_number(&p, 1, ctx). - * @param a The number to compute the modular inverse of, already in Montgomery form. - * @param ctx The Montgomery context. - * @return 0 for success, the relevant error code otherwise. - */ -int mont_inv_prime(uint64_t *out, uint64_t *a, const MontContext *ctx) +STATIC void curve448_invert(uint64_t *z, + uint64_t *t0, + uint64_t *t1, + const uint64_t *x, + uint64_t *scratch, + const MontContext *ctx) +{ + #define DOUBLE(out, in) mont_mult(out, in, in, scratch, ctx) + #define SHIFTL(out, in, times) do { unsigned i; mont_mult(out, in, in, scratch, ctx); \ + for (i=0; iwords, sizeof(uint64_t)); - if (NULL == tmp1) - return ERR_MEMORY; - - scratchpad = (uint64_t*)calloc(SCRATCHPAD_NR, ctx->words*sizeof(uint64_t)); - if (NULL == scratchpad) { - res = ERR_MEMORY; - goto cleanup; - } /** Exponent is guaranteed to be >0 **/ exponent = ctx->modulus_min_2; @@ -1049,10 +1073,58 @@ int mont_inv_prime(uint64_t *out, uint64_t *a, const MontContext *ctx) break; bit = (uint64_t)1 << 63; } +} + +/* + * Compute the modular inverse of an integer in Montgomery form. + * + * Condition: the modulus defining the Montgomery context MUST BE a non-secret prime number. + * + * @param out The location where the result will be stored at; it must have + * been allocated with mont_new_number(&p, 1, ctx). + * @param a The number to compute the modular inverse of, already in Montgomery form. + * @param ctx The Montgomery context. + * @return 0 for success, the relevant error code otherwise. + */ +int mont_inv_prime(uint64_t *out, uint64_t *a, const MontContext *ctx) +{ + uint64_t *tmp1 = NULL; + uint64_t *tmp2 = NULL; + uint64_t *scratchpad = NULL; + int res; + + if (NULL == out || NULL == a || NULL == ctx) + return ERR_NULL; + + tmp1 = (uint64_t*)calloc(ctx->words, sizeof(uint64_t)); + if (NULL == tmp1) + return ERR_MEMORY; + + tmp2 = (uint64_t*)calloc(ctx->words, sizeof(uint64_t)); + if (NULL == tmp2) { + res = ERR_MEMORY; + goto cleanup; + } + + scratchpad = (uint64_t*)calloc(SCRATCHPAD_NR, ctx->words*sizeof(uint64_t)); + if (NULL == scratchpad) { + res = ERR_MEMORY; + goto cleanup; + } + + switch (ctx->modulus_type) { + case ModulusEd448: + curve448_invert(out, tmp1, tmp2, a, scratchpad, ctx); + break; + default: + mont_inv_prime_generic(out, tmp1, a, scratchpad, ctx); + break; + } res = 0; cleanup: free(tmp1); + free(tmp2); free(scratchpad); return res; }