RosettaCodeData/Task/Elliptic-Curve-Digital-Signature-Algorithm/C/elliptic-curve-digital-signature-algorithm.c
2023-07-01 13:44:08 -04:00

344 lines
6.6 KiB
C

/*
subject: Elliptic curve digital signature algorithm,
toy version for small modulus N.
tested : gcc 4.6.3, tcc 0.9.27
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// 64-bit integer type
typedef long long int dlong;
// rational ec point
typedef struct {
dlong x, y;
} epnt;
// elliptic curve parameters
typedef struct {
long a, b;
dlong N;
epnt G;
dlong r;
} curve;
// signature pair
typedef struct {
long a, b;
} pair;
// dlong for holding intermediate results,
// long variables in exgcd() for efficiency,
// maximum parameter size 2 * p.y (line 129)
// limits the modulus size to 30 bits.
// maximum modulus
const long mxN = 1073741789;
// max order G = mxN + 65536
const long mxr = 1073807325;
// symbolic infinity
const long inf = -2147483647;
// single global curve
curve e;
// point at infinity zerO
epnt zerO;
// impossible inverse mod N
int inverr;
// return mod(v^-1, u)
long exgcd (long v, long u)
{
register long q, t;
long r = 0, s = 1;
if (v < 0) v += u;
while (v) {
q = u / v;
t = u - q * v;
u = v; v = t;
t = r - q * s;
r = s; s = t;
}
if (u != 1) {
printf (" impossible inverse mod N, gcd = %d\n", u);
inverr = 1;
}
return r;
}
// return mod(a, N)
static inline dlong modn (dlong a)
{
a %= e.N;
if (a < 0) a += e.N;
return a;
}
// return mod(a, r)
dlong modr (dlong a)
{
a %= e.r;
if (a < 0) a += e.r;
return a;
}
// return the discriminant of E
long disc (void)
{
dlong c, a = e.a, b = e.b;
c = 4 * modn(a * modn(a * a));
return modn(-16 * (c + 27 * modn(b * b)));
}
// return 1 if P = zerO
int isO (epnt p)
{
return (p.x == inf) && (p.y == 0);
}
// return 1 if P is on curve E
int ison (epnt p)
{
long r, s;
if (! isO (p)) {
r = modn(e.b + p.x * modn(e.a + p.x * p.x));
s = modn(p.y * p.y);
}
return (r == s);
}
// full ec point addition
void padd (epnt *r, epnt p, epnt q)
{
dlong la, t;
if (isO(p)) {*r = q; return;}
if (isO(q)) {*r = p; return;}
if (p.x != q.x) { // R:= P + Q
t = p.y - q.y;
la = modn(t * exgcd(p.x - q.x, e.N));
}
else // P = Q, R := 2P
if ((p.y == q.y) && (p.y != 0)) {
t = modn(3 * modn(p.x * p.x) + e.a);
la = modn(t * exgcd (2 * p.y, e.N));
}
else
{*r = zerO; return;} // P = -Q, R := O
t = modn(la * la - p.x - q.x);
r->y = modn(la * (p.x - t) - p.y);
r->x = t; if (inverr) *r = zerO;
}
// R:= multiple kP
void pmul (epnt *r, epnt p, long k)
{
epnt s = zerO, q = p;
for (; k; k >>= 1) {
if (k & 1) padd(&s, s, q);
if (inverr) {s = zerO; break;}
padd(&q, q, q);
}
*r = s;
}
// print point P with prefix f
void pprint (char *f, epnt p)
{
dlong y = p.y;
if (isO (p))
printf ("%s (0)\n", f);
else {
if (y > e.N - y) y -= e.N;
printf ("%s (%lld, %lld)\n", f, p.x, y);
}
}
// initialize elliptic curve
int ellinit (long i[])
{
long a = i[0], b = i[1];
e.N = i[2]; inverr = 0;
if ((e.N < 5) || (e.N > mxN)) return 0;
e.a = modn(a);
e.b = modn(b);
e.G.x = modn(i[3]);
e.G.y = modn(i[4]);
e.r = i[5];
if ((e.r < 5) || (e.r > mxr)) return 0;
printf ("\nE: y^2 = x^3 + %dx + %d", a, b);
printf (" (mod %lld)\n", e.N);
pprint ("base point G", e.G);
printf ("order(G, E) = %lld\n", e.r);
return 1;
}
// pseudorandom number [0..1)
double rnd(void)
{
return rand() / ((double)RAND_MAX + 1);
}
// signature primitive
pair signature (dlong s, long f)
{
long c, d, u, u1;
pair sg;
epnt V;
printf ("\nsignature computation\n");
do {
do {
u = 1 + (long)(rnd() * (e.r - 1));
pmul (&V, e.G, u);
c = modr(V.x);
}
while (c == 0);
u1 = exgcd (u, e.r);
d = modr(u1 * (f + modr(s * c)));
}
while (d == 0);
printf ("one-time u = %d\n", u);
pprint ("V = uG", V);
sg.a = c; sg.b = d;
return sg;
}
// verification primitive
int verify (epnt W, long f, pair sg)
{
long c = sg.a, d = sg.b;
long t, c1, h1, h2;
dlong h;
epnt V, V2;
// domain check
t = (c > 0) && (c < e.r);
t &= (d > 0) && (d < e.r);
if (! t) return 0;
printf ("\nsignature verification\n");
h = exgcd (d, e.r);
h1 = modr(f * h);
h2 = modr(c * h);
printf ("h1,h2 = %d, %d\n", h1,h2);
pmul (&V, e.G, h1);
pmul (&V2, W, h2);
pprint ("h1G", V);
pprint ("h2W", V2);
padd (&V, V, V2);
pprint ("+ =", V);
if (isO (V)) return 0;
c1 = modr(V.x);
printf ("c' = %d\n", c1);
return (c1 == c);
}
// digital signature on message hash f, error bit d
void ec_dsa (long f, long d)
{
long i, s, t;
pair sg;
epnt W;
// parameter check
t = (disc() == 0);
t |= isO (e.G);
pmul (&W, e.G, e.r);
t |= ! isO (W);
t |= ! ison (e.G);
if (t) goto errmsg;
printf ("\nkey generation\n");
s = 1 + (long)(rnd() * (e.r - 1));
pmul (&W, e.G, s);
printf ("private key s = %d\n", s);
pprint ("public key W = sG", W);
// next highest power of 2 - 1
t = e.r;
for (i = 1; i < 32; i <<= 1)
t |= t >> i;
while (f > t) f >>= 1;
printf ("\naligned hash %x\n", f);
sg = signature (s, f);
if (inverr) goto errmsg;
printf ("signature c,d = %d, %d\n", sg.a, sg.b);
if (d > 0) {
while (d > t) d >>= 1;
f ^= d;
printf ("\ncorrupted hash %x\n", f);
}
t = verify (W, f, sg);
if (inverr) goto errmsg;
if (t)
printf ("Valid\n_____\n");
else
printf ("invalid\n_______\n");
return;
errmsg:
printf ("invalid parameter set\n");
printf ("_____________________\n");
}
void main (void)
{
typedef long eparm[6];
long d, f;
zerO.x = inf; zerO.y = 0;
srand(time(NULL));
// Test vectors: elliptic curve domain parameters,
// short Weierstrass model y^2 = x^3 + ax + b (mod N)
eparm *sp, sets[10] = {
// a, b, modulus N, base point G, order(G, E), cofactor
{355, 671, 1073741789, 13693, 10088, 1073807281},
{ 0, 7, 67096021, 6580, 779, 16769911}, // 4
{ -3, 1, 877073, 0, 1, 878159},
{ 0, 14, 22651, 63, 30, 151}, // 151
{ 3, 2, 5, 2, 1, 5},
// ecdsa may fail if...
// the base point is of composite order
{ 0, 7, 67096021, 2402, 6067, 33539822}, // 2
// the given order is a multiple of the true order
{ 0, 7, 67096021, 6580, 779, 67079644}, // 1
// the modulus is not prime (deceptive example)
{ 0, 7, 877069, 3, 97123, 877069},
// fails if the modulus divides the discriminant
{ 39, 387, 22651, 95, 27, 22651},
};
// Digital signature on message hash f,
// set d > 0 to simulate corrupted data
f = 0x789abcde; d = 0;
for (sp = sets; ; sp++) {
if (ellinit (*sp))
ec_dsa (f, d);
else
break;
}
}