--- sys/include/libsec.h
+++ sys/include/libsec.h
@@ -128,6 +128,7 @@ int ccpoly_decrypt(uchar *dat, ulong ndat, uchar *aad,
void ccpoly_encrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], Chachastate *cs);
int ccpoly_decrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], Chachastate *cs);
+
/*
* Curve25519 (RFC 7748)
*/
@@ -137,6 +138,53 @@ int curve25519_dh_finish(uchar x[32], uchar y[32], uch
int curve25519_dh_finish(uchar x[32], uchar y[32], uchar z[32]);
/*
+ * Short-Weierstrass elliptic curves (secp256r1, secp384r1) + ECDSA.
+ * Used for TLS_ECDHE_ECDSA_* cipher suites and for validating certificate
+ * chains signed with ECDSA.
+ */
+typedef struct ECpoint{
+ int inf;
+ mpint *x;
+ mpint *y;
+ mpint *z; /* nil when using affine coordinates */
+} ECpoint;
+
+typedef ECpoint ECpub;
+typedef struct ECpriv{
+ ECpoint; /* anonymous -- same fields as ECpub */
+ mpint *d;
+} ECpriv;
+
+typedef struct ECdomain{
+ mpint *p;
+ mpint *a;
+ mpint *b;
+ ECpoint G;
+ mpint *n;
+ mpint *h;
+} ECdomain;
+
+void ecdominit(ECdomain*, void (*init)(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h));
+void ecdomfree(ECdomain*);
+
+void ecassign(ECdomain*, ECpoint *old, ECpoint *new);
+void ecadd(ECdomain*, ECpoint *a, ECpoint *b, ECpoint *s);
+void ecmul(ECdomain*, ECpoint *a, mpint *k, ECpoint *s);
+ECpriv* ecgen(ECdomain*, ECpriv*);
+int ecverify(ECdomain*, ECpoint*);
+int ecpubverify(ECdomain*, ECpub*);
+void ecdsasign(ECdomain*, ECpriv*, uchar*, int, mpint*, mpint*);
+int ecdsaverify(ECdomain*, ECpub*, uchar*, int, mpint*, mpint*);
+
+ECpub* ecdecodepub(ECdomain *dom, uchar*, int);
+int ecencodepub(ECdomain *dom, ECpub*, uchar*, int);
+void ecpubfree(ECpub*);
+
+/* curve parameter initializers -- compatible with ecdominit's init callback */
+void secp256r1(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h);
+void secp384r1(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h);
+
+/*
* DES definitions
*/
@@ -352,6 +400,8 @@ char* rsapkcs1verify(RSApub *pk, int sigalg, uchar *m
char* X509verify(uchar *cert, int ncert, RSApub *pk);
void X509dump(uchar *cert, int ncert);
char* rsapkcs1verify(RSApub *pk, int sigalg, uchar *msg, ulong msglen, uchar *sig, int siglen);
+ECpub* X509toECpub(uchar *cert, int ncert, char *name, int nname, ECdomain *dom);
+char* X509ecdsaverifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, ECdomain *dom, ECpub *pub);
/*
* elgamal
--- sys/src/libsec/port/ecc.c
+++ sys/src/libsec/port/ecc.c
@@ -0,0 +1,344 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <libsec.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;
+}
+
+ECpriv*
+ecgen(ECdomain *dom, ECpriv *p)
+{
+ if(p == nil){
+ p = 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, uchar *dig, int len, mpint *r, mpint *s)
+{
+ ECpriv tmp;
+ mpint *E, *t;
+
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.x = mpnew(0);
+ tmp.y = mpnew(0);
+ tmp.d = mpnew(0);
+ /* tmp.z stays nil per ecmul; memset cleared tmp.inf */
+ 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, uchar *dig, int len, mpint *r, mpint *s)
+{
+ mpint *E, *t, *u1, *u2;
+ ECpoint R, S;
+ int ret;
+
+ /* FIPS 186-4 Section 4.7 step 1: r, s in [1, n-1] */
+ if(mpcmp(r, mpone) < 0 || mpcmp(s, mpone) < 0
+ || mpcmp(r, dom->n) >= 0 || mpcmp(s, 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;
+}
+
+void
+ecdominit(ECdomain *dom,
+ void (*init)(mpint *p, mpint *a, mpint *b,
+ mpint *x, mpint *y, mpint *n, mpint *h))
+{
+ 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)
+{
+ if(dom == nil || dom->p == nil)
+ return; /* idempotent: no-op on zeroed or already-freed domain */
+ mpfree(dom->p);
+ mpfree(dom->a);
+ mpfree(dom->b);
+ mpfree(dom->G.x);
+ mpfree(dom->G.y);
+ mpfree(dom->n);
+ mpfree(dom->h);
+ memset(dom, 0, sizeof(*dom));
+}
+
+int
+ecencodepub(ECdomain *dom, ECpub *pub, uchar *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, uchar *data, int len)
+{
+ ECpub *pub;
+ int n;
+
+ n = (mpsignif(dom->p)+7)/8;
+ if(len != 1 + 2*n || data[0] != 0x04)
+ return nil;
+ pub = mallocz(sizeof(*pub), 1);
+ if(pub == nil)
+ return nil;
+ pub->x = betomp(data+1, n, nil);
+ pub->y = betomp(data+1+n, n, nil);
+ /* RFC 5480 Section 2.2: x, y must be in [0, p-1] */
+ if(mpcmp(pub->x, dom->p) >= 0 || mpcmp(pub->y, dom->p) >= 0){
+ ecpubfree(pub);
+ werrstr("ECPubKey coordinate out of field range");
+ return nil;
+ }
+ if(!ecpubverify(dom, pub)){
+ ecpubfree(pub);
+ pub = nil;
+ }
+ return pub;
+}
+
+void
+ecpubfree(ECpub *p)
+{
+ if(p == nil)
+ return;
+ mpfree(p->x); p->x = nil;
+ mpfree(p->y); p->y = nil;
+ mpfree(p->z); p->z = nil;
+ free(p);
+}
--- sys/src/libsec/port/jacobian.mp
+++ sys/src/libsec/port/jacobian.mp
@@ -0,0 +1,60 @@
+# Elliptic curve group operations in jacobian coordinates:
+# x=X/Z^2
+# x=Y/Z^3
+
+jacobian_new(x,y,z, X,Y,Z) {
+ X = x;
+ Y = y;
+ Z = z;
+}
+jacobian_inf(X,Y,Z) {
+ X,Y,Z = jacobian_new(0,1,0);
+}
+jacobian_affine(p, X,Y,Z) mod(p) {
+ if(Z != 0) {
+ ZZ = Z^2;
+ ZZZ = ZZ*Z;
+ X = X / ZZ;
+ Y = Y / ZZZ;
+ Z = 1;
+ }
+}
+jacobian_dbl(p,a, X1,Y1,Z1, X3,Y3,Z3) mod(p) {
+ if(Y1 == 0) {
+ X3,Y3,Z3 = jacobian_inf();
+ } else {
+ XX = X1^2;
+ YY = Y1^2;
+ YYYY = YY^2;
+ ZZ = Z1^2;
+ S = 2*((X1+YY)^2-XX-YYYY);
+ M = 3*XX+a*ZZ^2;
+ Z3 = (Y1+Z1)^2-YY-ZZ;
+ X3 = M^2-2*S;
+ Y3 = M*(S-X3)-8*YYYY;
+ }
+}
+jacobian_add(p,a, X1,Y1,Z1, X2,Y2,Z2, X3,Y3,Z3) mod(p) {
+ Z1Z1 = Z1^2;
+ Z2Z2 = Z2^2;
+ U1 = X1*Z2Z2;
+ U2 = X2*Z1Z1;
+ S1 = Y1*Z2*Z2Z2;
+ S2 = Y2*Z1*Z1Z1;
+ if(U1 == U2) {
+ if(S1 != S2) {
+ X3,Y3,Z3 = jacobian_inf();
+ } else {
+ X3,Y3,Z3 = jacobian_dbl(p,a, X1,Y1,Z1);
+ }
+ } else {
+ H = U2-U1;
+ I = (2*H)^2;
+ J = H*I;
+ r = 2*(S2-S1);
+ V = U1*I;
+ X3 = r^2-J-2*V;
+ Y3 = r*(V-X3)-2*S1*J;
+ Z3 = ((Z1+Z2)^2-Z1Z1-Z2Z2)*H;
+ }
+}
--- sys/src/libsec/port/mkfile
+++ sys/src/libsec/port/mkfile
@@ -5,6 +5,7 @@ CFILES = des.c desmodes.c desECB.c desCBC.c des3ECB.c
CFILES = des.c desmodes.c desECB.c desCBC.c des3ECB.c des3CBC.c\
aes.c aes_gcm.c blowfish.c chacha.c \
curve25519.c curve25519_dh.c\
+ ecc.c jacobian.c secp256r1.c secp384r1.c\
hmac.c md5.c md5block.c md4.c sha1.c sha1block.c\
sha2_64.c sha2_128.c sha2block64.c sha2block128.c\
sha1pickle.c md5pickle.c\
@@ -24,6 +25,11 @@ CFILES = des.c desmodes.c desECB.c desCBC.c des3ECB.c
ccpoly.c\
tsmemcmp.c\
+# jacobian.c is generated from jacobian.mp by mpc(1) at build time.
+# secp256r1.c and secp384r1.c are hand-written (9legacy's mpc mis-parses
+# large hex literals and the 2^N power operator used in 9front's .mp).
+CLEANFILES=jacobian.c
+
ALLOFILES=${CFILES:%.c=%.$O}
# cull things in the per-machine directories from this list
@@ -38,6 +44,13 @@ UPDATE=mkfile\
$CFILES\
</sys/src/cmd/mksyslib
+
+# generate .c from .mp (mpc) for curve/jacobian parameter tables
+%.c:D: %.mp
+ echo '#include <u.h>' > $target
+ echo '#include <libc.h>' >> $target
+ echo '#include <mp.h>' >> $target
+ mpc $prereq >> $target
$O.rsatest: rsatest.$O
$LD -o $target $prereq
--- sys/src/libsec/port/secp256r1.c
+++ sys/src/libsec/port/secp256r1.c
@@ -0,0 +1,27 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+
+/*
+ * NIST P-256 (secp256r1) curve parameters.
+ *
+ * Hand-written to avoid 9legacy's mpc(1) mis-parsing large hex literals
+ * and the "2^N" power operator -- the 9front .mp source relies on both,
+ * and 9legacy's mpc silently produces garbage for them.
+ *
+ * Reference: SEC 2 v2.0 Section 2.4.2 / NIST SP 800-186 Section 3.2.1.3.
+ */
+void
+secp256r1(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h)
+{
+ /* p = 2^256 - 2^224 + 2^192 + 2^96 - 1 */
+ strtomp("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", nil, 16, p);
+ /* a = p - 3 */
+ uitomp(3, a);
+ mpsub(p, a, a);
+ strtomp("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", nil, 16, b);
+ strtomp("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", nil, 16, x);
+ strtomp("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", nil, 16, y);
+ strtomp("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", nil, 16, n);
+ uitomp(1, h);
+}
--- sys/src/libsec/port/secp384r1.c
+++ sys/src/libsec/port/secp384r1.c
@@ -0,0 +1,24 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+
+/*
+ * NIST P-384 (secp384r1) curve parameters. See secp256r1.c for the
+ * reason this file is hand-written rather than generated from a .mp.
+ *
+ * Reference: SEC 2 v2.0 Section 2.5.1 / NIST SP 800-186 Section 3.2.1.4.
+ */
+void
+secp384r1(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h)
+{
+ /* p = 2^384 - 2^128 - 2^96 + 2^32 - 1 */
+ strtomp("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", nil, 16, p);
+ /* a = p - 3 */
+ uitomp(3, a);
+ mpsub(p, a, a);
+ strtomp("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", nil, 16, b);
+ strtomp("AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", nil, 16, x);
+ strtomp("3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", nil, 16, y);
+ strtomp("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", nil, 16, n);
+ uitomp(1, h);
+}
--- sys/src/libsec/port/x509.c
+++ sys/src/libsec/port/x509.c
@@ -1569,6 +1569,7 @@ typedef struct CertX509 {
char* validity_end;
char* subject;
int publickey_alg;
+ int curve; /* CURVE_* index when publickey_alg == ALG_ecPublicKey, else -1 */
Bytes* publickey;
int signature_alg;
Bytes* signature;
@@ -1592,6 +1593,11 @@ enum {
ALG_sha384,
ALG_sha512,
ALG_sha224,
+ ALG_ecPublicKey,
+ ALG_sha1WithECDSA,
+ ALG_sha256WithECDSA,
+ ALG_sha384WithECDSA,
+ ALG_sha512WithECDSA,
NUMALGS
};
typedef struct Ints9 {
@@ -1614,6 +1620,11 @@ static Ints9 oid_sha224 ={9, 2, 16, 840, 1, 101, 3, 4,
static Ints9 oid_sha384 ={9, 2, 16, 840, 1, 101, 3, 4, 2, 2 };
static Ints9 oid_sha512 ={9, 2, 16, 840, 1, 101, 3, 4, 2, 3 };
static Ints9 oid_sha224 ={9, 2, 16, 840, 1, 101, 3, 4, 2, 4 };
+static Ints9 oid_ecPublicKey = {6, 1, 2, 840, 10045, 2, 1 };
+static Ints9 oid_sha1WithECDSA = {6, 1, 2, 840, 10045, 4, 1 };
+static Ints9 oid_sha256WithECDSA = {7, 1, 2, 840, 10045, 4, 3, 2 };
+static Ints9 oid_sha384WithECDSA = {7, 1, 2, 840, 10045, 4, 3, 3 };
+static Ints9 oid_sha512WithECDSA = {7, 1, 2, 840, 10045, 4, 3, 4 };
static Ints *alg_oid_tab[NUMALGS+1] = {
(Ints*)&oid_rsaEncryption,
(Ints*)&oid_md2WithRSAEncryption,
@@ -1631,10 +1642,41 @@ static Ints *alg_oid_tab[NUMALGS+1] = {
(Ints*)&oid_sha384,
(Ints*)&oid_sha512,
(Ints*)&oid_sha224,
+ (Ints*)&oid_ecPublicKey,
+ (Ints*)&oid_sha1WithECDSA,
+ (Ints*)&oid_sha256WithECDSA,
+ (Ints*)&oid_sha384WithECDSA,
+ (Ints*)&oid_sha512WithECDSA,
nil
};
-static DigestFun digestalg[NUMALGS+1] = { md5, md5, md5, md5, sha1, sha1, sha2_256, sha2_384, sha2_512, sha2_224, md5, sha1, sha2_256, sha2_384, sha2_512, sha2_224, nil };
+static DigestFun digestalg[NUMALGS+1] = {
+ md5, md5, md5, md5, sha1, sha1, sha2_256, sha2_384, sha2_512, sha2_224,
+ md5, sha1, sha2_256, sha2_384, sha2_512, sha2_224,
+ nil, /* ecPublicKey -- key alg, no digest */
+ sha1, sha2_256, sha2_384, sha2_512, /* ECDSA signature algs */
+ nil
+};
+/* Named curve OIDs (parameters of an ecPublicKey SubjectPublicKeyInfo) */
+static Ints9 oid_secp256r1 = {7, 1, 2, 840, 10045, 3, 1, 7};
+static Ints9 oid_secp384r1 = {5, 1, 3, 132, 0, 34};
+
+enum {
+ CURVE_secp256r1,
+ CURVE_secp384r1,
+ NUMCURVES
+};
+static Ints *namedcurves_oid_tab[NUMCURVES+1] = {
+ (Ints*)&oid_secp256r1,
+ (Ints*)&oid_secp384r1,
+ nil
+};
+/* init callback per curve, indexed by CURVE_* */
+static void (*namedcurves[NUMCURVES])(mpint*, mpint*, mpint*, mpint*, mpint*, mpint*, mpint*) = {
+ secp256r1,
+ secp384r1,
+};
+
static void
freecert(CertX509* c)
{
@@ -1729,6 +1771,24 @@ parse_alg(Elem* e)
return oid_lookup(oid, alg_oid_tab);
}
+/*
+ * For an ecPublicKey AlgorithmIdentifier, the parameters slot holds the
+ * namedCurve OID. Returns the CURVE_* index, or -1 if the curve isn't
+ * supported (callers should treat this as a bad certificate).
+ */
+static int
+parse_ec_curve(Elem *e)
+{
+ Elist *el;
+ Ints *oid;
+
+ if(!is_seq(e, &el) || el == nil || el->tl == nil)
+ return -1;
+ if(!is_oid(&el->tl->hd, &oid))
+ return -1;
+ return oid_lookup(oid, namedcurves_oid_tab);
+}
+
static CertX509*
decode_cert(Bytes* a)
{
@@ -1763,6 +1823,7 @@ decode_cert(Bytes* a)
c->validity_end = nil;
c->subject = nil;
c->publickey_alg = -1;
+ c->curve = -1;
c->publickey = nil;
c->signature_alg = -1;
c->signature = nil;
@@ -1839,6 +1900,11 @@ decode_cert(Bytes* a)
c->publickey_alg = parse_alg(&elpubkey->hd);
if(c->publickey_alg < 0)
goto errret;
+ if(c->publickey_alg == ALG_ecPublicKey){
+ c->curve = parse_ec_curve(&elpubkey->hd);
+ if(c->curve < 0)
+ goto errret;
+ }
if(!is_bitstring(&elpubkey->tl->hd, &bits))
goto errret;
if(bits->unusedbits != 0)
@@ -2211,6 +2277,80 @@ X509toRSApub(uchar *cert, int ncert, char *name, int n
pk = decode_rsapubkey(c->publickey);
freecert(c);
return pk;
+}
+
+/*
+ * Extract the EC public key from an X.509 certificate. On success,
+ * *dom is initialised with the cert's named-curve parameters (caller
+ * must ecdomfree). Returns nil if the cert isn't ECC-keyed or uses
+ * an unsupported curve.
+ */
+ECpub*
+X509toECpub(uchar *cert, int ncert, char *name, int nname, ECdomain *dom)
+{
+ char *e;
+ Bytes *b;
+ CertX509 *c;
+ ECpub *pub;
+
+ b = makebytes(cert, ncert);
+ c = decode_cert(b);
+ freebytes(b);
+ if(c == nil)
+ return nil;
+ if(name != nil && c->subject != nil){
+ e = strchr(c->subject, ',');
+ if(e != nil)
+ *e = 0;
+ strncpy(name, c->subject, nname);
+ }
+ pub = nil;
+ if(c->publickey_alg == ALG_ecPublicKey
+ && c->curve >= 0 && c->curve < NUMCURVES){
+ ecdominit(dom, namedcurves[c->curve]);
+ pub = ecdecodepub(dom, c->publickey->data, c->publickey->len);
+ if(pub == nil)
+ ecdomfree(dom);
+ }
+ freecert(c);
+ return pub;
+}
+
+/*
+ * Verify an ECDSA signature. sig is the ASN.1
+ * SEQUENCE { r INTEGER, s INTEGER }
+ * as carried in TLS ServerKeyExchange or X.509 certificate signatures.
+ * edigest is the hash of the signed bytes (caller-computed).
+ * Returns nil on success, error string on failure.
+ */
+char*
+X509ecdsaverifydigest(uchar *sig, int siglen,
+ uchar *edigest, int edigestlen, ECdomain *dom, ECpub *pub)
+{
+ Elem e;
+ Elist *el;
+ mpint *r, *s;
+ char *err;
+
+ r = s = nil;
+ err = "bad signature";
+ if(decode(sig, siglen, &e) != ASN_OK)
+ goto end;
+ if(!is_seq(&e, &el) || elistlen(el) != 2)
+ goto end;
+ r = asn1mpint(&el->hd);
+ if(r == nil)
+ goto end;
+ s = asn1mpint(&el->tl->hd);
+ if(s == nil)
+ goto end;
+ if(ecdsaverify(dom, pub, edigest, edigestlen, r, s))
+ err = nil;
+end:
+ freevalfields(&e.val);
+ mpfree(s);
+ mpfree(r);
+ return err;
}
int
|