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>
This commit is contained in:
parent
e4f3f4df6e
commit
f27459e412
@ -22,6 +22,7 @@ add_library(
|
||||
OBJECT
|
||||
|
||||
curve25519.c
|
||||
spake25519.c
|
||||
x25519-x86_64.c
|
||||
|
||||
${CURVE25519_ARCH_SOURCES}
|
||||
@ -45,3 +46,12 @@ add_executable(
|
||||
|
||||
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)
|
||||
|
@ -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 @@ static void fe_pow22523(fe out, const fe z) {
|
||||
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 d = {-10913610, 13857413, -15372611, 6949391, 114729,
|
||||
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 @@ static void ge_p3_0(ge_p3 *h) {
|
||||
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 @@ static const fe d2 = {-21827239, -5839606, -30745221, 13898782, 229458,
|
||||
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 @@ static void ge_p3_to_cached(ge_cached *r, const ge_p3 *p) {
|
||||
}
|
||||
|
||||
/* 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 @@ static void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) {
|
||||
}
|
||||
|
||||
/* 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 @@ static void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) {
|
||||
}
|
||||
|
||||
/* 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 @@ static void cmov(ge_precomp *t, ge_precomp *u, uint8_t b) {
|
||||
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 @@ static const uint8_t k25519SmallPrecomp[15 * 2 * 32] = {
|
||||
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 @@ static void table_select(ge_precomp *t, int pos, signed char b) {
|
||||
*
|
||||
* 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 @@ static void ge_scalarmult_base(ge_p3 *h, const uint8_t *a) {
|
||||
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 @@ static ge_precomp Bi[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 @@ void ge_double_scalarmult_vartime(ge_p2 *r, const uint8_t *a,
|
||||
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 @@ void ge_double_scalarmult_vartime(ge_p2 *r, const uint8_t *a,
|
||||
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 @@ void ge_double_scalarmult_vartime(ge_p2 *r, const uint8_t *a,
|
||||
* 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 @@ void ED25519_keypair(uint8_t out_public_key[32], uint8_t out_private_key[64]) {
|
||||
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 @@ int ED25519_sign(uint8_t *out_sig, const uint8_t *message, size_t message_len,
|
||||
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 @@ int ED25519_sign(uint8_t *out_sig, const uint8_t *message, size_t message_len,
|
||||
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 @@ int ED25519_verify(const uint8_t *message, size_t message_len,
|
||||
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 @@ int ED25519_verify(const uint8_t *message, size_t message_len,
|
||||
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 @@ void X25519_public_from_private(uint8_t out_public_value[32],
|
||||
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). */
|
||||
|
@ -37,6 +37,70 @@ void x25519_NEON(uint8_t out[32], const uint8_t scalar[32],
|
||||
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 */
|
||||
|
464
crypto/curve25519/spake25519.c
Normal file
464
crypto/curve25519/spake25519.c
Normal file
@ -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;
|
||||
}
|
169
crypto/curve25519/spake25519_test.cc
Normal file
169
crypto/curve25519/spake25519_test.cc
Normal file
@ -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;
|
||||
}
|
@ -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 ScopedEVP_PKEY_CTX = ScopedOpenSSLType<EVP_PKEY_CTX, EVP_PKEY_CTX_free>;
|
||||
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>;
|
||||
|
@ -240,6 +240,7 @@ typedef struct rsa_st RSA;
|
||||
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;
|
||||
|
@ -84,6 +84,88 @@ OPENSSL_EXPORT int ED25519_verify(const uint8_t *message, size_t message_len,
|
||||
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
|
||||
|
@ -467,6 +467,53 @@ static bool Speed25519(const std::string &selected) {
|
||||
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 @@ bool Speed(const std::vector<std::string> &args) {
|
||||
!SpeedRandom(selected) ||
|
||||
!SpeedECDH(selected) ||
|
||||
!SpeedECDSA(selected) ||
|
||||
!Speed25519(selected)) {
|
||||
!Speed25519(selected) ||
|
||||
!SpeedSPAKE2(selected)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -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"],
|
||||
|
Loading…
x
Reference in New Issue
Block a user