Plan 9 from Bell Labs’s /usr/web/sources/contrib/mospak/tls-1.2/libsec-ecc-ecdsa-primitives.diff

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


--- 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

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.