jehanne/sys/src/lib/sec/port/ecc.c

613 lines
10 KiB
C

#include "os.h"
#include <mp.h>
#include <libsec.h>
#include <jehanne/ctype.h>
extern void jacobian_affine(mpint *p,
mpint *X, mpint *Y, mpint *Z);
extern void jacobian_dbl(mpint *p, mpint *a,
mpint *X1, mpint *Y1, mpint *Z1,
mpint *X3, mpint *Y3, mpint *Z3);
extern void jacobian_add(mpint *p, mpint *a,
mpint *X1, mpint *Y1, mpint *Z1,
mpint *X2, mpint *Y2, mpint *Z2,
mpint *X3, mpint *Y3, mpint *Z3);
void
ecassign(ECdomain *dom, ECpoint *a, ECpoint *b)
{
if((b->inf = a->inf) != 0)
return;
mpassign(a->x, b->x);
mpassign(a->y, b->y);
if(b->z != nil){
mpassign(a->z != nil ? a->z : mpone, b->z);
return;
}
if(a->z != nil){
b->z = mpcopy(a->z);
jacobian_affine(dom->p, b->x, b->y, b->z);
mpfree(b->z);
b->z = nil;
}
}
void
ecadd(ECdomain *dom, ECpoint *a, ECpoint *b, ECpoint *s)
{
if(a->inf && b->inf){
s->inf = 1;
return;
}
if(a->inf){
ecassign(dom, b, s);
return;
}
if(b->inf){
ecassign(dom, a, s);
return;
}
if(s->z == nil){
s->z = mpcopy(mpone);
ecadd(dom, a, b, s);
if(!s->inf)
jacobian_affine(dom->p, s->x, s->y, s->z);
mpfree(s->z);
s->z = nil;
return;
}
if(a == b)
jacobian_dbl(dom->p, dom->a,
a->x, a->y, a->z != nil ? a->z : mpone,
s->x, s->y, s->z);
else
jacobian_add(dom->p, dom->a,
a->x, a->y, a->z != nil ? a->z : mpone,
b->x, b->y, b->z != nil ? b->z : mpone,
s->x, s->y, s->z);
s->inf = mpcmp(s->z, mpzero) == 0;
}
void
ecmul(ECdomain *dom, ECpoint *a, mpint *k, ECpoint *s)
{
ECpoint ns, na;
mpint *l;
if(a->inf || mpcmp(k, mpzero) == 0){
s->inf = 1;
return;
}
ns.inf = 1;
ns.x = mpnew(0);
ns.y = mpnew(0);
ns.z = mpnew(0);
na.x = mpnew(0);
na.y = mpnew(0);
na.z = mpnew(0);
ecassign(dom, a, &na);
l = mpcopy(k);
l->sign = 1;
while(mpcmp(l, mpzero) != 0){
if(l->p[0] & 1)
ecadd(dom, &na, &ns, &ns);
ecadd(dom, &na, &na, &na);
mpright(l, 1, l);
}
if(k->sign < 0 && !ns.inf){
ns.y->sign = -1;
mpmod(ns.y, dom->p, ns.y);
}
ecassign(dom, &ns, s);
mpfree(ns.x);
mpfree(ns.y);
mpfree(ns.z);
mpfree(na.x);
mpfree(na.y);
mpfree(na.z);
mpfree(l);
}
int
ecverify(ECdomain *dom, ECpoint *a)
{
mpint *p, *q;
int r;
if(a->inf)
return 1;
assert(a->z == nil); /* need affine coordinates */
p = mpnew(0);
q = mpnew(0);
mpmodmul(a->y, a->y, dom->p, p);
mpmodmul(a->x, a->x, dom->p, q);
mpmodadd(q, dom->a, dom->p, q);
mpmodmul(q, a->x, dom->p, q);
mpmodadd(q, dom->b, dom->p, q);
r = mpcmp(p, q);
mpfree(p);
mpfree(q);
return r == 0;
}
int
ecpubverify(ECdomain *dom, ECpub *a)
{
ECpoint p;
int r;
if(a->inf)
return 0;
if(!ecverify(dom, a))
return 0;
p.x = mpnew(0);
p.y = mpnew(0);
p.z = mpnew(0);
ecmul(dom, a, dom->n, &p);
r = p.inf;
mpfree(p.x);
mpfree(p.y);
mpfree(p.z);
return r;
}
static void
fixnibble(uint8_t *a)
{
if(*a >= 'a')
*a -= 'a'-10;
else if(*a >= 'A')
*a -= 'A'-10;
else
*a -= '0';
}
static int
octet(char **s)
{
uint8_t c, d;
c = *(*s)++;
if(!isxdigit(c))
return -1;
d = *(*s)++;
if(!isxdigit(d))
return -1;
fixnibble(&c);
fixnibble(&d);
return (c << 4) | d;
}
static mpint*
halfpt(ECdomain *dom, char *s, char **rptr, mpint *out)
{
char *buf, *r;
int n;
mpint *ret;
n = ((mpsignif(dom->p)+7)/8)*2;
if(jehanne_strlen(s) < n)
return 0;
buf = jehanne_malloc(n+1);
buf[n] = 0;
jehanne_memcpy(buf, s, n);
ret = strtomp(buf, &r, 16, out);
*rptr = s + (r - buf);
jehanne_free(buf);
return ret;
}
static int
mpleg(mpint *a, mpint *b)
{
int r, k;
mpint *m, *n, *t;
r = 1;
m = mpcopy(a);
n = mpcopy(b);
for(;;){
if(mpcmp(m, n) > 0)
mpmod(m, n, m);
if(mpcmp(m, mpzero) == 0){
r = 0;
break;
}
if(mpcmp(m, mpone) == 0)
break;
k = mplowbits0(m);
if(k > 0){
if(k & 1)
switch(n->p[0] & 15){
case 3: case 5: case 11: case 13:
r = -r;
}
mpright(m, k, m);
}
if((n->p[0] & 3) == 3 && (m->p[0] & 3) == 3)
r = -r;
t = m;
m = n;
n = t;
}
mpfree(m);
mpfree(n);
return r;
}
static int
mpsqrt(mpint *n, mpint *p, mpint *r)
{
mpint *a, *t, *s, *xp, *xq, *yp, *yq, *zp, *zq, *N;
if(mpleg(n, p) == -1)
return 0;
a = mpnew(0);
t = mpnew(0);
s = mpnew(0);
N = mpnew(0);
xp = mpnew(0);
xq = mpnew(0);
yp = mpnew(0);
yq = mpnew(0);
zp = mpnew(0);
zq = mpnew(0);
for(;;){
for(;;){
mpnrand(p, genrandom, a);
if(mpcmp(a, mpzero) > 0)
break;
}
mpmul(a, a, t);
mpsub(t, n, t);
mpmod(t, p, t);
if(mpleg(t, p) == -1)
break;
}
mpadd(p, mpone, N);
mpright(N, 1, N);
mpmul(a, a, t);
mpsub(t, n, t);
mpassign(a, xp);
uitomp(1, xq);
uitomp(1, yp);
uitomp(0, yq);
while(mpcmp(N, mpzero) != 0){
if(N->p[0] & 1){
mpmul(xp, yp, zp);
mpmul(xq, yq, zq);
mpmul(zq, t, zq);
mpadd(zp, zq, zp);
mpmod(zp, p, zp);
mpmul(xp, yq, zq);
mpmul(xq, yp, s);
mpadd(zq, s, zq);
mpmod(zq, p, yq);
mpassign(zp, yp);
}
mpmul(xp, xp, zp);
mpmul(xq, xq, zq);
mpmul(zq, t, zq);
mpadd(zp, zq, zp);
mpmod(zp, p, zp);
mpmul(xp, xq, zq);
mpadd(zq, zq, zq);
mpmod(zq, p, xq);
mpassign(zp, xp);
mpright(N, 1, N);
}
if(mpcmp(yq, mpzero) != 0)
abort();
mpassign(yp, r);
mpfree(a);
mpfree(t);
mpfree(s);
mpfree(N);
mpfree(xp);
mpfree(xq);
mpfree(yp);
mpfree(yq);
mpfree(zp);
mpfree(zq);
return 1;
}
ECpoint*
strtoec(ECdomain *dom, char *s, char **rptr, ECpoint *ret)
{
int allocd, o;
mpint *r;
allocd = 0;
if(ret == nil){
allocd = 1;
ret = jehanne_mallocz(sizeof(*ret), 1);
if(ret == nil)
return nil;
ret->x = mpnew(0);
ret->y = mpnew(0);
}
ret->inf = 0;
o = 0;
switch(octet(&s)){
case 0:
ret->inf = 1;
break;
case 3:
o = 1;
case 2:
if(halfpt(dom, s, &s, ret->x) == nil)
goto err;
r = mpnew(0);
mpmul(ret->x, ret->x, r);
mpadd(r, dom->a, r);
mpmul(r, ret->x, r);
mpadd(r, dom->b, r);
if(!mpsqrt(r, dom->p, r)){
mpfree(r);
goto err;
}
if((r->p[0] & 1) != o)
mpsub(dom->p, r, r);
mpassign(r, ret->y);
mpfree(r);
if(!ecverify(dom, ret))
goto err;
break;
case 4:
if(halfpt(dom, s, &s, ret->x) == nil)
goto err;
if(halfpt(dom, s, &s, ret->y) == nil)
goto err;
if(!ecverify(dom, ret))
goto err;
break;
}
if(ret->z != nil && !ret->inf)
mpassign(mpone, ret->z);
return ret;
err:
if(rptr)
*rptr = s;
if(allocd){
mpfree(ret->x);
mpfree(ret->y);
jehanne_free(ret);
}
return nil;
}
ECpriv*
ecgen(ECdomain *dom, ECpriv *p)
{
if(p == nil){
p = jehanne_mallocz(sizeof(*p), 1);
if(p == nil)
return nil;
p->x = mpnew(0);
p->y = mpnew(0);
p->d = mpnew(0);
}
for(;;){
mpnrand(dom->n, genrandom, p->d);
if(mpcmp(p->d, mpzero) > 0)
break;
}
ecmul(dom, &dom->G, p->d, p);
return p;
}
void
ecdsasign(ECdomain *dom, ECpriv *priv, uint8_t *dig, int len, mpint *r, mpint *s)
{
ECpriv tmp;
mpint *E, *t;
tmp.x = mpnew(0);
tmp.y = mpnew(0);
tmp.z = nil;
tmp.d = mpnew(0);
E = betomp(dig, len, nil);
t = mpnew(0);
if(mpsignif(dom->n) < 8*len)
mpright(E, 8*len - mpsignif(dom->n), E);
for(;;){
ecgen(dom, &tmp);
mpmod(tmp.x, dom->n, r);
if(mpcmp(r, mpzero) == 0)
continue;
mpmul(r, priv->d, s);
mpadd(E, s, s);
mpinvert(tmp.d, dom->n, t);
mpmodmul(s, t, dom->n, s);
if(mpcmp(s, mpzero) != 0)
break;
}
mpfree(t);
mpfree(E);
mpfree(tmp.x);
mpfree(tmp.y);
mpfree(tmp.d);
}
int
ecdsaverify(ECdomain *dom, ECpub *pub, uint8_t *dig, int len, mpint *r, mpint *s)
{
mpint *E, *t, *u1, *u2;
ECpoint R, S;
int ret;
if(mpcmp(r, mpone) < 0 || mpcmp(s, mpone) < 0 || mpcmp(r, dom->n) >= 0 || mpcmp(r, dom->n) >= 0)
return 0;
E = betomp(dig, len, nil);
if(mpsignif(dom->n) < 8*len)
mpright(E, 8*len - mpsignif(dom->n), E);
t = mpnew(0);
u1 = mpnew(0);
u2 = mpnew(0);
R.x = mpnew(0);
R.y = mpnew(0);
R.z = mpnew(0);
S.x = mpnew(0);
S.y = mpnew(0);
S.z = mpnew(0);
mpinvert(s, dom->n, t);
mpmodmul(E, t, dom->n, u1);
mpmodmul(r, t, dom->n, u2);
ecmul(dom, &dom->G, u1, &R);
ecmul(dom, pub, u2, &S);
ecadd(dom, &R, &S, &R);
ret = 0;
if(!R.inf){
jacobian_affine(dom->p, R.x, R.y, R.z);
mpmod(R.x, dom->n, t);
ret = mpcmp(r, t) == 0;
}
mpfree(E);
mpfree(t);
mpfree(u1);
mpfree(u2);
mpfree(R.x);
mpfree(R.y);
mpfree(R.z);
mpfree(S.x);
mpfree(S.y);
mpfree(S.z);
return ret;
}
static char *code = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
void
base58enc(uint8_t *src, char *dst, int len)
{
mpint *n, *r, *b;
char *sdst, t;
sdst = dst;
n = betomp(src, len, nil);
b = uitomp(58, nil);
r = mpnew(0);
while(mpcmp(n, mpzero) != 0){
mpdiv(n, b, n, r);
*dst++ = code[mptoui(r)];
}
for(; *src == 0; src++)
*dst++ = code[0];
dst--;
while(dst > sdst){
t = *sdst;
*sdst++ = *dst;
*dst-- = t;
}
}
int
base58dec(char *src, uint8_t *dst, int len)
{
mpint *n, *b, *r;
char *t;
n = mpnew(0);
r = mpnew(0);
b = uitomp(58, nil);
for(; *src; src++){
t = jehanne_strchr(code, *src);
if(t == nil){
mpfree(n);
mpfree(r);
mpfree(b);
jehanne_werrstr("invalid base58 char");
return -1;
}
uitomp(t - code, r);
mpmul(n, b, n);
mpadd(n, r, n);
}
mptober(n, dst, len);
mpfree(n);
mpfree(r);
mpfree(b);
return 0;
}
void
ecdominit(ECdomain *dom, void (*init)(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h))
{
jehanne_memset(dom, 0, sizeof(*dom));
dom->p = mpnew(0);
dom->a = mpnew(0);
dom->b = mpnew(0);
dom->G.x = mpnew(0);
dom->G.y = mpnew(0);
dom->n = mpnew(0);
dom->h = mpnew(0);
if(init){
(*init)(dom->p, dom->a, dom->b, dom->G.x, dom->G.y, dom->n, dom->h);
dom->p = mpfield(dom->p);
}
}
void
ecdomfree(ECdomain *dom)
{
mpfree(dom->p);
mpfree(dom->a);
mpfree(dom->b);
mpfree(dom->G.x);
mpfree(dom->G.y);
mpfree(dom->n);
mpfree(dom->h);
jehanne_memset(dom, 0, sizeof(*dom));
}
int
ecencodepub(ECdomain *dom, ECpub *pub, uint8_t *data, int len)
{
int n;
n = (mpsignif(dom->p)+7)/8;
if(len < 1 + 2*n)
return 0;
len = 1 + 2*n;
data[0] = 0x04;
mptober(pub->x, data+1, n);
mptober(pub->y, data+1+n, n);
return len;
}
ECpub*
ecdecodepub(ECdomain *dom, uint8_t *data, int len)
{
ECpub *pub;
int n;
n = (mpsignif(dom->p)+7)/8;
if(len != 1 + 2*n || data[0] != 0x04)
return nil;
pub = jehanne_mallocz(sizeof(*pub), 1);
if(pub == nil)
return nil;
pub->x = betomp(data+1, n, nil);
pub->y = betomp(data+1+n, n, nil);
if(!ecpubverify(dom, pub)){
ecpubfree(pub);
pub = nil;
}
return pub;
}
void
ecpubfree(ECpub *p)
{
if(p == nil)
return;
mpfree(p->x);
mpfree(p->y);
jehanne_free(p);
}