/* * X25519 (Curve25519 ECDH), RFC 7748. * * This is the compact TweetNaCl field arithmetic (public domain), which uses * only 64-bit integer operations -- no floating point, no wide multiply beyond * what the compiler provides -- so it is safe in the freestanding kernel. It is * the key exchange for the TLS 1.3 client: x25519_base() turns a random private * scalar into a public key to send, and x25519() combines our private scalar * with the peer's public key to produce the shared secret. */ #include #include typedef int64_t i64; typedef uint8_t u8; typedef i64 gf[16]; static const gf _121665 = { 0xDB41, 1 }; static void car25519(gf o) { int i; i64 c; for (i = 0; i < 16; i++) { o[i] += (1LL << 16); c = o[i] >> 16; o[(i + 1) * (i < 15)] += c - 1 + 37 * (c - 1) * (i == 15); o[i] -= c << 16; } } static void sel25519(gf p, gf q, int b) { i64 t, i, c = ~(b - 1); for (i = 0; i < 16; i++) { t = c & (p[i] ^ q[i]); p[i] ^= t; q[i] ^= t; } } static void pack25519(u8* o, const gf n) { int i, j, b; gf m, t; for (i = 0; i < 16; i++) t[i] = n[i]; car25519(t); car25519(t); car25519(t); for (j = 0; j < 2; j++) { m[0] = t[0] - 0xffed; for (i = 1; i < 15; i++) { m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1); m[i - 1] &= 0xffff; } m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1); b = (m[15] >> 16) & 1; m[14] &= 0xffff; sel25519(t, m, 1 - b); } for (i = 0; i < 16; i++) { o[2 * i] = t[i] & 0xff; o[2 * i + 1] = t[i] >> 8; } } static void unpack25519(gf o, const u8* n) { int i; for (i = 0; i < 16; i++) o[i] = n[2 * i] + ((i64)n[2 * i + 1] << 8); o[15] &= 0x7fff; } static void A(gf o, const gf a, const gf b) { int i; for (i = 0; i < 16; i++) o[i] = a[i] + b[i]; } static void Z(gf o, const gf a, const gf b) { int i; for (i = 0; i < 16; i++) o[i] = a[i] - b[i]; } static void M(gf o, const gf a, const gf b) { i64 i, j, t[31]; for (i = 0; i < 31; i++) t[i] = 0; for (i = 0; i < 16; i++) for (j = 0; j < 16; j++) t[i + j] += a[i] * b[j]; for (i = 0; i < 15; i++) t[i] += 38 * t[i + 16]; for (i = 0; i < 16; i++) o[i] = t[i]; car25519(o); car25519(o); } static void S(gf o, const gf a) { M(o, a, a); } static void inv25519(gf o, const gf i) { gf c; int a; for (a = 0; a < 16; a++) c[a] = i[a]; for (a = 253; a >= 0; a--) { S(c, c); if (a != 2 && a != 4) M(c, c, i); } for (a = 0; a < 16; a++) o[a] = c[a]; } static int crypto_scalarmult(u8* q, const u8* n, const u8* p) { u8 z[32]; i64 x[80], r, i; gf a, b, c, d, e, f; for (i = 0; i < 31; i++) z[i] = n[i]; z[31] = (n[31] & 127) | 64; z[0] &= 248; unpack25519(x, p); for (i = 0; i < 16; i++) { b[i] = x[i]; d[i] = a[i] = c[i] = 0; } a[0] = d[0] = 1; for (i = 254; i >= 0; --i) { r = (z[i >> 3] >> (i & 7)) & 1; sel25519(a, b, r); sel25519(c, d, r); A(e, a, c); Z(a, a, c); A(c, b, d); Z(b, b, d); S(d, e); S(f, a); M(a, c, a); M(c, b, e); A(e, a, c); Z(a, a, c); S(b, a); Z(c, d, f); M(a, c, _121665); A(a, a, d); M(c, c, a); M(a, d, f); M(d, b, x); S(b, e); sel25519(a, b, r); sel25519(c, d, r); } for (i = 0; i < 16; i++) { x[i + 16] = a[i]; x[i + 32] = c[i]; x[i + 48] = b[i]; x[i + 64] = d[i]; } inv25519(x + 32, x + 32); M(x + 16, x + 16, x + 32); pack25519(q, x + 16); return 0; } // The Curve25519 base point, u = 9. static const u8 x25519_basepoint[32] = { 9 }; void x25519_base(uint8_t out_pub[32], const uint8_t priv[32]) { crypto_scalarmult(out_pub, priv, x25519_basepoint); } void x25519(uint8_t out_shared[32], const uint8_t priv[32], const uint8_t peer_pub[32]) { crypto_scalarmult(out_shared, priv, peer_pub); }