ZeroTierOne/core/ECC384.cpp

779 lines
27 KiB
C++

/*
* ECC384 code is based on EASY-ECC by Kenneth MacKay with only minor changes.
*
* It can be found in various places online such as: https://github.com/jestan/easy-ecc
*
* This file is under the BSD 2-clause license since that was the license under which
* the original ECC code was distributed.
*/
#include "ECC384.hpp"
#include "Constants.hpp"
#include "Utils.hpp"
namespace ZeroTier {
namespace {
#define ECC_CURVE_BYTES 48
#define ECC_CURVE_DIGITS (ECC_CURVE_BYTES / 8)
#define ECC_CREATE_KEY_MAX_ATTEMPTS 4096
#define vli_clear(p) std::fill((p), (p) + ECC_CURVE_DIGITS, 0ULL)
#define vli_set(dest, src) std::copy((src), (src) + ECC_CURVE_DIGITS, (dest))
#define vli_isEven(vli) ((vli[0] & 1ULL) == 0ULL)
#define vli_isZero(p) std::all_of((p), (p) + ECC_CURVE_DIGITS, [](const uint64_t i) { return i == 0; })
#define vli_testBit(p, b) ((p)[(unsigned int)(b) >> 6U] & (1ULL << ((unsigned int)(b)&63U)))
#ifndef ZT_HAVE_UINT128
struct uint128_t {
uint64_t m_low, m_high;
};
#endif
struct EccPoint {
uint64_t x[ECC_CURVE_DIGITS], y[ECC_CURVE_DIGITS];
};
// ECC curve NIST P-384
const uint64_t curve_p[ECC_CURVE_DIGITS] = { 0x00000000FFFFFFFF, 0xFFFFFFFF00000000, 0xFFFFFFFFFFFFFFFE,
0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF };
const uint64_t curve_b[ECC_CURVE_DIGITS] = { 0x2A85C8EDD3EC2AEF, 0xC656398D8A2ED19D, 0x0314088F5013875A,
0x181D9C6EFE814112, 0x988E056BE3F82D19, 0xB3312FA7E23EE7E4 };
const EccPoint curve_G = { { 0x3A545E3872760AB7, 0x5502F25DBF55296C, 0x59F741E082542A38, 0x6E1D3B628BA79B98,
0x8EB1C71EF320AD74, 0xAA87CA22BE8B0537 },
{ 0x7A431D7C90EA0E5F, 0x0A60B1CE1D7E819D, 0xE9DA3113B5F0B8C0, 0xF8F41DBD289A147C,
0x5D9E98BF9292DC29, 0x3617DE4A96262C6F } };
const uint64_t curve_n[ECC_CURVE_DIGITS] = { 0xECEC196ACCC52973, 0x581A0DB248B0A77A, 0xC7634D81F4372DDF,
0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF };
ZT_INLINE unsigned int vli_numBits(const uint64_t *const p_vli)
{
int l_numDigits = ECC_CURVE_DIGITS - 1;
for (; l_numDigits >= 0 && p_vli[l_numDigits] == 0; --l_numDigits) {}
if (likely(l_numDigits > -1)) {
uint64_t l_digit = p_vli[l_numDigits];
int i = 0;
for (; l_digit; ++i) {
l_digit >>= 1;
}
return (unsigned int)((l_numDigits * 64) + i);
}
return 0;
}
ZT_INLINE int vli_cmp(const uint64_t *const p_left, const uint64_t *const p_right)
{
for (int i = ECC_CURVE_DIGITS - 1; i >= 0; --i) {
if (p_left[i] > p_right[i]) {
return 1;
}
else if (p_left[i] < p_right[i]) {
return -1;
}
}
return 0;
}
ZT_INLINE uint64_t vli_lshift(uint64_t *const p_result, const uint64_t *const p_in, const unsigned int p_shift)
{
uint64_t l_carry = 0;
for (unsigned int i = 0, p_shift2 = (64U - p_shift); i < ECC_CURVE_DIGITS; ++i) {
uint64_t l_temp = p_in[i];
p_result[i] = (l_temp << p_shift) | l_carry;
l_carry = l_temp >> p_shift2;
}
return l_carry;
}
ZT_INLINE void vli_rshift1(uint64_t *p_vli)
{
uint64_t *const l_end = p_vli, l_carry = 0;
p_vli += ECC_CURVE_DIGITS;
while (p_vli-- > l_end) {
const uint64_t l_temp = *p_vli;
*p_vli = (l_temp >> 1U) | l_carry;
l_carry = l_temp << 63U;
}
}
ZT_INLINE uint64_t vli_add(uint64_t *const p_result, const uint64_t *const p_left, const uint64_t *const p_right)
{
uint64_t l_carry = 0;
for (unsigned int i = 0; i < ECC_CURVE_DIGITS; ++i) {
uint64_t l_sum = p_left[i] + p_right[i] + l_carry;
if (l_sum != p_left[i]) {
l_carry = (l_sum < p_left[i]);
}
p_result[i] = l_sum;
}
return l_carry;
}
ZT_INLINE uint64_t vli_sub(uint64_t *const p_result, const uint64_t *const p_left, const uint64_t *const p_right)
{
uint64_t l_borrow = 0;
for (unsigned int i = 0; i < ECC_CURVE_DIGITS; ++i) {
uint64_t l_diff = p_left[i] - p_right[i] - l_borrow;
if (l_diff != p_left[i]) {
l_borrow = (l_diff > p_left[i]);
}
p_result[i] = l_diff;
}
return l_borrow;
}
#ifdef ZT_HAVE_UINT128
void vli_mult(uint64_t *const p_result, const uint64_t *const p_left, const uint64_t *const p_right)
{
uint128_t r01 = 0;
uint64_t r2 = 0;
for (int k = 0; k < ECC_CURVE_DIGITS * 2 - 1; ++k) {
for (int i = (k < ECC_CURVE_DIGITS ? 0 : (k + 1) - ECC_CURVE_DIGITS); i <= k && i < ECC_CURVE_DIGITS; ++i) {
uint128_t l_product = (uint128_t)p_left[i] * p_right[k - i];
r01 += l_product;
r2 += (r01 < l_product);
}
p_result[k] = (uint64_t)r01;
r01 = (r01 >> 64U) | (((uint128_t)r2) << 64U);
r2 = 0;
}
p_result[ECC_CURVE_DIGITS * 2 - 1] = (uint64_t)r01;
}
ZT_INLINE void vli_square(uint64_t *const p_result, const uint64_t *const p_left)
{
uint128_t r01 = 0;
uint64_t r2 = 0;
for (int k = 0; k < ECC_CURVE_DIGITS * 2 - 1; ++k) {
for (int i = (k < ECC_CURVE_DIGITS ? 0 : (k + 1) - ECC_CURVE_DIGITS); i <= k && i <= k - i; ++i) {
uint128_t l_product = (uint128_t)p_left[i] * p_left[k - i];
if (i < k - i) {
r2 += l_product >> 127U;
l_product *= 2;
}
r01 += l_product;
r2 += (r01 < l_product);
}
p_result[k] = (uint64_t)r01;
r01 = (r01 >> 64U) | (((uint128_t)r2) << 64U);
r2 = 0;
}
p_result[ECC_CURVE_DIGITS * 2 - 1] = (uint64_t)r01;
}
#else /* ZT_HAVE_UINT128 */
uint128_t mul_64_64(uint64_t p_left, uint64_t p_right)
{
uint64_t a0 = p_left & 0xffffffffull, a1 = p_left >> 32, b0 = p_right & 0xffffffffull, b1 = p_right >> 32,
m0 = a0 * b0, m1 = a0 * b1, m2 = a1 * b0, m3 = a1 * b1;
uint128_t l_result;
m2 += (m0 >> 32);
m2 += m1;
if (m2 < m1) { // overflow
m3 += 0x100000000ull;
}
l_result.m_low = (m0 & 0xffffffffull) | (m2 << 32);
l_result.m_high = m3 + (m2 >> 32);
return l_result;
}
ZT_INLINE uint128_t add_128_128(uint128_t a, uint128_t b)
{
uint128_t l_result;
l_result.m_low = a.m_low + b.m_low;
l_result.m_high = a.m_high + b.m_high + (l_result.m_low < a.m_low);
return l_result;
}
void vli_mult(uint64_t *const p_result, uint64_t *const p_left, const uint64_t *const p_right)
{
uint128_t r01 = { 0, 0 };
uint64_t r2 = 0;
/* Compute each digit of p_result in sequence, maintaining the carries. */
for (int k = 0; k < ECC_CURVE_DIGITS * 2 - 1; ++k) {
for (int i = (k < ECC_CURVE_DIGITS ? 0 : (k + 1) - ECC_CURVE_DIGITS); i <= k && i < ECC_CURVE_DIGITS; ++i) {
uint128_t l_product = mul_64_64(p_left[i], p_right[k - i]);
r01 = add_128_128(r01, l_product);
r2 += (r01.m_high < l_product.m_high);
}
p_result[k] = r01.m_low;
r01.m_low = r01.m_high;
r01.m_high = r2;
r2 = 0;
}
p_result[ECC_CURVE_DIGITS * 2 - 1] = r01.m_low;
}
ZT_INLINE void vli_square(uint64_t *const p_result, uint64_t *const p_left)
{
uint128_t r01 = { 0, 0 };
uint64_t r2 = 0;
for (int k = 0; k < ECC_CURVE_DIGITS * 2 - 1; ++k) {
for (int i = (k < ECC_CURVE_DIGITS ? 0 : (k + 1) - ECC_CURVE_DIGITS); i <= k && i <= k - i; ++i) {
uint128_t l_product = mul_64_64(p_left[i], p_left[k - i]);
if (i < k - i) {
r2 += l_product.m_high >> 63;
l_product.m_high = (l_product.m_high << 1) | (l_product.m_low >> 63);
l_product.m_low <<= 1;
}
r01 = add_128_128(r01, l_product);
r2 += (r01.m_high < l_product.m_high);
}
p_result[k] = r01.m_low;
r01.m_low = r01.m_high;
r01.m_high = r2;
r2 = 0;
}
p_result[ECC_CURVE_DIGITS * 2 - 1] = r01.m_low;
}
#endif /* ZT_HAVE_UINT128 */
void vli_modAdd(
uint64_t *const p_result, uint64_t *const p_left, const uint64_t *const p_right, const uint64_t *const p_mod)
{
if ((vli_add(p_result, p_left, p_right) != 0ULL) || vli_cmp(p_result, p_mod) >= 0) {
vli_sub(p_result, p_result, p_mod);
}
}
void vli_modSub(
uint64_t *const p_result, uint64_t *const p_left, const uint64_t *const p_right, const uint64_t *const p_mod)
{
if (vli_sub(p_result, p_left, p_right) != 0ULL) {
vli_add(p_result, p_result, p_mod);
}
}
ZT_INLINE void omega_mult(uint64_t *const p_result, const uint64_t *const p_right)
{
uint64_t l_tmp[ECC_CURVE_DIGITS];
vli_set(p_result, p_right);
uint64_t l_carry = vli_lshift(l_tmp, p_right, 32);
p_result[1 + ECC_CURVE_DIGITS] = l_carry + vli_add(p_result + 1, p_result + 1, l_tmp);
p_result[2 + ECC_CURVE_DIGITS] = vli_add(p_result + 2, p_result + 2, p_right);
uint64_t l_diff = p_result[ECC_CURVE_DIGITS] - (l_carry + vli_sub(p_result, p_result, l_tmp));
if (l_diff > p_result[ECC_CURVE_DIGITS]) {
for (unsigned int i = 1 + ECC_CURVE_DIGITS;; ++i) {
if (likely(--p_result[i] != (uint64_t)-1)) {
break;
}
}
}
p_result[ECC_CURVE_DIGITS] = l_diff;
}
void vli_mmod_fast(uint64_t *const p_result, uint64_t *const p_product)
{
uint64_t l_tmp[2 * ECC_CURVE_DIGITS];
while (!vli_isZero(p_product + ECC_CURVE_DIGITS)) {
uint64_t l_carry = 0;
std::fill(l_tmp, l_tmp + (2 * ECC_CURVE_DIGITS), 0ULL);
omega_mult(l_tmp, p_product + ECC_CURVE_DIGITS);
vli_clear(p_product + ECC_CURVE_DIGITS);
for (unsigned int i = 0; i < ECC_CURVE_DIGITS + 3; ++i) {
uint64_t l_sum = p_product[i] + l_tmp[i] + l_carry;
if (l_sum != p_product[i]) {
l_carry = (l_sum < p_product[i]);
}
p_product[i] = l_sum;
}
}
while (vli_cmp(p_product, curve_p) > 0) {
vli_sub(p_product, p_product, curve_p);
}
vli_set(p_result, p_product);
}
ZT_INLINE void vli_modMult_fast(uint64_t *const p_result, uint64_t *const p_left, const uint64_t *const p_right)
{
uint64_t l_product[2 * ECC_CURVE_DIGITS];
vli_mult(l_product, p_left, p_right);
vli_mmod_fast(p_result, l_product);
}
ZT_INLINE void vli_modSquare_fast(uint64_t *const p_result, uint64_t *const p_left)
{
uint64_t l_product[2 * ECC_CURVE_DIGITS];
vli_square(l_product, p_left);
vli_mmod_fast(p_result, l_product);
}
void vli_modInv(uint64_t *const p_result, uint64_t *const p_input, const uint64_t *const p_mod)
{
if (likely(!vli_isZero(p_input))) {
uint64_t a[ECC_CURVE_DIGITS], b[ECC_CURVE_DIGITS], u[ECC_CURVE_DIGITS], v[ECC_CURVE_DIGITS], l_carry;
vli_set(a, p_input);
vli_set(b, p_mod);
u[0] = 1;
std::fill(u + 1, u + ECC_CURVE_DIGITS, 0ULL);
vli_clear(v);
int l_cmpResult;
while ((l_cmpResult = vli_cmp(a, b)) != 0) {
l_carry = 0;
if (vli_isEven(a)) {
vli_rshift1(a);
if (!vli_isEven(u)) {
l_carry = vli_add(u, u, p_mod);
}
vli_rshift1(u);
if (l_carry) {
u[ECC_CURVE_DIGITS - 1] |= 0x8000000000000000ULL;
}
}
else if (vli_isEven(b)) {
vli_rshift1(b);
if (!vli_isEven(v)) {
l_carry = vli_add(v, v, p_mod);
}
vli_rshift1(v);
if (l_carry) {
v[ECC_CURVE_DIGITS - 1] |= 0x8000000000000000ULL;
}
}
else if (l_cmpResult > 0) {
vli_sub(a, a, b);
vli_rshift1(a);
if (vli_cmp(u, v) < 0) {
vli_add(u, u, p_mod);
}
vli_sub(u, u, v);
if (!vli_isEven(u)) {
l_carry = vli_add(u, u, p_mod);
}
vli_rshift1(u);
if (l_carry) {
u[ECC_CURVE_DIGITS - 1] |= 0x8000000000000000ULL;
}
}
else {
vli_sub(b, b, a);
vli_rshift1(b);
if (vli_cmp(v, u) < 0) {
vli_add(v, v, p_mod);
}
vli_sub(v, v, u);
if (!vli_isEven(v)) {
l_carry = vli_add(v, v, p_mod);
}
vli_rshift1(v);
if (l_carry) {
v[ECC_CURVE_DIGITS - 1] |= 0x8000000000000000ULL;
}
}
}
vli_set(p_result, u);
}
else {
vli_clear(p_result);
}
}
ZT_INLINE bool EccPoint_isZero(const EccPoint *const p_point)
{
return (vli_isZero(p_point->x) && vli_isZero(p_point->y));
}
void EccPoint_double_jacobian(uint64_t *const X1, uint64_t *const Y1, uint64_t *const Z1)
{
if (likely(!vli_isZero(Z1))) {
uint64_t t4[ECC_CURVE_DIGITS], t5[ECC_CURVE_DIGITS];
vli_modSquare_fast(t4, Y1);
vli_modMult_fast(t5, X1, t4);
vli_modSquare_fast(t4, t4);
vli_modMult_fast(Y1, Y1, Z1);
vli_modSquare_fast(Z1, Z1);
vli_modAdd(X1, X1, Z1, curve_p);
vli_modAdd(Z1, Z1, Z1, curve_p);
vli_modSub(Z1, X1, Z1, curve_p);
vli_modMult_fast(X1, X1, Z1);
vli_modAdd(Z1, X1, X1, curve_p);
vli_modAdd(X1, X1, Z1, curve_p);
if (vli_testBit(X1, 0)) {
const uint64_t l_carry = vli_add(X1, X1, curve_p);
vli_rshift1(X1);
X1[ECC_CURVE_DIGITS - 1] |= l_carry << 63U;
}
else {
vli_rshift1(X1);
}
vli_modSquare_fast(Z1, X1);
vli_modSub(Z1, Z1, t5, curve_p);
vli_modSub(Z1, Z1, t5, curve_p);
vli_modSub(t5, t5, Z1, curve_p);
vli_modMult_fast(X1, X1, t5);
vli_modSub(t4, X1, t4, curve_p);
vli_set(X1, Z1);
vli_set(Z1, Y1);
vli_set(Y1, t4);
}
}
ZT_INLINE void apply_z(uint64_t *const X1, uint64_t *const Y1, uint64_t *const Z)
{
uint64_t t1[ECC_CURVE_DIGITS];
vli_modSquare_fast(t1, Z);
vli_modMult_fast(X1, X1, t1);
vli_modMult_fast(t1, t1, Z);
vli_modMult_fast(Y1, Y1, t1);
}
void XYcZ_initial_double(
uint64_t *const X1, uint64_t *const Y1, uint64_t *const X2, uint64_t *const Y2, uint64_t *const p_initialZ)
{
uint64_t z[ECC_CURVE_DIGITS];
vli_set(X2, X1);
vli_set(Y2, Y1);
z[0] = 1;
std::fill(z + 1, z + ECC_CURVE_DIGITS, 0ULL);
z[0] = 1;
if (p_initialZ) {
vli_set(z, p_initialZ);
}
apply_z(X1, Y1, z);
EccPoint_double_jacobian(X1, Y1, z);
apply_z(X2, Y2, z);
}
void XYcZ_add(uint64_t *const X1, uint64_t *const Y1, uint64_t *const X2, uint64_t *const Y2)
{
uint64_t t5[ECC_CURVE_DIGITS];
vli_modSub(t5, X2, X1, curve_p);
vli_modSquare_fast(t5, t5);
vli_modMult_fast(X1, X1, t5);
vli_modMult_fast(X2, X2, t5);
vli_modSub(Y2, Y2, Y1, curve_p);
vli_modSquare_fast(t5, Y2);
vli_modSub(t5, t5, X1, curve_p);
vli_modSub(t5, t5, X2, curve_p);
vli_modSub(X2, X2, X1, curve_p);
vli_modMult_fast(Y1, Y1, X2);
vli_modSub(X2, X1, t5, curve_p);
vli_modMult_fast(Y2, Y2, X2);
vli_modSub(Y2, Y2, Y1, curve_p);
vli_set(X2, t5);
}
void XYcZ_addC(uint64_t *const X1, uint64_t *const Y1, uint64_t *const X2, uint64_t *const Y2)
{
uint64_t t5[ECC_CURVE_DIGITS], t6[ECC_CURVE_DIGITS], t7[ECC_CURVE_DIGITS];
vli_modSub(t5, X2, X1, curve_p);
vli_modSquare_fast(t5, t5);
vli_modMult_fast(X1, X1, t5);
vli_modMult_fast(X2, X2, t5);
vli_modAdd(t5, Y2, Y1, curve_p);
vli_modSub(Y2, Y2, Y1, curve_p);
vli_modSub(t6, X2, X1, curve_p);
vli_modMult_fast(Y1, Y1, t6);
vli_modAdd(t6, X1, X2, curve_p);
vli_modSquare_fast(X2, Y2);
vli_modSub(X2, X2, t6, curve_p);
vli_modSub(t7, X1, X2, curve_p);
vli_modMult_fast(Y2, Y2, t7);
vli_modSub(Y2, Y2, Y1, curve_p);
vli_modSquare_fast(t7, t5);
vli_modSub(t7, t7, t6, curve_p);
vli_modSub(t6, t7, X1, curve_p);
vli_modMult_fast(t6, t6, t5);
vli_modSub(Y1, t6, Y1, curve_p);
vli_set(X1, t7);
}
void EccPoint_mult(
EccPoint *const p_result, const EccPoint *const p_point, uint64_t *const p_scalar, uint64_t *const p_initialZ)
{
uint64_t Rx[2][ECC_CURVE_DIGITS], Ry[2][ECC_CURVE_DIGITS], z[ECC_CURVE_DIGITS];
vli_set(Rx[1], p_point->x);
vli_set(Ry[1], p_point->y);
XYcZ_initial_double(Rx[1], Ry[1], Rx[0], Ry[0], p_initialZ);
for (int i = (int)vli_numBits(p_scalar) - 2; i > 0; --i) {
int nb = (int)!vli_testBit(p_scalar, i);
XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb]);
XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb]);
}
int nb = (int)!vli_testBit(p_scalar, 0);
XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb]);
vli_modSub(z, Rx[1], Rx[0], curve_p);
vli_modMult_fast(z, z, Ry[1 - nb]);
vli_modMult_fast(z, z, p_point->x);
vli_modInv(z, z, curve_p);
vli_modMult_fast(z, z, p_point->y);
vli_modMult_fast(z, z, Rx[1 - nb]);
XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb]);
apply_z(Rx[0], Ry[0], z);
vli_set(p_result->x, Rx[0]);
vli_set(p_result->y, Ry[0]);
}
ZT_INLINE void ECC_CURVE_BYTES2native(uint64_t p_native[ECC_CURVE_DIGITS], const uint8_t p_bytes[ECC_CURVE_BYTES])
{
for (unsigned int i = 0; i < ECC_CURVE_DIGITS; ++i) {
const uint8_t *const p_digit = p_bytes + 8 * (ECC_CURVE_DIGITS - 1 - i);
p_native[i] = ((uint64_t)p_digit[0] << 56) | ((uint64_t)p_digit[1] << 48) | ((uint64_t)p_digit[2] << 40)
| ((uint64_t)p_digit[3] << 32) | ((uint64_t)p_digit[4] << 24) | ((uint64_t)p_digit[5] << 16)
| ((uint64_t)p_digit[6] << 8) | (uint64_t)p_digit[7];
}
}
ZT_INLINE void ecc_native2bytes(uint8_t p_bytes[ECC_CURVE_BYTES], const uint64_t p_native[ECC_CURVE_DIGITS])
{
for (unsigned int i = 0; i < ECC_CURVE_DIGITS; ++i) {
uint8_t *p_digit = p_bytes + 8 * (ECC_CURVE_DIGITS - 1 - i);
p_digit[0] = p_native[i] >> 56;
p_digit[1] = p_native[i] >> 48;
p_digit[2] = p_native[i] >> 40;
p_digit[3] = p_native[i] >> 32;
p_digit[4] = p_native[i] >> 24;
p_digit[5] = p_native[i] >> 16;
p_digit[6] = p_native[i] >> 8;
p_digit[7] = p_native[i];
}
}
void mod_sqrt(uint64_t a[ECC_CURVE_DIGITS])
{
uint64_t l_result[ECC_CURVE_DIGITS] = { 1 }, p1[ECC_CURVE_DIGITS] = { 1 };
vli_add(p1, curve_p, p1);
for (int i = (int)vli_numBits(p1) - 1; i > 1; --i) {
vli_modSquare_fast(l_result, l_result);
if (vli_testBit(p1, i)) {
vli_modMult_fast(l_result, l_result, a);
}
}
vli_set(a, l_result);
}
void ecc_point_decompress(EccPoint *p_point, const uint8_t p_compressed[ECC_CURVE_BYTES + 1])
{
static const uint64_t _3[ECC_CURVE_DIGITS] = { 3 };
ECC_CURVE_BYTES2native(p_point->x, p_compressed + 1);
vli_modSquare_fast(p_point->y, p_point->x);
vli_modSub(p_point->y, p_point->y, _3, curve_p);
vli_modMult_fast(p_point->y, p_point->y, p_point->x);
vli_modAdd(p_point->y, p_point->y, curve_b, curve_p);
mod_sqrt(p_point->y);
if ((p_point->y[0] & 0x01) != (p_compressed[0] & 0x01)) {
vli_sub(p_point->y, curve_p, p_point->y);
}
}
ZT_INLINE bool ecc_make_key(uint8_t p_publicKey[ECC_CURVE_BYTES + 1], uint8_t p_privateKey[ECC_CURVE_BYTES])
{
uint64_t l_private[ECC_CURVE_DIGITS];
EccPoint l_public;
unsigned int l_tries = 0;
do {
if (unlikely(l_tries++ >= ECC_CREATE_KEY_MAX_ATTEMPTS))
return false;
Utils::getSecureRandom(l_private, ECC_CURVE_BYTES);
if (likely(!vli_isZero(l_private))) {
if (vli_cmp(curve_n, l_private) != 1)
vli_sub(l_private, l_private, curve_n);
EccPoint_mult(&l_public, &curve_G, l_private, NULL);
}
} while (EccPoint_isZero(&l_public));
ecc_native2bytes(p_privateKey, l_private);
ecc_native2bytes(p_publicKey + 1, l_public.x);
p_publicKey[0] = 0x02 + (l_public.y[0] & 0x01);
return true;
}
ZT_INLINE bool ecdh_shared_secret(
const uint8_t p_publicKey[ECC_CURVE_BYTES + 1], const uint8_t p_privateKey[ECC_CURVE_BYTES],
uint8_t p_secret[ECC_CURVE_BYTES])
{
EccPoint l_public;
uint64_t l_private[ECC_CURVE_DIGITS];
uint64_t l_random[ECC_CURVE_DIGITS];
Utils::getSecureRandom(l_random, ECC_CURVE_BYTES);
ecc_point_decompress(&l_public, p_publicKey);
ECC_CURVE_BYTES2native(l_private, p_privateKey);
EccPoint l_product;
EccPoint_mult(&l_product, &l_public, l_private, l_random);
ecc_native2bytes(p_secret, l_product.x);
return !EccPoint_isZero(&l_product);
}
void vli_modMult(uint64_t *const p_result, uint64_t *const p_left, uint64_t *const p_right, const uint64_t *const p_mod)
{
uint64_t l_product[2 * ECC_CURVE_DIGITS], l_modMultiple[2 * ECC_CURVE_DIGITS];
unsigned int l_digitShift, l_bitShift, l_productBits, l_modBits = vli_numBits(p_mod);
vli_mult(l_product, p_left, p_right);
l_productBits = vli_numBits(l_product + ECC_CURVE_DIGITS);
if (l_productBits) {
l_productBits += ECC_CURVE_DIGITS * 64;
}
else {
l_productBits = vli_numBits(l_product);
}
if (l_productBits < l_modBits) {
vli_set(p_result, l_product);
return;
}
vli_clear(l_modMultiple);
vli_clear(l_modMultiple + ECC_CURVE_DIGITS);
l_digitShift = (l_productBits - l_modBits) / 64;
l_bitShift = (l_productBits - l_modBits) % 64;
if (l_bitShift) {
l_modMultiple[l_digitShift + ECC_CURVE_DIGITS] = vli_lshift(l_modMultiple + l_digitShift, p_mod, l_bitShift);
}
else {
vli_set(l_modMultiple + l_digitShift, p_mod);
}
vli_clear(p_result);
p_result[0] = 1;
while (l_productBits > ECC_CURVE_DIGITS * 64 || vli_cmp(l_modMultiple, p_mod) >= 0) {
int l_cmp = vli_cmp(l_modMultiple + ECC_CURVE_DIGITS, l_product + ECC_CURVE_DIGITS);
if (l_cmp < 0 || (l_cmp == 0 && vli_cmp(l_modMultiple, l_product) <= 0)) {
if (vli_sub(l_product, l_product, l_modMultiple)) { /* borrow */
vli_sub(l_product + ECC_CURVE_DIGITS, l_product + ECC_CURVE_DIGITS, p_result);
}
vli_sub(l_product + ECC_CURVE_DIGITS, l_product + ECC_CURVE_DIGITS, l_modMultiple + ECC_CURVE_DIGITS);
}
uint64_t l_carry = (l_modMultiple[ECC_CURVE_DIGITS] & 0x01) << 63;
vli_rshift1(l_modMultiple + ECC_CURVE_DIGITS);
vli_rshift1(l_modMultiple);
l_modMultiple[ECC_CURVE_DIGITS - 1] |= l_carry;
--l_productBits;
}
vli_set(p_result, l_product);
}
ZT_INLINE bool ecdsa_sign(
const uint8_t p_privateKey[ECC_CURVE_BYTES], const uint8_t p_hash[ECC_CURVE_BYTES],
uint8_t p_signature[ECC_CURVE_BYTES * 2])
{
uint64_t k[ECC_CURVE_DIGITS], l_tmp[ECC_CURVE_DIGITS], l_s[ECC_CURVE_DIGITS];
EccPoint p;
unsigned int l_tries = 0;
do {
if (unlikely(l_tries++ >= ECC_CREATE_KEY_MAX_ATTEMPTS)) {
return false;
}
Utils::getSecureRandom(k, ECC_CURVE_BYTES);
if (likely(!vli_isZero(k))) {
if (vli_cmp(curve_n, k) != 1) {
vli_sub(k, k, curve_n);
}
EccPoint_mult(&p, &curve_G, k, NULL);
if (vli_cmp(curve_n, p.x) != 1) {
vli_sub(p.x, p.x, curve_n);
}
}
} while (vli_isZero(p.x));
ecc_native2bytes(p_signature, p.x);
ECC_CURVE_BYTES2native(l_tmp, p_privateKey);
vli_modMult(l_s, p.x, l_tmp, curve_n); /* s = r*d */
ECC_CURVE_BYTES2native(l_tmp, p_hash);
vli_modAdd(l_s, l_tmp, l_s, curve_n); /* s = e + r*d */
vli_modInv(k, k, curve_n); /* k = 1 / k */
vli_modMult(l_s, l_s, k, curve_n); /* s = (e + r*d) / k */
ecc_native2bytes(p_signature + ECC_CURVE_BYTES, l_s);
return true;
}
ZT_INLINE bool ecdsa_verify(
const uint8_t p_publicKey[ECC_CURVE_BYTES + 1], const uint8_t p_hash[ECC_CURVE_BYTES],
const uint8_t p_signature[ECC_CURVE_BYTES * 2])
{
uint64_t u1[ECC_CURVE_DIGITS], u2[ECC_CURVE_DIGITS], z[ECC_CURVE_DIGITS], rx[ECC_CURVE_DIGITS],
ry[ECC_CURVE_DIGITS], tx[ECC_CURVE_DIGITS], ty[ECC_CURVE_DIGITS], tz[ECC_CURVE_DIGITS], l_r[ECC_CURVE_DIGITS],
l_s[ECC_CURVE_DIGITS];
EccPoint l_public, l_sum;
ecc_point_decompress(&l_public, p_publicKey);
ECC_CURVE_BYTES2native(l_r, p_signature);
ECC_CURVE_BYTES2native(l_s, p_signature + ECC_CURVE_BYTES);
if (unlikely(vli_isZero(l_r) || vli_isZero(l_s) || (vli_cmp(curve_n, l_r) != 1) || (vli_cmp(curve_n, l_s) != 1))) {
return false;
}
vli_modInv(z, l_s, curve_n);
ECC_CURVE_BYTES2native(u1, p_hash);
vli_modMult(u1, u1, z, curve_n);
vli_modMult(u2, l_r, z, curve_n);
vli_set(l_sum.x, l_public.x);
vli_set(l_sum.y, l_public.y);
vli_set(tx, curve_G.x);
vli_set(ty, curve_G.y);
vli_modSub(z, l_sum.x, tx, curve_p);
XYcZ_add(tx, ty, l_sum.x, l_sum.y);
vli_modInv(z, z, curve_p);
apply_z(l_sum.x, l_sum.y, z);
const EccPoint *const l_points[4] = { NULL, &curve_G, &l_public, &l_sum };
unsigned int l_numBits = std::max(vli_numBits(u1), vli_numBits(u2));
const EccPoint *const l_point =
l_points[(!!vli_testBit(u1, l_numBits - 1)) | ((!!vli_testBit(u2, l_numBits - 1)) << 1)];
vli_set(rx, l_point->x);
vli_set(ry, l_point->y);
vli_clear(z);
z[0] = 1;
for (int i = l_numBits - 2; i >= 0; --i) {
EccPoint_double_jacobian(rx, ry, z);
int l_index = (!!vli_testBit(u1, i)) | ((!!vli_testBit(u2, i)) << 1);
const EccPoint *const l_point2 = l_points[l_index];
if (l_point2) {
vli_set(tx, l_point2->x);
vli_set(ty, l_point2->y);
apply_z(tx, ty, z);
vli_modSub(tz, rx, tx, curve_p);
XYcZ_add(tx, ty, rx, ry);
vli_modMult_fast(z, z, tz);
}
}
vli_modInv(z, z, curve_p);
apply_z(rx, ry, z);
if (vli_cmp(curve_n, rx) != 1) {
vli_sub(rx, rx, curve_n);
}
return (vli_cmp(rx, l_r) == 0);
}
} // anonymous namespace
void ECC384GenerateKey(uint8_t pub[ZT_ECC384_PUBLIC_KEY_SIZE], uint8_t priv[ZT_ECC384_PRIVATE_KEY_SIZE])
{
if (unlikely(!ecc_make_key(pub, priv))) {
fprintf(stderr, "FATAL: ecdsa_make_key() failed!" ZT_EOL_S);
abort();
}
}
void ECC384ECDSASign(
const uint8_t priv[ZT_ECC384_PRIVATE_KEY_SIZE], const uint8_t hash[ZT_ECC384_SIGNATURE_HASH_SIZE],
uint8_t sig[ZT_ECC384_SIGNATURE_SIZE])
{
if (unlikely(!ecdsa_sign(priv, hash, sig))) {
fprintf(stderr, "FATAL: ecdsa_sign() failed!" ZT_EOL_S);
abort();
}
}
bool ECC384ECDSAVerify(
const uint8_t pub[ZT_ECC384_PUBLIC_KEY_SIZE], const uint8_t hash[ZT_ECC384_SIGNATURE_HASH_SIZE],
const uint8_t sig[ZT_ECC384_SIGNATURE_SIZE])
{
return (ecdsa_verify(pub, hash, sig) != 0);
}
bool ECC384ECDH(
const uint8_t theirPub[ZT_ECC384_PUBLIC_KEY_SIZE], const uint8_t ourPriv[ZT_ECC384_PRIVATE_KEY_SIZE],
uint8_t secret[ZT_ECC384_SHARED_SECRET_SIZE])
{
return (ecdh_shared_secret(theirPub, ourPriv, secret) != 0);
}
} // namespace ZeroTier