Add SPAKE2 over Ed25519. SPAKE2 is a password-authenticated key exchange. This implementation is over the twisted Edwards curve Ed25519, and uses SHA-512 as the hash primitive. See https://tools.ietf.org/html/draft-irtf-cfrg-spake2-03 Change-Id: I2cd3c3ebdc3d55ac3aea3a9eb0d06275509597ac Reviewed-on: https://boringssl-review.googlesource.com/7114 Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/curve25519/CMakeLists.txt b/crypto/curve25519/CMakeLists.txt index a2ef3bb..195b1e6 100644 --- a/crypto/curve25519/CMakeLists.txt +++ b/crypto/curve25519/CMakeLists.txt
@@ -22,6 +22,7 @@ OBJECT curve25519.c + spake25519.c x25519-x86_64.c ${CURVE25519_ARCH_SOURCES} @@ -45,3 +46,12 @@ target_link_libraries(x25519_test crypto) add_dependencies(all_tests x25519_test) + +add_executable( + spake25519_test + + spake25519_test.cc +) + +target_link_libraries(spake25519_test crypto) +add_dependencies(all_tests spake25519_test)
diff --git a/crypto/curve25519/curve25519.c b/crypto/curve25519/curve25519.c index 61bbdce..272dfcf 100644 --- a/crypto/curve25519/curve25519.c +++ b/crypto/curve25519/curve25519.c
@@ -30,13 +30,6 @@ #include "internal.h" - -/* fe means field element. Here the field is \Z/(2^255-19). An element t, - * entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77 - * t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on - * context. */ -typedef int32_t fe[10]; - static uint64_t load_3(const uint8_t *in) { uint64_t result; result = (uint64_t)in[0]; @@ -974,52 +967,7 @@ fe_mul(out, t0, z); } -/* ge means group element. - - * Here the group is the set of pairs (x,y) of field elements (see fe.h) - * satisfying -x^2 + y^2 = 1 + d x^2y^2 - * where d = -121665/121666. - * - * Representations: - * ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z - * ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT - * ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T - * ge_precomp (Duif): (y+x,y-x,2dxy) */ - -typedef struct { - fe X; - fe Y; - fe Z; -} ge_p2; - -typedef struct { - fe X; - fe Y; - fe Z; - fe T; -} ge_p3; - -typedef struct { - fe X; - fe Y; - fe Z; - fe T; -} ge_p1p1; - -typedef struct { - fe yplusx; - fe yminusx; - fe xy2d; -} ge_precomp; - -typedef struct { - fe YplusX; - fe YminusX; - fe Z; - fe T2d; -} ge_cached; - -static void ge_tobytes(uint8_t *s, const ge_p2 *h) { +void x25519_ge_tobytes(uint8_t *s, const ge_p2 *h) { fe recip; fe x; fe y; @@ -1049,7 +997,7 @@ static const fe sqrtm1 = {-32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482}; -static int ge_frombytes_vartime(ge_p3 *h, const uint8_t *s) { +int x25519_ge_frombytes_vartime(ge_p3 *h, const uint8_t *s) { fe u; fe v; fe v3; @@ -1105,6 +1053,13 @@ fe_0(h->T); } +static void ge_cached_0(ge_cached *h) { + fe_1(h->YplusX); + fe_1(h->YminusX); + fe_1(h->Z); + fe_0(h->T2d); +} + static void ge_precomp_0(ge_precomp *h) { fe_1(h->yplusx); fe_1(h->yminusx); @@ -1122,7 +1077,7 @@ 15978800, -12551817, -6495438, 29715968, 9444199}; /* r = p */ -static void ge_p3_to_cached(ge_cached *r, const ge_p3 *p) { +void x25519_ge_p3_to_cached(ge_cached *r, const ge_p3 *p) { fe_add(r->YplusX, p->Y, p->X); fe_sub(r->YminusX, p->Y, p->X); fe_copy(r->Z, p->Z); @@ -1130,20 +1085,27 @@ } /* r = p */ -static void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p) { +void x25519_ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p) { fe_mul(r->X, p->X, p->T); fe_mul(r->Y, p->Y, p->Z); fe_mul(r->Z, p->Z, p->T); } /* r = p */ -static void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p) { +void x25519_ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p) { fe_mul(r->X, p->X, p->T); fe_mul(r->Y, p->Y, p->Z); fe_mul(r->Z, p->Z, p->T); fe_mul(r->T, p->X, p->Y); } +/* r = p */ +static void ge_p1p1_to_cached(ge_cached *r, const ge_p1p1 *p) { + ge_p3 t; + x25519_ge_p1p1_to_p3(&t, p); + x25519_ge_p3_to_cached(r, &t); +} + /* r = 2 * p */ static void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p) { fe t0; @@ -1199,7 +1161,7 @@ } /* r = p + q */ -static void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { +void x25519_ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { fe t0; fe_add(r->X, p->Y, p->X); @@ -1216,7 +1178,7 @@ } /* r = p - q */ -static void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { +void x25519_ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { fe t0; fe_add(r->X, p->Y, p->X); @@ -1248,6 +1210,58 @@ fe_cmov(t->xy2d, u->xy2d, b); } +void x25519_ge_scalarmult_small_precomp( + ge_p3 *h, const uint8_t a[32], const uint8_t precomp_table[15 * 2 * 32]) { + /* precomp_table is first expanded into matching |ge_precomp| + * elements. */ + ge_precomp multiples[15]; + + unsigned i; + for (i = 0; i < 15; i++) { + const uint8_t *bytes = &precomp_table[i*(2 * 32)]; + fe x, y; + fe_frombytes(x, bytes); + fe_frombytes(y, bytes + 32); + + ge_precomp *out = &multiples[i]; + fe_add(out->yplusx, y, x); + fe_sub(out->yminusx, y, x); + fe_mul(out->xy2d, x, y); + fe_mul(out->xy2d, out->xy2d, d2); + } + + /* See the comment above |k25519SmallPrecomp| about the structure of the + * precomputed elements. This loop does 64 additions and 64 doublings to + * calculate the result. */ + ge_p3_0(h); + + for (i = 63; i < 64; i--) { + unsigned j; + signed char index = 0; + + for (j = 0; j < 4; j++) { + const uint8_t bit = 1 & (a[(8 * j) + (i / 8)] >> (i & 7)); + index |= (bit << j); + } + + ge_precomp e; + ge_precomp_0(&e); + + for (j = 1; j < 16; j++) { + cmov(&e, &multiples[j-1], equal(index, j)); + } + + ge_cached cached; + ge_p1p1 r; + x25519_ge_p3_to_cached(&cached, h); + x25519_ge_add(&r, h, &cached); + x25519_ge_p1p1_to_p3(h, &r); + + ge_madd(&r, h, &e); + x25519_ge_p1p1_to_p3(h, &r); + } +} + #if defined(OPENSSL_SMALL) /* This block of code replaces the standard base-point table with a much smaller @@ -1341,55 +1355,8 @@ 0x45, 0xc9, 0x8b, 0x17, 0x79, 0xe7, 0xc7, 0x90, 0x99, 0x3a, 0x18, 0x25, }; -static void ge_scalarmult_base(ge_p3 *h, const uint8_t a[32]) { - /* k25519SmallPrecomp is first expanded into matching |ge_precomp| - * elements. */ - ge_precomp multiples[15]; - - unsigned i; - for (i = 0; i < 15; i++) { - const uint8_t *bytes = &k25519SmallPrecomp[i*(2 * 32)]; - fe x, y; - fe_frombytes(x, bytes); - fe_frombytes(y, bytes + 32); - - ge_precomp *out = &multiples[i]; - fe_add(out->yplusx, y, x); - fe_sub(out->yminusx, y, x); - fe_mul(out->xy2d, x, y); - fe_mul(out->xy2d, out->xy2d, d2); - } - - /* See the comment above |k25519SmallPrecomp| about the structure of the - * precomputed elements. This loop does 64 additions and 64 doublings to - * calculate the result. */ - ge_p3_0(h); - - for (i = 63; i < 64; i--) { - unsigned j; - signed char index = 0; - - for (j = 0; j < 4; j++) { - const uint8_t bit = 1 & (a[(8 * j) + (i / 8)] >> (i & 7)); - index |= (bit << j); - } - - ge_precomp e; - ge_precomp_0(&e); - - for (j = 1; j < 16; j++) { - cmov(&e, &multiples[j-1], equal(index, j)); - } - - ge_cached cached; - ge_p1p1 r; - ge_p3_to_cached(&cached, h); - ge_add(&r, h, &cached); - ge_p1p1_to_p3(h, &r); - - ge_madd(&r, h, &e); - ge_p1p1_to_p3(h, &r); - } +void x25519_ge_scalarmult_base(ge_p3 *h, const uint8_t a[32]) { + x25519_ge_scalarmult_small_precomp(h, a, k25519SmallPrecomp); } #else @@ -3542,7 +3509,7 @@ * * Preconditions: * a[31] <= 127 */ -static void ge_scalarmult_base(ge_p3 *h, const uint8_t *a) { +void x25519_ge_scalarmult_base(ge_p3 *h, const uint8_t *a) { signed char e[64]; signed char carry; ge_p1p1 r; @@ -3571,27 +3538,87 @@ for (i = 1; i < 64; i += 2) { table_select(&t, i / 2, e[i]); ge_madd(&r, h, &t); - ge_p1p1_to_p3(h, &r); + x25519_ge_p1p1_to_p3(h, &r); } ge_p3_dbl(&r, h); - ge_p1p1_to_p2(&s, &r); + x25519_ge_p1p1_to_p2(&s, &r); ge_p2_dbl(&r, &s); - ge_p1p1_to_p2(&s, &r); + x25519_ge_p1p1_to_p2(&s, &r); ge_p2_dbl(&r, &s); - ge_p1p1_to_p2(&s, &r); + x25519_ge_p1p1_to_p2(&s, &r); ge_p2_dbl(&r, &s); - ge_p1p1_to_p3(h, &r); + x25519_ge_p1p1_to_p3(h, &r); for (i = 0; i < 64; i += 2) { table_select(&t, i / 2, e[i]); ge_madd(&r, h, &t); - ge_p1p1_to_p3(h, &r); + x25519_ge_p1p1_to_p3(h, &r); } } #endif +static void cmov_cached(ge_cached *t, ge_cached *u, uint8_t b) { + fe_cmov(t->YplusX, u->YplusX, b); + fe_cmov(t->YminusX, u->YminusX, b); + fe_cmov(t->Z, u->Z, b); + fe_cmov(t->T2d, u->T2d, b); +} + +/* r = scalar * A. + * where a = a[0]+256*a[1]+...+256^31 a[31]. */ +void x25519_ge_scalarmult(ge_p2 *r, const uint8_t *scalar, const ge_p3 *A) { + ge_p2 Ai_p2[8]; + ge_cached Ai[16]; + ge_p1p1 t; + + ge_cached_0(&Ai[0]); + x25519_ge_p3_to_cached(&Ai[1], A); + ge_p3_to_p2(&Ai_p2[1], A); + + unsigned i; + for (i = 2; i < 16; i += 2) { + ge_p2_dbl(&t, &Ai_p2[i / 2]); + ge_p1p1_to_cached(&Ai[i], &t); + if (i < 8) { + x25519_ge_p1p1_to_p2(&Ai_p2[i], &t); + } + x25519_ge_add(&t, A, &Ai[i]); + ge_p1p1_to_cached(&Ai[i + 1], &t); + if (i < 7) { + x25519_ge_p1p1_to_p2(&Ai_p2[i + 1], &t); + } + } + + ge_p2_0(r); + ge_p3 u; + + for (i = 0; i < 256; i += 4) { + ge_p2_dbl(&t, r); + x25519_ge_p1p1_to_p2(r, &t); + ge_p2_dbl(&t, r); + x25519_ge_p1p1_to_p2(r, &t); + ge_p2_dbl(&t, r); + x25519_ge_p1p1_to_p2(r, &t); + ge_p2_dbl(&t, r); + x25519_ge_p1p1_to_p3(&u, &t); + + uint8_t index = scalar[31 - i/8]; + index >>= 4 - (i & 4); + index &= 0xf; + + unsigned j; + ge_cached selected; + for (j = 0; j < 16; j++) { + cmov_cached(&selected, &Ai[j], equal(j, index)); + } + + x25519_ge_add(&t, &u, &selected); + x25519_ge_p1p1_to_p2(r, &t); + } +} + static void slide(signed char *r, const uint8_t *a) { int i; int b; @@ -3697,8 +3724,8 @@ * where a = a[0]+256*a[1]+...+256^31 a[31]. * and b = b[0]+256*b[1]+...+256^31 b[31]. * B is the Ed25519 base point (x,4/5) with x positive. */ -void ge_double_scalarmult_vartime(ge_p2 *r, const uint8_t *a, - const ge_p3 *A, const uint8_t *b) { +static void ge_double_scalarmult_vartime(ge_p2 *r, const uint8_t *a, + const ge_p3 *A, const uint8_t *b) { signed char aslide[256]; signed char bslide[256]; ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ @@ -3710,30 +3737,30 @@ slide(aslide, a); slide(bslide, b); - ge_p3_to_cached(&Ai[0], A); + x25519_ge_p3_to_cached(&Ai[0], A); ge_p3_dbl(&t, A); - ge_p1p1_to_p3(&A2, &t); - ge_add(&t, &A2, &Ai[0]); - ge_p1p1_to_p3(&u, &t); - ge_p3_to_cached(&Ai[1], &u); - ge_add(&t, &A2, &Ai[1]); - ge_p1p1_to_p3(&u, &t); - ge_p3_to_cached(&Ai[2], &u); - ge_add(&t, &A2, &Ai[2]); - ge_p1p1_to_p3(&u, &t); - ge_p3_to_cached(&Ai[3], &u); - ge_add(&t, &A2, &Ai[3]); - ge_p1p1_to_p3(&u, &t); - ge_p3_to_cached(&Ai[4], &u); - ge_add(&t, &A2, &Ai[4]); - ge_p1p1_to_p3(&u, &t); - ge_p3_to_cached(&Ai[5], &u); - ge_add(&t, &A2, &Ai[5]); - ge_p1p1_to_p3(&u, &t); - ge_p3_to_cached(&Ai[6], &u); - ge_add(&t, &A2, &Ai[6]); - ge_p1p1_to_p3(&u, &t); - ge_p3_to_cached(&Ai[7], &u); + x25519_ge_p1p1_to_p3(&A2, &t); + x25519_ge_add(&t, &A2, &Ai[0]); + x25519_ge_p1p1_to_p3(&u, &t); + x25519_ge_p3_to_cached(&Ai[1], &u); + x25519_ge_add(&t, &A2, &Ai[1]); + x25519_ge_p1p1_to_p3(&u, &t); + x25519_ge_p3_to_cached(&Ai[2], &u); + x25519_ge_add(&t, &A2, &Ai[2]); + x25519_ge_p1p1_to_p3(&u, &t); + x25519_ge_p3_to_cached(&Ai[3], &u); + x25519_ge_add(&t, &A2, &Ai[3]); + x25519_ge_p1p1_to_p3(&u, &t); + x25519_ge_p3_to_cached(&Ai[4], &u); + x25519_ge_add(&t, &A2, &Ai[4]); + x25519_ge_p1p1_to_p3(&u, &t); + x25519_ge_p3_to_cached(&Ai[5], &u); + x25519_ge_add(&t, &A2, &Ai[5]); + x25519_ge_p1p1_to_p3(&u, &t); + x25519_ge_p3_to_cached(&Ai[6], &u); + x25519_ge_add(&t, &A2, &Ai[6]); + x25519_ge_p1p1_to_p3(&u, &t); + x25519_ge_p3_to_cached(&Ai[7], &u); ge_p2_0(r); @@ -3747,22 +3774,22 @@ ge_p2_dbl(&t, r); if (aslide[i] > 0) { - ge_p1p1_to_p3(&u, &t); - ge_add(&t, &u, &Ai[aslide[i] / 2]); + x25519_ge_p1p1_to_p3(&u, &t); + x25519_ge_add(&t, &u, &Ai[aslide[i] / 2]); } else if (aslide[i] < 0) { - ge_p1p1_to_p3(&u, &t); - ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]); + x25519_ge_p1p1_to_p3(&u, &t); + x25519_ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]); } if (bslide[i] > 0) { - ge_p1p1_to_p3(&u, &t); + x25519_ge_p1p1_to_p3(&u, &t); ge_madd(&t, &u, &Bi[bslide[i] / 2]); } else if (bslide[i] < 0) { - ge_p1p1_to_p3(&u, &t); + x25519_ge_p1p1_to_p3(&u, &t); ge_msub(&t, &u, &Bi[(-bslide[i]) / 2]); } - ge_p1p1_to_p2(r, &t); + x25519_ge_p1p1_to_p2(r, &t); } } @@ -3776,7 +3803,7 @@ * s[0]+256*s[1]+...+256^31*s[31] = s mod l * where l = 2^252 + 27742317777372353535851937790883648493. * Overwrites s in place. */ -static void sc_reduce(uint8_t *s) { +void x25519_sc_reduce(uint8_t *s) { int64_t s0 = 2097151 & load_3(s); int64_t s1 = 2097151 & (load_4(s + 2) >> 5); int64_t s2 = 2097151 & (load_3(s + 5) >> 2); @@ -4610,7 +4637,7 @@ az[31] |= 64; ge_p3 A; - ge_scalarmult_base(&A, az); + x25519_ge_scalarmult_base(&A, az); ge_p3_tobytes(out_public_key, &A); memcpy(out_private_key, seed, 32); @@ -4633,9 +4660,9 @@ uint8_t nonce[SHA512_DIGEST_LENGTH]; SHA512_Final(nonce, &hash_ctx); - sc_reduce(nonce); + x25519_sc_reduce(nonce); ge_p3 R; - ge_scalarmult_base(&R, nonce); + x25519_ge_scalarmult_base(&R, nonce); ge_p3_tobytes(out_sig, &R); SHA512_Init(&hash_ctx); @@ -4645,7 +4672,7 @@ uint8_t hram[SHA512_DIGEST_LENGTH]; SHA512_Final(hram, &hash_ctx); - sc_reduce(hram); + x25519_sc_reduce(hram); sc_muladd(out_sig + 32, hram, az, nonce); return 1; @@ -4655,7 +4682,7 @@ const uint8_t signature[64], const uint8_t public_key[32]) { ge_p3 A; if ((signature[63] & 224) != 0 || - ge_frombytes_vartime(&A, public_key) != 0) { + x25519_ge_frombytes_vartime(&A, public_key) != 0) { return 0; } @@ -4677,13 +4704,13 @@ uint8_t h[SHA512_DIGEST_LENGTH]; SHA512_Final(h, &hash_ctx); - sc_reduce(h); + x25519_sc_reduce(h); ge_p2 R; ge_double_scalarmult_vartime(&R, h, &A, scopy); uint8_t rcheck[32]; - ge_tobytes(rcheck, &R); + x25519_ge_tobytes(rcheck, &R); return CRYPTO_memcmp(rcheck, rcopy, sizeof(rcheck)) == 0; } @@ -4887,7 +4914,7 @@ e[31] |= 64; ge_p3 A; - ge_scalarmult_base(&A, e); + x25519_ge_scalarmult_base(&A, e); /* We only need the u-coordinate of the curve25519 point. The map is * u=(y+1)/(1-y). Since y=Y/Z, this gives u=(Z+Y)/(Z-Y). */
diff --git a/crypto/curve25519/internal.h b/crypto/curve25519/internal.h index 27994b7..ea206a3 100644 --- a/crypto/curve25519/internal.h +++ b/crypto/curve25519/internal.h
@@ -37,6 +37,70 @@ const uint8_t point[32]); #endif +/* fe means field element. Here the field is \Z/(2^255-19). An element t, + * entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77 + * t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on + * context. */ +typedef int32_t fe[10]; + +/* ge means group element. + + * Here the group is the set of pairs (x,y) of field elements (see fe.h) + * satisfying -x^2 + y^2 = 1 + d x^2y^2 + * where d = -121665/121666. + * + * Representations: + * ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z + * ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT + * ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T + * ge_precomp (Duif): (y+x,y-x,2dxy) */ + +typedef struct { + fe X; + fe Y; + fe Z; +} ge_p2; + +typedef struct { + fe X; + fe Y; + fe Z; + fe T; +} ge_p3; + +typedef struct { + fe X; + fe Y; + fe Z; + fe T; +} ge_p1p1; + +typedef struct { + fe yplusx; + fe yminusx; + fe xy2d; +} ge_precomp; + +typedef struct { + fe YplusX; + fe YminusX; + fe Z; + fe T2d; +} ge_cached; + +void x25519_ge_tobytes(uint8_t *s, const ge_p2 *h); +int x25519_ge_frombytes_vartime(ge_p3 *h, const uint8_t *s); +void x25519_ge_p3_to_cached(ge_cached *r, const ge_p3 *p); +void x25519_ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p); +void x25519_ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p); +void x25519_ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); +void x25519_ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); +void x25519_ge_scalarmult_small_precomp( + ge_p3 *h, const uint8_t a[32], const uint8_t precomp_table[15 * 2 * 32]); +void x25519_ge_scalarmult_base(ge_p3 *h, const uint8_t a[32]); +void x25519_ge_scalarmult(ge_p2 *r, const uint8_t *scalar, const ge_p3 *A); +void x25519_sc_reduce(uint8_t *s); + #if defined(__cplusplus) } /* extern C */
diff --git a/crypto/curve25519/spake25519.c b/crypto/curve25519/spake25519.c new file mode 100644 index 0000000..617418c --- /dev/null +++ b/crypto/curve25519/spake25519.c
@@ -0,0 +1,464 @@ +/* Copyright (c) 2016, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <openssl/curve25519.h> + +#include <string.h> + +#include <openssl/bytestring.h> +#include <openssl/mem.h> +#include <openssl/rand.h> +#include <openssl/sha.h> + +#include "internal.h" + + +/* The following precomputation tables are for the following + * points used in the SPAKE2 protocol. + * + * N: + * x: 49918732221787544735331783592030787422991506689877079631459872391322455579424 + * y: 54629554431565467720832445949441049581317094546788069926228343916274969994000 + * encoded: 10e3df0ae37d8e7a99b5fe74b44672103dbddcbd06af680d71329a11693bc778 + * + * M: + * x: 31406539342727633121250288103050113562375374900226415211311216773867585644232 + * y: 21177308356423958466833845032658859666296341766942662650232962324899758529114 + * encoded: 5ada7e4bf6ddd9adb6626d32131c6b5c51a1e347a3478f53cfcf441b88eed12e + * + * These points and their precomputation tables are generated with the + * following Python code. For a description of the precomputation table, + * see curve25519.c in this directory. + * + * Exact copies of the source code are kept in bug 27296743. + * + * import hashlib + * import ed25519 as E # http://ed25519.cr.yp.to/python/ed25519.py + * + * SEED_N = 'edwards25519 point generation seed (N)' + * SEED_M = 'edwards25519 point generation seed (M)' + * + * def genpoint(seed): + * v = hashlib.sha256(seed).digest() + * it = 1 + * while True: + * try: + * x,y = E.decodepoint(v) + * except Exception, e: + * print e + * it += 1 + * v = hashlib.sha256(v).digest() + * continue + * print "Found in %d iterations:" % it + * print " x = %d" % x + * print " y = %d" % y + * print " Encoded (hex)" + * print E.encodepoint((x,y)).encode('hex') + * return (x,y) + * + * def gentable(P): + * t = [] + * for i in range(1,16): + * k = (i >> 3 & 1) * (1 << 192) + \ + * (i >> 2 & 1) * (1 << 128) + \ + * (i >> 1 & 1) * (1 << 64) + \ + * (i & 1) + * t.append(E.scalarmult(P, k)) + * return ''.join(E.encodeint(x) + E.encodeint(y) for (x,y) in t) + * + * def printtable(table, name): + * print "static const uint8_t %s[15 * 2 * 32] = {" % name, + * for i in range(15 * 2 * 32): + * if i % 12 == 0: + * print "\n ", + * print " 0x%02x," % ord(table[i]), + * print "\n};" + * + * if __name__ == "__main__": + * print "Searching for N" + * N = genpoint(SEED_N) + * print "Generating precomputation table for N" + * Ntable = gentable(N) + * printtable(Ntable, "kSpakeNSmallPrecomp") + * + * print "Searching for M" + * M = genpoint(SEED_M) + * print "Generating precomputation table for M" + * Mtable = gentable(M) + * printtable(Mtable, "kSpakeMSmallPrecomp") + */ +static const uint8_t kSpakeNSmallPrecomp[15 * 2 * 32] = { + 0x20, 0x1b, 0xc5, 0xb3, 0x43, 0x17, 0x71, 0x10, 0x44, 0x1e, 0x73, 0xb3, + 0xae, 0x3f, 0xbf, 0x9f, 0xf5, 0x44, 0xc8, 0x13, 0x8f, 0xd1, 0x01, 0xc2, + 0x8a, 0x1a, 0x6d, 0xea, 0x4d, 0x00, 0x5d, 0x6e, 0x10, 0xe3, 0xdf, 0x0a, + 0xe3, 0x7d, 0x8e, 0x7a, 0x99, 0xb5, 0xfe, 0x74, 0xb4, 0x46, 0x72, 0x10, + 0x3d, 0xbd, 0xdc, 0xbd, 0x06, 0xaf, 0x68, 0x0d, 0x71, 0x32, 0x9a, 0x11, + 0x69, 0x3b, 0xc7, 0x78, 0x93, 0xf1, 0x57, 0x97, 0x6e, 0xf0, 0x6e, 0x45, + 0x37, 0x4a, 0xf4, 0x0b, 0x18, 0x51, 0xf5, 0x4f, 0x67, 0x3c, 0xdc, 0xec, + 0x84, 0xed, 0xd0, 0xeb, 0xca, 0xfb, 0xdb, 0xff, 0x7f, 0xeb, 0xa8, 0x23, + 0x68, 0x87, 0x13, 0x64, 0x6a, 0x10, 0xf7, 0x45, 0xe0, 0x0f, 0x32, 0x21, + 0x59, 0x7c, 0x0e, 0x50, 0xad, 0x56, 0xd7, 0x12, 0x69, 0x7b, 0x58, 0xf8, + 0xb9, 0x3b, 0xa5, 0xbb, 0x4d, 0x1b, 0x87, 0x1c, 0x46, 0xa7, 0x17, 0x9d, + 0x6d, 0x84, 0x45, 0xbe, 0x7f, 0x95, 0xd2, 0x34, 0xcd, 0x89, 0x95, 0xc0, + 0xf0, 0xd3, 0xdf, 0x6e, 0x10, 0x4a, 0xe3, 0x7b, 0xce, 0x7f, 0x40, 0x27, + 0xc7, 0x2b, 0xab, 0x66, 0x03, 0x59, 0xb4, 0x7b, 0xc7, 0xc7, 0xf0, 0x39, + 0x9a, 0x33, 0x35, 0xbf, 0xcc, 0x2f, 0xf3, 0x2e, 0x68, 0x9d, 0x53, 0x5c, + 0x88, 0x52, 0xe3, 0x77, 0x90, 0xa1, 0x27, 0x85, 0xc5, 0x74, 0x7f, 0x23, + 0x0e, 0x93, 0x01, 0x3e, 0xe7, 0x2e, 0x2e, 0x95, 0xf3, 0x0d, 0xc2, 0x25, + 0x25, 0x39, 0x39, 0x3d, 0x6e, 0x8e, 0x89, 0xbd, 0xe8, 0xbb, 0x67, 0x5e, + 0x8c, 0x66, 0x8b, 0x63, 0x28, 0x1e, 0x4e, 0x74, 0x85, 0xa8, 0xaf, 0x0f, + 0x12, 0x5d, 0xb6, 0x8a, 0x83, 0x1a, 0x77, 0x76, 0x5e, 0x62, 0x8a, 0xa7, + 0x3c, 0xb8, 0x05, 0x57, 0x2b, 0xaf, 0x36, 0x2e, 0x10, 0x90, 0xb2, 0x39, + 0xb4, 0x3e, 0x75, 0x6d, 0x3a, 0xa8, 0x31, 0x35, 0xc2, 0x1e, 0x8f, 0xc2, + 0x79, 0x89, 0x35, 0x16, 0x26, 0xd1, 0xc7, 0x0b, 0x04, 0x1f, 0x1d, 0xf9, + 0x9c, 0x05, 0xa6, 0x6b, 0xb5, 0x19, 0x5a, 0x24, 0x6d, 0x91, 0xc5, 0x31, + 0xfd, 0xc5, 0xfa, 0xe7, 0xa6, 0xcb, 0x0e, 0x4b, 0x18, 0x0d, 0x94, 0xc7, + 0xee, 0x1d, 0x46, 0x1f, 0x92, 0xb1, 0xb2, 0x4a, 0x2b, 0x43, 0x37, 0xfe, + 0xc2, 0x15, 0x11, 0x89, 0xef, 0x59, 0x73, 0x3c, 0x06, 0x76, 0x78, 0xcb, + 0xa6, 0x0d, 0x79, 0x5f, 0x28, 0x0b, 0x5b, 0x8c, 0x9e, 0xe4, 0xaa, 0x51, + 0x9a, 0x42, 0x6f, 0x11, 0x50, 0x3d, 0x01, 0xd6, 0x21, 0xc0, 0x99, 0x5e, + 0x1a, 0xe8, 0x81, 0x25, 0x80, 0xeb, 0xed, 0x5d, 0x37, 0x47, 0x30, 0x70, + 0xa0, 0x4e, 0x0b, 0x43, 0x17, 0xbe, 0xb6, 0x47, 0xe7, 0x2a, 0x62, 0x9d, + 0x5d, 0xa6, 0xc5, 0x33, 0x62, 0x9d, 0x56, 0x24, 0x9d, 0x1d, 0xb2, 0x13, + 0xbc, 0x17, 0x66, 0x43, 0xd1, 0x68, 0xd5, 0x3b, 0x17, 0x69, 0x17, 0xa6, + 0x06, 0x9e, 0x12, 0xb8, 0x7c, 0xd5, 0xaf, 0x3e, 0x21, 0x1b, 0x31, 0xeb, + 0x0b, 0xa4, 0x98, 0x1c, 0xf2, 0x6a, 0x5e, 0x7c, 0x9b, 0x45, 0x8f, 0xb2, + 0x12, 0x06, 0xd5, 0x8c, 0x1d, 0xb2, 0xa7, 0x57, 0x5f, 0x2f, 0x4f, 0xdb, + 0x52, 0x99, 0x7c, 0x58, 0x01, 0x5f, 0xf2, 0xa5, 0xf6, 0x51, 0x86, 0x21, + 0x2f, 0x5b, 0x8d, 0x6a, 0xae, 0x83, 0x34, 0x6d, 0x58, 0x4b, 0xef, 0xfe, + 0xbf, 0x73, 0x5d, 0xdb, 0xc4, 0x97, 0x2a, 0x85, 0xf3, 0x6c, 0x46, 0x42, + 0xb3, 0x90, 0xc1, 0x57, 0x97, 0x50, 0x35, 0xb1, 0x9d, 0xb7, 0xc7, 0x3c, + 0x85, 0x6d, 0x6c, 0xfd, 0xce, 0xb0, 0xc9, 0xa2, 0x77, 0xee, 0xc3, 0x6b, + 0x0c, 0x37, 0xfa, 0x30, 0x91, 0xd1, 0x2c, 0xb8, 0x5e, 0x7f, 0x81, 0x5f, + 0x87, 0xfd, 0x18, 0x02, 0x5a, 0x30, 0x4e, 0x62, 0xbc, 0x65, 0xc6, 0xce, + 0x1a, 0xcf, 0x2b, 0xaa, 0x56, 0x3e, 0x4d, 0xcf, 0xba, 0x62, 0x5f, 0x9a, + 0xd0, 0x72, 0xff, 0xef, 0x28, 0xbd, 0xbe, 0xd8, 0x57, 0x3d, 0xf5, 0x57, + 0x7d, 0xe9, 0x71, 0x31, 0xec, 0x98, 0x90, 0x94, 0xd9, 0x54, 0xbf, 0x84, + 0x0b, 0xe3, 0x06, 0x47, 0x19, 0x9a, 0x13, 0x1d, 0xef, 0x9d, 0x13, 0xf3, + 0xdb, 0xc3, 0x5c, 0x72, 0x9e, 0xed, 0x24, 0xaa, 0x64, 0xed, 0xe7, 0x0d, + 0xa0, 0x7c, 0x73, 0xba, 0x9b, 0x86, 0xa7, 0x3b, 0x55, 0xab, 0x58, 0x30, + 0xf1, 0x15, 0x81, 0x83, 0x2f, 0xf9, 0x62, 0x84, 0x98, 0x66, 0xf6, 0x55, + 0x21, 0xd8, 0xf2, 0x25, 0x64, 0x71, 0x4b, 0x12, 0x76, 0x59, 0xc5, 0xaa, + 0x93, 0x67, 0xc3, 0x86, 0x25, 0xab, 0x4e, 0x4b, 0xf6, 0xd8, 0x3f, 0x44, + 0x2e, 0x11, 0xe0, 0xbd, 0x6a, 0xf2, 0x5d, 0xf5, 0xf9, 0x53, 0xea, 0xa4, + 0xc8, 0xd9, 0x50, 0x33, 0x81, 0xd9, 0xa8, 0x2d, 0x91, 0x7d, 0x13, 0x2a, + 0x11, 0xcf, 0xde, 0x3f, 0x0a, 0xd2, 0xbc, 0x33, 0xb2, 0x62, 0x53, 0xea, + 0x77, 0x88, 0x43, 0x66, 0x27, 0x43, 0x85, 0xe9, 0x5f, 0x55, 0xf5, 0x2a, + 0x8a, 0xac, 0xdf, 0xff, 0x9b, 0x4c, 0x96, 0x9c, 0xa5, 0x7a, 0xce, 0xd5, + 0x79, 0x18, 0xf1, 0x0b, 0x58, 0x95, 0x7a, 0xe7, 0xd3, 0x74, 0x65, 0x0b, + 0xa4, 0x64, 0x30, 0xe8, 0x5c, 0xfc, 0x55, 0x56, 0xee, 0x14, 0x14, 0xd3, + 0x45, 0x3b, 0xf8, 0xde, 0x05, 0x3e, 0xb9, 0x3c, 0xd7, 0x6a, 0x52, 0x72, + 0x5b, 0x39, 0x09, 0xbe, 0x82, 0x23, 0x10, 0x4a, 0xb7, 0xc3, 0xdc, 0x4c, + 0x5d, 0xc9, 0xf1, 0x14, 0x83, 0xf9, 0x0b, 0x9b, 0xe9, 0x23, 0x84, 0x6a, + 0xc4, 0x08, 0x3d, 0xda, 0x3d, 0x12, 0x95, 0x87, 0x18, 0xa4, 0x7d, 0x3f, + 0x23, 0xde, 0xd4, 0x1e, 0xa8, 0x47, 0xc3, 0x71, 0xdb, 0xf5, 0x03, 0x6c, + 0x57, 0xe7, 0xa4, 0x43, 0x82, 0x33, 0x7b, 0x62, 0x46, 0x7d, 0xf7, 0x10, + 0x69, 0x18, 0x38, 0x27, 0x9a, 0x6f, 0x38, 0xac, 0xfa, 0x92, 0xc5, 0xae, + 0x66, 0xa6, 0x73, 0x95, 0x15, 0x0e, 0x4c, 0x04, 0xb6, 0xfc, 0xf5, 0xc7, + 0x21, 0x3a, 0x99, 0xdb, 0x0e, 0x36, 0xf0, 0x56, 0xbc, 0x75, 0xf9, 0x87, + 0x9b, 0x11, 0x18, 0x92, 0x64, 0x1a, 0xe7, 0xc7, 0xab, 0x5a, 0xc7, 0x26, + 0x7f, 0x13, 0x98, 0x42, 0x52, 0x43, 0xdb, 0xc8, 0x6d, 0x0b, 0xb7, 0x31, + 0x93, 0x24, 0xd6, 0xe8, 0x24, 0x1f, 0x6f, 0x21, 0xa7, 0x8c, 0xeb, 0xdb, + 0x83, 0xb8, 0x89, 0xe3, 0xc1, 0xd7, 0x69, 0x3b, 0x02, 0x6b, 0x54, 0x0f, + 0x84, 0x2f, 0xb5, 0x5c, 0x17, 0x77, 0xbe, 0xe5, 0x61, 0x0d, 0xc5, 0xdf, + 0x3b, 0xcf, 0x3e, 0x93, 0x4f, 0xf5, 0x89, 0xb9, 0x5a, 0xc5, 0x29, 0x31, + 0xc0, 0xc2, 0xff, 0xe5, 0x3f, 0xa6, 0xac, 0x03, 0xca, 0xf5, 0xff, 0xe0, + 0x36, 0xce, 0xf3, 0xe2, 0xb7, 0x9c, 0x02, 0xe9, 0x9e, 0xd2, 0xbc, 0x87, + 0x2f, 0x3d, 0x9a, 0x1d, 0x8f, 0xc5, 0x72, 0xb8, 0xa2, 0x01, 0xd4, 0x68, + 0xb1, 0x84, 0x16, 0x10, 0xf6, 0xf3, 0x52, 0x25, 0xd9, 0xdc, 0x4c, 0xdd, + 0x0f, 0xd6, 0x4a, 0xcf, 0x60, 0x96, 0x7e, 0xcc, 0x42, 0x0f, 0x64, 0x9d, + 0x72, 0x46, 0x04, 0x07, 0xf2, 0x5b, 0xf4, 0x07, 0xd1, 0xf4, 0x59, 0x71, +}; + +static const uint8_t kSpakeMSmallPrecomp[15 * 2 * 32] = { + 0xc8, 0xa6, 0x63, 0xc5, 0x97, 0xf1, 0xee, 0x40, 0xab, 0x62, 0x42, 0xee, + 0x25, 0x6f, 0x32, 0x6c, 0x75, 0x2c, 0xa7, 0xd3, 0xbd, 0x32, 0x3b, 0x1e, + 0x11, 0x9c, 0xbd, 0x04, 0xa9, 0x78, 0x6f, 0x45, 0x5a, 0xda, 0x7e, 0x4b, + 0xf6, 0xdd, 0xd9, 0xad, 0xb6, 0x62, 0x6d, 0x32, 0x13, 0x1c, 0x6b, 0x5c, + 0x51, 0xa1, 0xe3, 0x47, 0xa3, 0x47, 0x8f, 0x53, 0xcf, 0xcf, 0x44, 0x1b, + 0x88, 0xee, 0xd1, 0x2e, 0x03, 0x89, 0xaf, 0xc0, 0x61, 0x2d, 0x9e, 0x35, + 0xeb, 0x0e, 0x03, 0xe0, 0xb7, 0xfb, 0xa5, 0xbc, 0x44, 0xbe, 0x0c, 0x89, + 0x0a, 0x0f, 0xd6, 0x59, 0x47, 0x9e, 0xe6, 0x3d, 0x36, 0x9d, 0xff, 0x44, + 0x5e, 0xac, 0xab, 0xe5, 0x3a, 0xd5, 0xb0, 0x35, 0x9f, 0x6d, 0x7f, 0xba, + 0xc0, 0x85, 0x0e, 0xf4, 0x70, 0x3f, 0x13, 0x90, 0x4c, 0x50, 0x1a, 0xee, + 0xc5, 0xeb, 0x69, 0xfe, 0x98, 0x42, 0x87, 0x1d, 0xce, 0x6c, 0x29, 0xaa, + 0x2b, 0x31, 0xc2, 0x38, 0x7b, 0x6b, 0xee, 0x88, 0x0b, 0xba, 0xce, 0xa8, + 0xca, 0x19, 0x60, 0x1b, 0x16, 0xf1, 0x25, 0x1e, 0xcf, 0x63, 0x66, 0x1e, + 0xbb, 0x63, 0xeb, 0x7d, 0xca, 0xd2, 0xb4, 0x23, 0x5a, 0x01, 0x6f, 0x05, + 0xd1, 0xdc, 0x41, 0x73, 0x75, 0xc0, 0xfd, 0x30, 0x91, 0x52, 0x68, 0x96, + 0x45, 0xb3, 0x66, 0x01, 0x3b, 0x53, 0x89, 0x3c, 0x69, 0xbc, 0x6c, 0x69, + 0xe3, 0x51, 0x8f, 0xe3, 0xd2, 0x84, 0xd5, 0x28, 0x66, 0xb5, 0xe6, 0x06, + 0x09, 0xfe, 0x6d, 0xb0, 0x72, 0x16, 0xe0, 0x8a, 0xce, 0x61, 0x65, 0xa9, + 0x21, 0x32, 0x48, 0xdc, 0x7a, 0x1d, 0xe1, 0x38, 0x7f, 0x8c, 0x75, 0x88, + 0x3d, 0x08, 0xa9, 0x4a, 0x6f, 0x3d, 0x9f, 0x7f, 0x3f, 0xbd, 0x57, 0x6b, + 0x19, 0xce, 0x3f, 0x4a, 0xc9, 0xd3, 0xf9, 0x6e, 0x72, 0x7b, 0x5b, 0x74, + 0xea, 0xbe, 0x9c, 0x7a, 0x6d, 0x9c, 0x40, 0x49, 0xe6, 0xfb, 0x2a, 0x1a, + 0x75, 0x70, 0xe5, 0x4e, 0xed, 0x74, 0xe0, 0x75, 0xac, 0xc0, 0xb1, 0x11, + 0x3e, 0xf2, 0xaf, 0x88, 0x4d, 0x66, 0xb6, 0xf6, 0x15, 0x4f, 0x3c, 0x6c, + 0x77, 0xae, 0x47, 0x51, 0x63, 0x9a, 0xfe, 0xe1, 0xb4, 0x1a, 0x12, 0xdf, + 0xe9, 0x54, 0x8d, 0x3b, 0x30, 0x2a, 0x75, 0xe3, 0xe5, 0x29, 0xb1, 0x4c, + 0xb0, 0x7c, 0x6d, 0xb5, 0xae, 0x85, 0xdb, 0x1e, 0x38, 0x55, 0x96, 0xa5, + 0x5b, 0x9f, 0x15, 0x23, 0x28, 0x36, 0xb8, 0xa2, 0x41, 0xb4, 0xd7, 0x19, + 0x91, 0x8d, 0x26, 0x3e, 0xca, 0x9c, 0x05, 0x7a, 0x2b, 0x60, 0x45, 0x86, + 0x8b, 0xee, 0x64, 0x6f, 0x5c, 0x09, 0x4d, 0x4b, 0x5a, 0x7f, 0xb0, 0xc3, + 0x26, 0x9d, 0x8b, 0xb8, 0x83, 0x69, 0xcf, 0x16, 0x72, 0x62, 0x3e, 0x5e, + 0x53, 0x4f, 0x9c, 0x73, 0x76, 0xfc, 0x19, 0xef, 0xa0, 0x74, 0x3a, 0x11, + 0x1e, 0xd0, 0x4d, 0xb7, 0x87, 0xa1, 0xd6, 0x87, 0x6c, 0x0e, 0x6c, 0x8c, + 0xe9, 0xa0, 0x44, 0xc4, 0x72, 0x3e, 0x73, 0x17, 0x13, 0xd1, 0x4e, 0x3d, + 0x8e, 0x1d, 0x5a, 0x8b, 0x75, 0xcb, 0x59, 0x2c, 0x47, 0x87, 0x15, 0x41, + 0xfe, 0x08, 0xe9, 0xa6, 0x97, 0x17, 0x08, 0x26, 0x6a, 0xb5, 0xbb, 0x73, + 0xaa, 0xb8, 0x5b, 0x65, 0x65, 0x5b, 0x30, 0x9e, 0x62, 0x59, 0x02, 0xf8, + 0xb8, 0x0f, 0x32, 0x10, 0xc1, 0x36, 0x08, 0x52, 0x98, 0x4a, 0x1e, 0xf0, + 0xab, 0x21, 0x5e, 0xde, 0x16, 0x0c, 0xda, 0x09, 0x99, 0x6b, 0x9e, 0xc0, + 0x90, 0xa5, 0x5a, 0xcc, 0xb0, 0xb7, 0xbb, 0xd2, 0x8b, 0x5f, 0xd3, 0x3b, + 0x3e, 0x8c, 0xa5, 0x71, 0x66, 0x06, 0xe3, 0x28, 0xd4, 0xf8, 0x3f, 0xe5, + 0x27, 0xdf, 0xfe, 0x0f, 0x09, 0xb2, 0x8a, 0x09, 0x5a, 0x23, 0x61, 0x0d, + 0x2d, 0xf5, 0x44, 0xf1, 0x5c, 0xf8, 0x82, 0x4e, 0xdc, 0x78, 0x7a, 0xab, + 0xc3, 0x57, 0x91, 0xaf, 0x65, 0x6e, 0x71, 0xf1, 0x44, 0xbf, 0xed, 0x43, + 0x50, 0xb4, 0x67, 0x48, 0xef, 0x5a, 0x10, 0x46, 0x81, 0xb4, 0x0c, 0xc8, + 0x48, 0xed, 0x99, 0x7a, 0x45, 0xa5, 0x92, 0xc3, 0x69, 0xd6, 0xd7, 0x8a, + 0x20, 0x1b, 0xeb, 0x8f, 0xb2, 0xff, 0xec, 0x6d, 0x76, 0x04, 0xf8, 0xc2, + 0x58, 0x9b, 0xf2, 0x20, 0x53, 0xc4, 0x74, 0x91, 0x19, 0xdd, 0x2d, 0x12, + 0x53, 0xc7, 0x6e, 0xd0, 0x02, 0x51, 0x3c, 0xa6, 0x7d, 0x80, 0x75, 0x6b, + 0x1d, 0xdf, 0xf8, 0x6a, 0x52, 0xbb, 0x81, 0xf8, 0x30, 0x45, 0xef, 0x51, + 0x85, 0x36, 0xbe, 0x8e, 0xcf, 0x0b, 0x9a, 0x46, 0xe8, 0x3f, 0x99, 0xfd, + 0xf7, 0xd9, 0x3e, 0x84, 0xe5, 0xe3, 0x37, 0xcf, 0x98, 0x7f, 0xeb, 0x5e, + 0x5a, 0x53, 0x77, 0x1c, 0x20, 0xdc, 0xf1, 0x20, 0x99, 0xec, 0x60, 0x40, + 0x93, 0xef, 0x5c, 0x1c, 0x81, 0xe2, 0xa5, 0xad, 0x2a, 0xc2, 0xdb, 0x6b, + 0xc1, 0x7e, 0x8f, 0xa9, 0x23, 0x5b, 0xd9, 0x0d, 0xfe, 0xa0, 0xac, 0x11, + 0x28, 0xba, 0x8e, 0x92, 0x07, 0x2d, 0x07, 0x40, 0x83, 0x14, 0x4c, 0x35, + 0x8d, 0xd0, 0x11, 0xff, 0x98, 0xdb, 0x00, 0x30, 0x6f, 0x65, 0xb6, 0xa0, + 0x7f, 0x9c, 0x08, 0xb8, 0xce, 0xb3, 0xa8, 0x42, 0xd3, 0x84, 0x45, 0xe1, + 0xe3, 0x8f, 0xa6, 0x89, 0x21, 0xd7, 0x74, 0x02, 0x4d, 0x64, 0xdf, 0x54, + 0x15, 0x9e, 0xba, 0x12, 0x49, 0x09, 0x41, 0xf6, 0x10, 0x24, 0xa1, 0x84, + 0x15, 0xfd, 0x68, 0x6a, 0x57, 0x66, 0xb3, 0x6d, 0x4c, 0xea, 0xbf, 0xbc, + 0x60, 0x3f, 0x52, 0x1c, 0x44, 0x1b, 0xc0, 0x4a, 0x25, 0xe3, 0xd9, 0x4c, + 0x9a, 0x74, 0xad, 0xfc, 0x9e, 0x8d, 0x0b, 0x18, 0x66, 0x24, 0xd1, 0x06, + 0xac, 0x68, 0xc1, 0xae, 0x14, 0xce, 0xb1, 0xf3, 0x86, 0x9f, 0x87, 0x11, + 0xd7, 0x9f, 0x30, 0x92, 0xdb, 0xec, 0x0b, 0x4a, 0xe8, 0xf6, 0x53, 0x36, + 0x68, 0x12, 0x11, 0x5e, 0xe0, 0x34, 0xa4, 0xff, 0x00, 0x0a, 0x26, 0xb8, + 0x62, 0x79, 0x9c, 0x0c, 0xd5, 0xe5, 0xf5, 0x1c, 0x1a, 0x16, 0x84, 0x4d, + 0x8e, 0x5d, 0x31, 0x7e, 0xf7, 0xe2, 0xd3, 0xa1, 0x41, 0x90, 0x61, 0x5d, + 0x04, 0xb2, 0x9a, 0x18, 0x9e, 0x54, 0xfb, 0xd1, 0x61, 0x95, 0x1b, 0x08, + 0xca, 0x7c, 0x49, 0x44, 0x74, 0x1d, 0x2f, 0xca, 0xc4, 0x7a, 0xe1, 0x8b, + 0x2f, 0xbb, 0x96, 0xee, 0x19, 0x8a, 0x5d, 0xfb, 0x3e, 0x82, 0xe7, 0x15, + 0xdb, 0x29, 0x14, 0xee, 0xc9, 0x4d, 0x9a, 0xfb, 0x9f, 0x8a, 0xbb, 0x17, + 0x37, 0x1b, 0x6e, 0x28, 0x6c, 0xf9, 0xff, 0xb5, 0xb5, 0x8b, 0x9d, 0x88, + 0x20, 0x08, 0x10, 0xd7, 0xca, 0x58, 0xf6, 0xe1, 0x32, 0x91, 0x6f, 0x36, + 0xc0, 0xad, 0xc1, 0x57, 0x5d, 0x76, 0x31, 0x43, 0xf3, 0xdd, 0xec, 0xf1, + 0xa9, 0x79, 0xe9, 0xe9, 0x85, 0xd7, 0x91, 0xc7, 0x31, 0x62, 0x3c, 0xd2, + 0x90, 0x2c, 0x9c, 0xa4, 0x56, 0x37, 0x7b, 0xbe, 0x40, 0x58, 0xc0, 0x81, + 0x83, 0x22, 0xe8, 0x13, 0x79, 0x18, 0xdb, 0x3a, 0x1b, 0x31, 0x0d, 0x00, + 0x6c, 0x22, 0x62, 0x75, 0x70, 0xd8, 0x96, 0x59, 0x99, 0x44, 0x79, 0x71, + 0xa6, 0x76, 0x81, 0x28, 0xb2, 0x65, 0xe8, 0x47, 0x14, 0xc6, 0x39, 0x06, +}; + +enum spake2_state_t { + spake2_state_init = 0, + spake2_state_msg_generated, + spake2_state_key_generated, +}; + +struct spake2_ctx_st { + uint8_t private_key[32]; + uint8_t my_msg[32]; + uint8_t password_scalar[32]; + uint8_t password_hash[SHA512_DIGEST_LENGTH]; + uint8_t *my_name; + size_t my_name_len; + uint8_t *their_name; + size_t their_name_len; + enum spake2_role_t my_role; + enum spake2_state_t state; +}; + +SPAKE2_CTX *SPAKE2_CTX_new(enum spake2_role_t my_role, + const uint8_t *my_name, size_t my_name_len, + const uint8_t *their_name, size_t their_name_len) { + SPAKE2_CTX *ctx = OPENSSL_malloc(sizeof(SPAKE2_CTX)); + if (ctx == NULL) { + return NULL; + } + + memset(ctx, 0, sizeof(SPAKE2_CTX)); + ctx->my_role = my_role; + + CBS my_name_cbs, their_name_cbs; + CBS_init(&my_name_cbs, my_name, my_name_len); + CBS_init(&their_name_cbs, their_name, their_name_len); + if (!CBS_stow(&my_name_cbs, &ctx->my_name, &ctx->my_name_len) || + !CBS_stow(&their_name_cbs, &ctx->their_name, &ctx->their_name_len)) { + SPAKE2_CTX_free(ctx); + return NULL; + } + + return ctx; +} + +void SPAKE2_CTX_free(SPAKE2_CTX *ctx) { + if (ctx == NULL) { + return; + } + + OPENSSL_free(ctx->my_name); + OPENSSL_free(ctx->their_name); + OPENSSL_free(ctx); +} + +/* left_shift_3 sets |n| to |n|*8, where |n| is represented in little-endian + * order. */ +static void left_shift_3(uint8_t n[32]) { + uint8_t carry = 0; + unsigned i; + + for (i = 0; i < 32; i++) { + const uint8_t next_carry = n[i] >> 5; + n[i] = (n[i] << 3) | carry; + carry = next_carry; + } +} + +int SPAKE2_generate_msg(SPAKE2_CTX *ctx, uint8_t *out, size_t *out_len, + size_t max_out_len, const uint8_t *password, + size_t password_len) { + if (ctx->state != spake2_state_init) { + return 0; + } + + if (max_out_len < sizeof(ctx->my_msg)) { + return 0; + } + + uint8_t private_tmp[64]; + RAND_bytes(private_tmp, sizeof(private_tmp)); + x25519_sc_reduce(private_tmp); + /* Multiply by the cofactor (eight) so that we'll clear it when operating on + * the peer's point later in the protocol. */ + left_shift_3(private_tmp); + memcpy(ctx->private_key, private_tmp, sizeof(ctx->private_key)); + + ge_p3 P; + x25519_ge_scalarmult_base(&P, ctx->private_key); + + /* mask = h(password) * <N or M>. */ + uint8_t password_tmp[SHA512_DIGEST_LENGTH]; + SHA512(password, password_len, password_tmp); + memcpy(ctx->password_hash, password_tmp, sizeof(ctx->password_hash)); + x25519_sc_reduce(password_tmp); + memcpy(ctx->password_scalar, password_tmp, sizeof(ctx->password_scalar)); + + ge_p3 mask; + x25519_ge_scalarmult_small_precomp(&mask, ctx->password_scalar, + ctx->my_role == spake2_role_alice + ? kSpakeMSmallPrecomp + : kSpakeNSmallPrecomp); + + /* P* = P + mask. */ + ge_cached mask_cached; + x25519_ge_p3_to_cached(&mask_cached, &mask); + ge_p1p1 Pstar; + x25519_ge_add(&Pstar, &P, &mask_cached); + + /* Encode P* */ + ge_p2 Pstar_proj; + x25519_ge_p1p1_to_p2(&Pstar_proj, &Pstar); + x25519_ge_tobytes(ctx->my_msg, &Pstar_proj); + + memcpy(out, ctx->my_msg, sizeof(ctx->my_msg)); + *out_len = sizeof(ctx->my_msg); + ctx->state = spake2_state_msg_generated; + + return 1; +} + +static void update_with_length_prefix(SHA512_CTX *sha, const uint8_t *data, + const size_t len) { + uint8_t len_le[8]; + size_t l = len; + unsigned i; + + for (i = 0; i < 8; i++) { + len_le[i] = l & 0xff; + l >>= 8; + } + + SHA512_Update(sha, len_le, sizeof(len_le)); + SHA512_Update(sha, data, len); +} + +int SPAKE2_process_msg(SPAKE2_CTX *ctx, uint8_t *out_key, size_t *out_key_len, + size_t max_out_key_len, const uint8_t *their_msg, + size_t their_msg_len) { + if (ctx->state != spake2_state_msg_generated || + their_msg_len != 32) { + return 0; + } + + ge_p3 Qstar; + if (0 != x25519_ge_frombytes_vartime(&Qstar, their_msg)) { + /* Point received from peer was not on the curve. */ + return 0; + } + + /* Unmask peer's value. */ + ge_p3 peers_mask; + x25519_ge_scalarmult_small_precomp(&peers_mask, ctx->password_scalar, + ctx->my_role == spake2_role_alice + ? kSpakeNSmallPrecomp + : kSpakeMSmallPrecomp); + + ge_cached peers_mask_cached; + x25519_ge_p3_to_cached(&peers_mask_cached, &peers_mask); + + ge_p1p1 Q_compl; + ge_p3 Q_ext; + x25519_ge_sub(&Q_compl, &Qstar, &peers_mask_cached); + x25519_ge_p1p1_to_p3(&Q_ext, &Q_compl); + + ge_p2 dh_shared; + x25519_ge_scalarmult(&dh_shared, ctx->private_key, &Q_ext); + + uint8_t dh_shared_encoded[32]; + x25519_ge_tobytes(dh_shared_encoded, &dh_shared); + + SHA512_CTX sha; + SHA512_Init(&sha); + if (ctx->my_role == spake2_role_alice) { + update_with_length_prefix(&sha, ctx->my_name, ctx->my_name_len); + update_with_length_prefix(&sha, ctx->their_name, ctx->their_name_len); + update_with_length_prefix(&sha, ctx->my_msg, sizeof(ctx->my_msg)); + update_with_length_prefix(&sha, their_msg, 32); + } else { + update_with_length_prefix(&sha, ctx->their_name, ctx->their_name_len); + update_with_length_prefix(&sha, ctx->my_name, ctx->my_name_len); + update_with_length_prefix(&sha, their_msg, 32); + update_with_length_prefix(&sha, ctx->my_msg, sizeof(ctx->my_msg)); + } + update_with_length_prefix(&sha, dh_shared_encoded, sizeof(dh_shared_encoded)); + update_with_length_prefix(&sha, ctx->password_hash, + sizeof(ctx->password_hash)); + + uint8_t key[SHA512_DIGEST_LENGTH]; + SHA512_Final(key, &sha); + + size_t to_copy = max_out_key_len; + if (to_copy > sizeof(key)) { + to_copy = sizeof(key); + } + memcpy(out_key, key, to_copy); + *out_key_len = to_copy; + ctx->state = spake2_state_key_generated; + + return 1; +}
diff --git a/crypto/curve25519/spake25519_test.cc b/crypto/curve25519/spake25519_test.cc new file mode 100644 index 0000000..d97a860 --- /dev/null +++ b/crypto/curve25519/spake25519_test.cc
@@ -0,0 +1,169 @@ +/* Copyright (c) 2016, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <string> + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <openssl/curve25519.h> +#include "../test/scoped_types.h" + + +struct SPAKE2Run { + bool Run() { + ScopedSPAKE2_CTX alice(SPAKE2_CTX_new( + spake2_role_alice, + reinterpret_cast<const uint8_t *>(alice_names.first.data()), + alice_names.first.size(), + reinterpret_cast<const uint8_t *>(alice_names.second.data()), + alice_names.second.size())); + ScopedSPAKE2_CTX bob(SPAKE2_CTX_new( + spake2_role_bob, + reinterpret_cast<const uint8_t *>(bob_names.first.data()), + bob_names.first.size(), + reinterpret_cast<const uint8_t *>(bob_names.second.data()), + bob_names.second.size())); + + if (!alice || !bob) { + return false; + } + + uint8_t alice_msg[SPAKE2_MAX_MSG_SIZE]; + uint8_t bob_msg[SPAKE2_MAX_MSG_SIZE]; + size_t alice_msg_len, bob_msg_len; + + if (!SPAKE2_generate_msg( + alice.get(), alice_msg, &alice_msg_len, sizeof(alice_msg), + reinterpret_cast<const uint8_t *>(alice_password.data()), + alice_password.size()) || + !SPAKE2_generate_msg( + bob.get(), bob_msg, &bob_msg_len, sizeof(bob_msg), + reinterpret_cast<const uint8_t *>(bob_password.data()), + bob_password.size())) { + return false; + } + + if (alice_corrupt_msg_bit >= 0 && + static_cast<size_t>(alice_corrupt_msg_bit) < 8 * alice_msg_len) { + alice_msg[alice_corrupt_msg_bit/8] ^= 1 << (alice_corrupt_msg_bit & 7); + } + + uint8_t alice_key[64], bob_key[64]; + size_t alice_key_len, bob_key_len; + + if (!SPAKE2_process_msg(alice.get(), alice_key, &alice_key_len, + sizeof(alice_key), bob_msg, bob_msg_len) || + !SPAKE2_process_msg(bob.get(), bob_key, &bob_key_len, sizeof(bob_key), + alice_msg, alice_msg_len)) { + return false; + } + + key_matches_ = (alice_key_len == bob_key_len && + memcmp(alice_key, bob_key, alice_key_len) == 0); + + return true; + } + + bool key_matches() const { + return key_matches_; + } + + std::string alice_password = "password"; + std::string bob_password = "password"; + std::pair<std::string, std::string> alice_names = {"alice", "bob"}; + std::pair<std::string, std::string> bob_names = {"bob", "alice"}; + int alice_corrupt_msg_bit = -1; + + private: + bool key_matches_ = false; +}; + +static bool TestSPAKE2() { + for (unsigned i = 0; i < 20; i++) { + SPAKE2Run spake2; + if (!spake2.Run()) { + fprintf(stderr, "TestSPAKE2: SPAKE2 failed.\n"); + return false; + } + + if (!spake2.key_matches()) { + fprintf(stderr, "Key didn't match for equal passwords.\n"); + return false; + } + } + + return true; +} + +static bool TestWrongPassword() { + SPAKE2Run spake2; + spake2.bob_password = "wrong password"; + if (!spake2.Run()) { + fprintf(stderr, "TestSPAKE2: SPAKE2 failed.\n"); + return false; + } + + if (spake2.key_matches()) { + fprintf(stderr, "Key matched for unequal passwords.\n"); + return false; + } + + return true; +} + +static bool TestWrongNames() { + SPAKE2Run spake2; + spake2.alice_names.second = "charlie"; + spake2.bob_names.second = "charlie"; + if (!spake2.Run()) { + fprintf(stderr, "TestSPAKE2: SPAKE2 failed.\n"); + return false; + } + + if (spake2.key_matches()) { + fprintf(stderr, "Key matched for unequal names.\n"); + return false; + } + + return true; +} + +static bool TestCorruptMessages() { + for (int i = 0; i < 8 * SPAKE2_MAX_MSG_SIZE; i++) { + SPAKE2Run spake2; + spake2.alice_corrupt_msg_bit = i; + if (spake2.Run() && spake2.key_matches()) { + fprintf(stderr, "Passed after corrupting Alice's message, bit %d\n", i); + return false; + } + } + + return true; +} + +/* TODO(agl): add tests with fixed vectors once SPAKE2 is nailed down. */ + +int main(int argc, char **argv) { + if (!TestSPAKE2() || + !TestWrongPassword() || + !TestWrongNames() || + !TestCorruptMessages()) { + return 1; + } + + printf("PASS\n"); + return 0; +}
diff --git a/crypto/test/scoped_types.h b/crypto/test/scoped_types.h index b5ae324..eef30e7 100644 --- a/crypto/test/scoped_types.h +++ b/crypto/test/scoped_types.h
@@ -25,10 +25,11 @@ #include <openssl/bio.h> #include <openssl/bn.h> #include <openssl/cmac.h> +#include <openssl/curve25519.h> #include <openssl/dh.h> +#include <openssl/ecdsa.h> #include <openssl/ec.h> #include <openssl/ec_key.h> -#include <openssl/ecdsa.h> #include <openssl/evp.h> #include <openssl/hmac.h> #include <openssl/mem.h> @@ -112,6 +113,7 @@ using ScopedPKCS8_PRIV_KEY_INFO = ScopedOpenSSLType<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>; using ScopedPKCS12 = ScopedOpenSSLType<PKCS12, PKCS12_free>; +using ScopedSPAKE2_CTX = ScopedOpenSSLType<SPAKE2_CTX, SPAKE2_CTX_free>; using ScopedRSA = ScopedOpenSSLType<RSA, RSA_free>; using ScopedX509 = ScopedOpenSSLType<X509, X509_free>; using ScopedX509_ALGOR = ScopedOpenSSLType<X509_ALGOR, X509_ALGOR_free>;
diff --git a/include/openssl/base.h b/include/openssl/base.h index f1da61a..8507f37 100644 --- a/include/openssl/base.h +++ b/include/openssl/base.h
@@ -240,6 +240,7 @@ typedef struct sha256_state_st SHA256_CTX; typedef struct sha512_state_st SHA512_CTX; typedef struct sha_state_st SHA_CTX; +typedef struct spake2_ctx_st SPAKE2_CTX; typedef struct srtp_protection_profile_st SRTP_PROTECTION_PROFILE; typedef struct ssl_cipher_st SSL_CIPHER; typedef struct ssl_ctx_st SSL_CTX;
diff --git a/include/openssl/curve25519.h b/include/openssl/curve25519.h index c202575..fd6a0da 100644 --- a/include/openssl/curve25519.h +++ b/include/openssl/curve25519.h
@@ -84,6 +84,88 @@ const uint8_t public_key[32]); +/* SPAKE2. + * + * SPAKE2 is a password-authenticated key-exchange. It allows two parties, + * who share a low-entropy secret (i.e. password), to agree on a shared key. + * An attacker can only make one guess of the password per execution of the + * protocol. + * + * See https://tools.ietf.org/html/draft-irtf-cfrg-spake2-02. */ + +/* spake2_role_t enumerates the different “roles” in SPAKE2. The protocol + * requires that the symmetry of the two parties be broken so one participant + * must be “Alice” and the other be “Bob”. */ +enum spake2_role_t { + spake2_role_alice, + spake2_role_bob, +}; + +/* SPAKE2_CTX_new creates a new |SPAKE2_CTX| (which can only be used for a + * single execution of the protocol). SPAKE2 requires the symmetry of the two + * parties to be broken which is indicated via |my_role| – each party must pass + * a different value for this argument. + * + * The |my_name| and |their_name| arguments allow optional, opaque names to be + * bound into the protocol. For example MAC addresses, hostnames, usernames + * etc. These values are not exposed and can avoid context-confusion attacks + * when a password is shared between several devices. */ +OPENSSL_EXPORT SPAKE2_CTX *SPAKE2_CTX_new( + enum spake2_role_t my_role, + const uint8_t *my_name, size_t my_name_len, + const uint8_t *their_name, size_t their_name_len); + +/* SPAKE2_CTX_free frees |ctx| and all the resources that it has allocated. */ +OPENSSL_EXPORT void SPAKE2_CTX_free(SPAKE2_CTX *ctx); + +/* SPAKE2_MAX_MSG_SIZE is the maximum size of a SPAKE2 message. */ +#define SPAKE2_MAX_MSG_SIZE 32 + +/* SPAKE2_generate_msg generates a SPAKE2 message given |password|, writes + * it to |out| and sets |*out_len| to the number of bytes written. + * + * At most |max_out_len| bytes are written to |out| and, in order to ensure + * success, |max_out_len| should be at least |SPAKE2_MAX_MSG_SIZE| bytes. + * + * This function can only be called once for a given |SPAKE2_CTX|. + * + * It returns one on success and zero on error. */ +OPENSSL_EXPORT int SPAKE2_generate_msg(SPAKE2_CTX *ctx, uint8_t *out, + size_t *out_len, size_t max_out_len, + const uint8_t *password, + size_t password_len); + +/* SPAKE2_MAX_KEY_SIZE is the maximum amount of key material that SPAKE2 will + * produce. */ +#define SPAKE2_MAX_KEY_SIZE 64 + +/* SPAKE2_process_msg completes the SPAKE2 exchange given the peer's message in + * |their_msg|, writes at most |max_out_key_len| bytes to |out_key| and sets + * |*out_key_len| to the number of bytes written. + * + * The resulting keying material is suitable for: + * a) Using directly in a key-confirmation step: i.e. each side could + * transmit a hash of their role, a channel-binding value and the key + * material to prove to the other side that they know the shared key. + * b) Using as input keying material to HKDF to generate a variety of subkeys + * for encryption etc. + * + * If |max_out_key_key| is smaller than the amount of key material generated + * then the key is silently truncated. If you want to ensure that no truncation + * occurs then |max_out_key| should be at least |SPAKE2_MAX_KEY_SIZE|. + * + * You must call |SPAKE2_generate_msg| on a given |SPAKE2_CTX| before calling + * this function. On successful return, |ctx| is complete and calling + * |SPAKE2_CTX_free| is the only acceptable operation on it. + * + * Returns one on success or zero on error. */ +OPENSSL_EXPORT int SPAKE2_process_msg(SPAKE2_CTX *ctx, uint8_t *out_key, + size_t *out_key_len, + size_t max_out_key_len, + const uint8_t *their_msg, + size_t their_msg_len); + + #if defined(__cplusplus) } /* extern C */ #endif
diff --git a/tool/speed.cc b/tool/speed.cc index db7c5fa..a4edfe7d 100644 --- a/tool/speed.cc +++ b/tool/speed.cc
@@ -467,6 +467,53 @@ return true; } +static bool SpeedSPAKE2(const std::string &selected) { + if (!selected.empty() && selected.find("SPAKE2") == std::string::npos) { + return true; + } + + TimeResults results; + + static const uint8_t kAliceName[] = {'A'}; + static const uint8_t kBobName[] = {'B'}; + static const uint8_t kPassword[] = "password"; + ScopedSPAKE2_CTX alice(SPAKE2_CTX_new(spake2_role_alice, kAliceName, + sizeof(kAliceName), kBobName, + sizeof(kBobName))); + uint8_t alice_msg[SPAKE2_MAX_MSG_SIZE]; + size_t alice_msg_len; + + if (!SPAKE2_generate_msg(alice.get(), alice_msg, &alice_msg_len, + sizeof(alice_msg), + kPassword, sizeof(kPassword))) { + fprintf(stderr, "SPAKE2_generate_msg failed.\n"); + return false; + } + + if (!TimeFunction(&results, [alice_msg, alice_msg_len]() -> bool { + ScopedSPAKE2_CTX bob(SPAKE2_CTX_new(spake2_role_bob, kBobName, + sizeof(kBobName), kAliceName, + sizeof(kAliceName))); + uint8_t bob_msg[SPAKE2_MAX_MSG_SIZE], bob_key[64]; + size_t bob_msg_len, bob_key_len; + if (!SPAKE2_generate_msg(bob.get(), bob_msg, &bob_msg_len, + sizeof(bob_msg), kPassword, + sizeof(kPassword)) || + !SPAKE2_process_msg(bob.get(), bob_key, &bob_key_len, + sizeof(bob_key), alice_msg, alice_msg_len)) { + return false; + } + + return true; + })) { + fprintf(stderr, "SPAKE2 failed.\n"); + } + + results.Print("SPAKE2 over Ed25519"); + + return true; +} + bool Speed(const std::vector<std::string> &args) { std::string selected; if (args.size() > 1) { @@ -546,7 +593,8 @@ !SpeedRandom(selected) || !SpeedECDH(selected) || !SpeedECDSA(selected) || - !Speed25519(selected)) { + !Speed25519(selected) || + !SpeedSPAKE2(selected)) { return false; }
diff --git a/util/all_tests.json b/util/all_tests.json index bacee7a..3614591 100644 --- a/util/all_tests.json +++ b/util/all_tests.json
@@ -34,6 +34,7 @@ ["crypto/constant_time_test"], ["crypto/curve25519/ed25519_test", "crypto/curve25519/ed25519_tests.txt"], ["crypto/curve25519/x25519_test"], + ["crypto/curve25519/spake25519_test"], ["crypto/dh/dh_test"], ["crypto/digest/digest_test"], ["crypto/dsa/dsa_test"],