/* subject: Elliptic curve digital signature algorithm, toy version for small modulus N. tested : gcc 4.6.3, tcc 0.9.27 */ #include #include #include // 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; } }