#include <string.h>
#include "hlm_ed25519.h"
#include "hlm_sha512.h"
typedef int64_t gf[16];
static const gf GF0 = { 0 };
static const gf GF1 = { 1 };
static const gf ED_D = { 0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141,
0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7,
0xfe73, 0x2b6f, 0x6cee, 0x5203 };
static const gf ED_D2 = { 0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283,
0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e,
0xfce7, 0x56df, 0xd9dc, 0x2406 };
static const gf ED_X = { 0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525,
0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4,
0x53fe, 0xcd6e, 0x36d3, 0x2169 };
static const gf ED_Y = { 0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666,
0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666,
0x6666, 0x6666, 0x6666, 0x6666 };
static const gf ED_I = { 0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f,
0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d,
0xdf0b, 0x4fc1, 0x2480, 0x2b83 };
static const uint8_t ED_L[32] = {
0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10
};
static void fe_copy(gf r, const gf a)
{
int i;
for (i = 0; i < 16; i++) r[i] = a[i];
}
static void fe_carry(gf o)
{
int i;
int64_t c;
for (i = 0; i < 16; i++) {
o[i] += (int64_t)1 << 16;
c = o[i] >> 16;
o[(i + 1) * (i < 15)] += c - 1 + 37 * (c - 1) * (i == 15);
o[i] -= c * 65536;
}
}
static void fe_sel(gf p, gf q, int b)
{
int i;
int64_t t, mask = ~(int64_t)(b - 1);
for (i = 0; i < 16; i++) {
t = mask & (p[i] ^ q[i]);
p[i] ^= t;
q[i] ^= t;
}
}
static void fe_pack(uint8_t *o, const gf n)
{
int i, j, b;
gf m, t;
fe_copy(t, n);
fe_carry(t);
fe_carry(t);
fe_carry(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 = (int)((m[15] >> 16) & 1);
m[14] &= 0xffff;
fe_sel(t, m, 1 - b);
}
for (i = 0; i < 16; i++) {
o[2 * i] = (uint8_t)(t[i] & 0xff);
o[2 * i + 1] = (uint8_t)(t[i] >> 8);
}
}
static void fe_unpack(gf o, const uint8_t *n)
{
int i;
for (i = 0; i < 16; i++)
o[i] = (int64_t)n[2 * i] + ((int64_t)n[2 * i + 1] << 8);
o[15] &= 0x7fff;
}
static int fe_neq(const gf a, const gf b)
{
uint8_t x[32], y[32];
uint32_t d = 0;
int i;
fe_pack(x, a);
fe_pack(y, b);
for (i = 0; i < 32; i++) d |= (uint32_t)(x[i] ^ y[i]);
return (int)((d | (0u - d)) >> 31);
}
static int fe_parity(const gf a)
{
uint8_t d[32];
fe_pack(d, a);
return d[0] & 1;
}
static void fe_add(gf o, const gf a, const gf b)
{
int i;
for (i = 0; i < 16; i++) o[i] = a[i] + b[i];
}
static void fe_sub(gf o, const gf a, const gf b)
{
int i;
for (i = 0; i < 16; i++) o[i] = a[i] - b[i];
}
static void fe_mul(gf o, const gf a, const gf b)
{
int64_t t[31];
int i, j;
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];
fe_carry(o);
fe_carry(o);
}
static void fe_sq(gf o, const gf a)
{
fe_mul(o, a, a);
}
static void fe_inv(gf o, const gf i)
{
gf c;
int a;
fe_copy(c, i);
for (a = 253; a >= 0; a--) {
fe_sq(c, c);
if (a != 2 && a != 4) fe_mul(c, c, i);
}
fe_copy(o, c);
}
static void fe_pow2523(gf o, const gf i)
{
gf c;
int a;
fe_copy(c, i);
for (a = 250; a >= 0; a--) {
fe_sq(c, c);
if (a != 1) fe_mul(c, c, i);
}
fe_copy(o, c);
}
static void point_add(gf p[4], gf q[4])
{
gf a, b, c, d, t, e, f, g, h;
fe_sub(a, p[1], p[0]);
fe_sub(t, q[1], q[0]);
fe_mul(a, a, t);
fe_add(b, p[0], p[1]);
fe_add(t, q[0], q[1]);
fe_mul(b, b, t);
fe_mul(c, p[3], q[3]);
fe_mul(c, c, ED_D2);
fe_mul(d, p[2], q[2]);
fe_add(d, d, d);
fe_sub(e, b, a);
fe_sub(f, d, c);
fe_add(g, d, c);
fe_add(h, b, a);
fe_mul(p[0], e, f);
fe_mul(p[1], h, g);
fe_mul(p[2], g, f);
fe_mul(p[3], e, h);
}
static void point_cswap(gf p[4], gf q[4], int b)
{
int i;
for (i = 0; i < 4; i++) fe_sel(p[i], q[i], b);
}
static void point_pack(uint8_t *r, gf p[4])
{
gf tx, ty, zi;
fe_inv(zi, p[2]);
fe_mul(tx, p[0], zi);
fe_mul(ty, p[1], zi);
fe_pack(r, ty);
r[31] ^= (uint8_t)(fe_parity(tx) << 7);
}
static void point_scalarmult(gf p[4], gf q[4], const uint8_t *s)
{
int i;
fe_copy(p[0], GF0);
fe_copy(p[1], GF1);
fe_copy(p[2], GF1);
fe_copy(p[3], GF0);
for (i = 255; i >= 0; --i) {
int b = (s[i / 8] >> (i & 7)) & 1;
point_cswap(p, q, b);
point_add(q, p);
point_add(p, p);
point_cswap(p, q, b);
}
}
static void point_scalarbase(gf p[4], const uint8_t *s)
{
gf q[4];
fe_copy(q[0], ED_X);
fe_copy(q[1], ED_Y);
fe_copy(q[2], GF1);
fe_mul(q[3], ED_X, ED_Y);
point_scalarmult(p, q, s);
}
static int point_unpack_neg(gf r[4], const uint8_t p[32])
{
gf t, chk, num, den, den2, den4, den6;
fe_copy(r[2], GF1);
fe_unpack(r[1], p);
fe_sq(num, r[1]);
fe_mul(den, num, ED_D);
fe_sub(num, num, r[2]);
fe_add(den, r[2], den);
fe_sq(den2, den);
fe_sq(den4, den2);
fe_mul(den6, den4, den2);
fe_mul(t, den6, num);
fe_mul(t, t, den);
fe_pow2523(t, t);
fe_mul(t, t, num);
fe_mul(t, t, den);
fe_mul(t, t, den);
fe_mul(r[0], t, den);
fe_sq(chk, r[0]);
fe_mul(chk, chk, den);
if (fe_neq(chk, num)) fe_mul(r[0], r[0], ED_I);
fe_sq(chk, r[0]);
fe_mul(chk, chk, den);
if (fe_neq(chk, num)) return -1;
if (fe_parity(r[0]) == (p[31] >> 7)) fe_sub(r[0], GF0, r[0]);
fe_mul(r[3], r[0], r[1]);
return 0;
}
static void scalar_modL(uint8_t *r, int64_t x[64])
{
int64_t carry;
int i, j;
for (i = 63; i >= 32; --i) {
carry = 0;
for (j = i - 32; j < i - 12; ++j) {
x[j] += carry - 16 * x[i] * ED_L[j - (i - 32)];
carry = (x[j] + 128) >> 8;
x[j] -= carry * 256;
}
x[j] += carry;
x[i] = 0;
}
carry = 0;
for (j = 0; j < 32; j++) {
x[j] += carry - (x[31] >> 4) * ED_L[j];
carry = x[j] >> 8;
x[j] &= 255;
}
for (j = 0; j < 32; j++) x[j] -= carry * ED_L[j];
for (i = 0; i < 32; i++) {
x[i + 1] += x[i] >> 8;
r[i] = (uint8_t)(x[i] & 255);
}
}
static void scalar_reduce64(uint8_t r[64])
{
int64_t x[64];
int i;
for (i = 0; i < 64; i++) x[i] = r[i];
scalar_modL(r, x);
for (i = 32; i < 64; i++) r[i] = 0;
}
static int scalar_is_canonical(const uint8_t s[32])
{
int i;
for (i = 31; i >= 0; i--) {
if (s[i] < ED_L[i]) return 1;
if (s[i] > ED_L[i]) return 0;
}
return 0;
}
int hlm_ed25519_verify(const uint8_t pub[32],
const uint8_t *msg, size_t msg_len,
const uint8_t sig[64])
{
gf p[4], q[4];
hlm_sha512_ctx sc;
uint8_t h[64], rcheck[32];
uint32_t d = 0;
int i;
if (pub == NULL || sig == NULL || (msg == NULL && msg_len != 0))
return 0;
if (!scalar_is_canonical(sig + 32)) return 0;
if (point_unpack_neg(q, pub) != 0) return 0;
hlm_sha512_init(&sc);
hlm_sha512_update(&sc, sig, 32);
hlm_sha512_update(&sc, pub, 32);
if (msg_len > 0) hlm_sha512_update(&sc, msg, msg_len);
hlm_sha512_final(&sc, h);
scalar_reduce64(h);
point_scalarmult(p, q, h);
point_scalarbase(q, sig + 32);
point_add(p, q);
point_pack(rcheck, p);
for (i = 0; i < 32; i++) d |= (uint32_t)(rcheck[i] ^ sig[i]);
return d == 0;
}