diff options
author | rsc <devnull@localhost> | 2004-03-21 14:04:56 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2004-03-21 14:04:56 +0000 |
commit | 0fc65b37a1e7585ca2347bf61dcb8bc3a6b146a4 (patch) | |
tree | dd9189a823998f494082adb769451f12be056566 /src/libsec/port/x509.c | |
parent | 768206abfcf505fb034a0151bf263bc0b1f2380c (diff) | |
download | plan9port-0fc65b37a1e7585ca2347bf61dcb8bc3a6b146a4.tar.gz plan9port-0fc65b37a1e7585ca2347bf61dcb8bc3a6b146a4.tar.bz2 plan9port-0fc65b37a1e7585ca2347bf61dcb8bc3a6b146a4.zip |
Add most of libsec.
Diffstat (limited to 'src/libsec/port/x509.c')
-rw-r--r-- | src/libsec/port/x509.c | 2524 |
1 files changed, 2524 insertions, 0 deletions
diff --git a/src/libsec/port/x509.c b/src/libsec/port/x509.c new file mode 100644 index 00000000..7aa0dd70 --- /dev/null +++ b/src/libsec/port/x509.c @@ -0,0 +1,2524 @@ +#include <u.h> +#include <libc.h> +#include <mp.h> +#include <libsec.h> + +typedef DigestState*(*DigestFun)(uchar*,ulong,uchar*,DigestState*); + +/* ANSI offsetof, backwards. */ +#define OFFSETOF(a, b) offsetof(b, a) + +/*=============================================================*/ +/* general ASN1 declarations and parsing + * + * For now, this is used only for extracting the key from an + * X509 certificate, so the entire collection is hidden. But + * someday we should probably make the functions visible and + * give them their own man page. + */ +typedef struct Elem Elem; +typedef struct Tag Tag; +typedef struct Value Value; +typedef struct Bytes Bytes; +typedef struct Ints Ints; +typedef struct Bits Bits; +typedef struct Elist Elist; + +/* tag classes */ +#define Universal 0 +#define Context 0x80 + +/* universal tags */ +#define BOOLEAN 1 +#define INTEGER 2 +#define BIT_STRING 3 +#define OCTET_STRING 4 +#define NULLTAG 5 +#define OBJECT_ID 6 +#define ObjectDescriptor 7 +#define EXTERNAL 8 +#define REAL 9 +#define ENUMERATED 10 +#define EMBEDDED_PDV 11 +#define SEQUENCE 16 /* also SEQUENCE OF */ +#define SETOF 17 /* also SETOF OF */ +#define NumericString 18 +#define PrintableString 19 +#define TeletexString 20 +#define VideotexString 21 +#define IA5String 22 +#define UTCTime 23 +#define GeneralizedTime 24 +#define GraphicString 25 +#define VisibleString 26 +#define GeneralString 27 +#define UniversalString 28 +#define BMPString 30 + +struct Bytes { + int len; + uchar data[1]; +}; + +struct Ints { + int len; + int data[1]; +}; + +struct Bits { + int len; /* number of bytes */ + int unusedbits; /* unused bits in last byte */ + uchar data[1]; /* most-significant bit first */ +}; + +struct Tag { + int class; + int num; +}; + +enum { VBool, VInt, VOctets, VBigInt, VReal, VOther, + VBitString, VNull, VEOC, VObjId, VString, VSeq, VSet }; +struct Value { + int tag; /* VBool, etc. */ + union { + int boolval; + int intval; + Bytes* octetsval; + Bytes* bigintval; + Bytes* realval; /* undecoded; hardly ever used */ + Bytes* otherval; + Bits* bitstringval; + Ints* objidval; + char* stringval; + Elist* seqval; + Elist* setval; + } u; /* (Don't use anonymous unions, for ease of porting) */ +}; + +struct Elem { + Tag tag; + Value val; +}; + +struct Elist { + Elist* tl; + Elem hd; +}; + +/* decoding errors */ +enum { ASN_OK, ASN_ESHORT, ASN_ETOOBIG, ASN_EVALLEN, + ASN_ECONSTR, ASN_EPRIM, ASN_EINVAL, ASN_EUNIMPL }; + + +/* here are the functions to consider making extern someday */ +static Bytes* newbytes(int len); +static Bytes* makebytes(uchar* buf, int len); +static void freebytes(Bytes* b); +static Bytes* catbytes(Bytes* b1, Bytes* b2); +static Ints* newints(int len); +static Ints* makeints(int* buf, int len); +static void freeints(Ints* b); +static Bits* newbits(int len); +static Bits* makebits(uchar* buf, int len, int unusedbits); +static void freebits(Bits* b); +static Elist* mkel(Elem e, Elist* tail); +static void freeelist(Elist* el); +static int elistlen(Elist* el); +static int is_seq(Elem* pe, Elist** pseq); +static int is_set(Elem* pe, Elist** pset); +static int is_int(Elem* pe, int* pint); +static int is_bigint(Elem* pe, Bytes** pbigint); +static int is_bitstring(Elem* pe, Bits** pbits); +static int is_octetstring(Elem* pe, Bytes** poctets); +static int is_oid(Elem* pe, Ints** poid); +static int is_string(Elem* pe, char** pstring); +static int is_time(Elem* pe, char** ptime); +static int decode(uchar* a, int alen, Elem* pelem); +static int decode_seq(uchar* a, int alen, Elist** pelist); +static int decode_value(uchar* a, int alen, int kind, int isconstr, Value* pval); +static int encode(Elem e, Bytes** pbytes); +static int oid_lookup(Ints* o, Ints** tab); +static void freevalfields(Value* v); +static mpint *asn1mpint(Elem *e); + + + +#define TAG_MASK 0x1F +#define CONSTR_MASK 0x20 +#define CLASS_MASK 0xC0 +#define MAXOBJIDLEN 20 + +static int ber_decode(uchar** pp, uchar* pend, Elem* pelem); +static int tag_decode(uchar** pp, uchar* pend, Tag* ptag, int* pisconstr); +static int length_decode(uchar** pp, uchar* pend, int* plength); +static int value_decode(uchar** pp, uchar* pend, int length, int kind, int isconstr, Value* pval); +static int int_decode(uchar** pp, uchar* pend, int count, int unsgned, int* pint); +static int uint7_decode(uchar** pp, uchar* pend, int* pint); +static int octet_decode(uchar** pp, uchar* pend, int length, int isconstr, Bytes** pbytes); +static int seq_decode(uchar** pp, uchar* pend, int length, int isconstr, Elist** pelist); +static int enc(uchar** pp, Elem e, int lenonly); +static int val_enc(uchar** pp, Elem e, int *pconstr, int lenonly); +static void uint7_enc(uchar** pp, int num, int lenonly); +static void int_enc(uchar** pp, int num, int unsgned, int lenonly); + +static void * +emalloc(int n) +{ + void *p; + if(n==0) + n=1; + p = malloc(n); + if(p == nil){ + exits("out of memory"); + } + memset(p, 0, n); + return p; +} + +static char* +estrdup(char *s) +{ + char *d, *d0; + + if(!s) + return 0; + d = d0 = emalloc(strlen(s)+1); + while(*d++ = *s++) + ; + return d0; +} + + +/* + * Decode a[0..len] as a BER encoding of an ASN1 type. + * The return value is one of ASN_OK, etc. + * Depending on the error, the returned elem may or may not + * be nil. + */ +static int +decode(uchar* a, int alen, Elem* pelem) +{ + uchar* p = a; + + return ber_decode(&p, &a[alen], pelem); +} + +/* + * Like decode, but continue decoding after first element + * of array ends. + */ +static int +decode_seq(uchar* a, int alen, Elist** pelist) +{ + uchar* p = a; + + return seq_decode(&p, &a[alen], -1, 1, pelist); +} + +/* + * Decode the whole array as a BER encoding of an ASN1 value, + * (i.e., the part after the tag and length). + * Assume the value is encoded as universal tag "kind". + * The constr arg is 1 if the value is constructed, 0 if primitive. + * If there's an error, the return string will contain the error. + * Depending on the error, the returned value may or may not + * be nil. + */ +static int +decode_value(uchar* a, int alen, int kind, int isconstr, Value* pval) +{ + uchar* p = a; + + return value_decode(&p, &a[alen], alen, kind, isconstr, pval); +} + +/* + * All of the following decoding routines take arguments: + * uchar **pp; + * uchar *pend; + * Where parsing is supposed to start at **pp, and when parsing + * is done, *pp is updated to point at next char to be parsed. + * The pend pointer is just past end of string; an error should + * be returned parsing hasn't finished by then. + * + * The returned int is ASN_OK if all went fine, else ASN_ESHORT, etc. + * The remaining argument(s) are pointers to where parsed entity goes. + */ + +/* Decode an ASN1 'Elem' (tag, length, value) */ +static int +ber_decode(uchar** pp, uchar* pend, Elem* pelem) +{ + int err; + int isconstr; + int length; + Tag tag; + Value val; + + err = tag_decode(pp, pend, &tag, &isconstr); + if(err == ASN_OK) { + err = length_decode(pp, pend, &length); + if(err == ASN_OK) { + if(tag.class == Universal) + err = value_decode(pp, pend, length, tag.num, isconstr, &val); + else + err = value_decode(pp, pend, length, OCTET_STRING, 0, &val); + if(err == ASN_OK) { + pelem->tag = tag; + pelem->val = val; + } + } + } + return err; +} + +/* Decode a tag field */ +static int +tag_decode(uchar** pp, uchar* pend, Tag* ptag, int* pisconstr) +{ + int err; + int v; + uchar* p; + + err = ASN_OK; + p = *pp; + if(pend-p >= 2) { + v = *p++; + ptag->class = v&CLASS_MASK; + if(v&CONSTR_MASK) + *pisconstr = 1; + else + *pisconstr = 0; + v &= TAG_MASK; + if(v == TAG_MASK) + err = uint7_decode(&p, pend, &v); + ptag->num = v; + } + else + err = ASN_ESHORT; + *pp = p; + return err; +} + +/* Decode a length field */ +static int +length_decode(uchar** pp, uchar* pend, int* plength) +{ + int err; + int num; + int v; + uchar* p; + + err = ASN_OK; + num = 0; + p = *pp; + if(p < pend) { + v = *p++; + if(v&0x80) + err = int_decode(&p, pend, v&0x7F, 1, &num); + else + num = v; + } + else + err = ASN_ESHORT; + *pp = p; + *plength = num; + return err; +} + +/* Decode a value field */ +static int +value_decode(uchar** pp, uchar* pend, int length, int kind, int isconstr, Value* pval) +{ + int err; + Bytes* va; + int num; + int bitsunused; + int subids[MAXOBJIDLEN]; + int isubid; + Elist* vl; + uchar* p; + uchar* pe; + + err = ASN_OK; + p = *pp; + if(length == -1) { /* "indefinite" length spec */ + if(!isconstr) + err = ASN_EINVAL; + } + else if(p + length > pend) + err = ASN_EVALLEN; + if(err != ASN_OK) + return err; + + switch(kind) { + case 0: + /* marker for end of indefinite constructions */ + if(length == 0) + pval->tag = VNull; + else + err = ASN_EINVAL; + break; + + case BOOLEAN: + if(isconstr) + err = ASN_ECONSTR; + else if(length != 1) + err = ASN_EVALLEN; + else { + pval->tag = VBool; + pval->u.boolval = (*p++ != 0); + } + break; + + case INTEGER: + case ENUMERATED: + if(isconstr) + err = ASN_ECONSTR; + else if(length <= 4) { + err = int_decode(&p, pend, length, 0, &num); + if(err == ASN_OK) { + pval->tag = VInt; + pval->u.intval = num; + } + } + else { + pval->tag = VBigInt; + pval->u.bigintval = makebytes(p, length); + p += length; + } + break; + + case BIT_STRING: + pval->tag = VBitString; + if(isconstr) { + if(length == -1 && p + 2 <= pend && *p == 0 && *(p+1) ==0) { + pval->u.bitstringval = makebits(0, 0, 0); + p += 2; + } + else + /* TODO: recurse and concat results */ + err = ASN_EUNIMPL; + } + else { + if(length < 2) { + if(length == 1 && *p == 0) { + pval->u.bitstringval = makebits(0, 0, 0); + p++; + } + else + err = ASN_EINVAL; + } + else { + bitsunused = *p; + if(bitsunused > 7) + err = ASN_EINVAL; + else if(length > 0x0FFFFFFF) + err = ASN_ETOOBIG; + else { + pval->u.bitstringval = makebits(p+1, length-1, bitsunused); + p += length; + } + } + } + break; + + case OCTET_STRING: + case ObjectDescriptor: + err = octet_decode(&p, pend, length, isconstr, &va); + if(err == ASN_OK) { + pval->tag = VOctets; + pval->u.octetsval = va; + } + break; + + case NULLTAG: + if(isconstr) + err = ASN_ECONSTR; + else if(length != 0) + err = ASN_EVALLEN; + else + pval->tag = VNull; + break; + + case OBJECT_ID: + if(isconstr) + err = ASN_ECONSTR; + else if(length == 0) + err = ASN_EVALLEN; + else { + isubid = 0; + pe = p+length; + while(p < pe && isubid < MAXOBJIDLEN) { + err = uint7_decode(&p, pend, &num); + if(err != ASN_OK) + break; + if(isubid == 0) { + subids[isubid++] = num / 40; + subids[isubid++] = num % 40; + } + else + subids[isubid++] = num; + } + if(err == ASN_OK) { + if(p != pe) + err = ASN_EVALLEN; + else { + pval->tag = VObjId; + pval->u.objidval = makeints(subids, isubid); + } + } + } + break; + + case EXTERNAL: + case EMBEDDED_PDV: + /* TODO: parse this internally */ + if(p+length > pend) + err = ASN_EVALLEN; + else { + pval->tag = VOther; + pval->u.otherval = makebytes(p, length); + p += length; + } + break; + + case REAL: + /* Let the application decode */ + if(isconstr) + err = ASN_ECONSTR; + else if(p+length > pend) + err = ASN_EVALLEN; + else { + pval->tag = VReal; + pval->u.realval = makebytes(p, length); + p += length; + } + break; + + case SEQUENCE: + err = seq_decode(&p, pend, length, isconstr, &vl); + if(err == ASN_OK) { + pval->tag = VSeq ; + pval->u.seqval = vl; + } + break; + + case SETOF: + err = seq_decode(&p, pend, length, isconstr, &vl); + if(err == ASN_OK) { + pval->tag = VSet; + pval->u.setval = vl; + } + break; + + case NumericString: + case PrintableString: + case TeletexString: + case VideotexString: + case IA5String: + case UTCTime: + case GeneralizedTime: + case GraphicString: + case VisibleString: + case GeneralString: + case UniversalString: + case BMPString: + /* TODO: figure out when character set conversion is necessary */ + err = octet_decode(&p, pend, length, isconstr, &va); + if(err == ASN_OK) { + pval->tag = VString; + pval->u.stringval = (char*)emalloc(va->len+1); + memmove(pval->u.stringval, va->data, va->len); + pval->u.stringval[va->len] = 0; + free(va); + } + break; + + default: + if(p+length > pend) + err = ASN_EVALLEN; + else { + pval->tag = VOther; + pval->u.otherval = makebytes(p, length); + p += length; + } + break; + } + *pp = p; + return err; +} + +/* + * Decode an int in format where count bytes are + * concatenated to form value. + * Although ASN1 allows any size integer, we return + * an error if the result doesn't fit in a 32-bit int. + * If unsgned is not set, make sure to propagate sign bit. + */ +static int +int_decode(uchar** pp, uchar* pend, int count, int unsgned, int* pint) +{ + int err; + int num; + uchar* p; + + p = *pp; + err = ASN_OK; + num = 0; + if(p+count <= pend) { + if((count > 4) || (unsgned && count == 4 && (*p&0x80))) + err = ASN_ETOOBIG; + else { + if(!unsgned && count > 0 && count < 4 && (*p&0x80)) + num = -1; // set all bits, initially + while(count--) + num = (num << 8)|(*p++); + } + } + else + err = ASN_ESHORT; + *pint = num; + *pp = p; + return err; +} + +/* + * Decode an unsigned int in format where each + * byte except last has high bit set, and remaining + * seven bits of each byte are concatenated to form value. + * Although ASN1 allows any size integer, we return + * an error if the result doesn't fit in a 32 bit int. + */ +static int +uint7_decode(uchar** pp, uchar* pend, int* pint) +{ + int err; + int num; + int more; + int v; + uchar* p; + + p = *pp; + err = ASN_OK; + num = 0; + more = 1; + while(more && p < pend) { + v = *p++; + if(num&0x7F000000) { + err = ASN_ETOOBIG; + break; + } + num <<= 7; + more = v&0x80; + num |= (v&0x7F); + } + if(p == pend) + err = ASN_ESHORT; + *pint = num; + *pp = p; + return err; +} + +/* + * Decode an octet string, recursively if isconstr. + * We've already checked that length==-1 implies isconstr==1, + * and otherwise that specified length fits within (*pp..pend) + */ +static int +octet_decode(uchar** pp, uchar* pend, int length, int isconstr, Bytes** pbytes) +{ + int err; + uchar* p; + Bytes* ans; + Bytes* newans; + uchar* pstart; + uchar* pold; + Elem elem; + + err = ASN_OK; + p = *pp; + ans = nil; + if(length >= 0 && !isconstr) { + ans = makebytes(p, length); + p += length; + } + else { + /* constructed, either definite or indefinite length */ + pstart = p; + for(;;) { + if(length >= 0 && p >= pstart + length) { + if(p != pstart + length) + err = ASN_EVALLEN; + break; + } + pold = p; + err = ber_decode(&p, pend, &elem); + if(err != ASN_OK) + break; + switch(elem.val.tag) { + case VOctets: + newans = catbytes(ans, elem.val.u.octetsval); + freebytes(ans); + ans = newans; + break; + + case VEOC: + if(length != -1) { + p = pold; + err = ASN_EINVAL; + } + goto cloop_done; + + default: + p = pold; + err = ASN_EINVAL; + goto cloop_done; + } + } +cloop_done: + ; + } + *pp = p; + *pbytes = ans; + return err; +} + +/* + * Decode a sequence or set. + * We've already checked that length==-1 implies isconstr==1, + * and otherwise that specified length fits within (*p..pend) + */ +static int +seq_decode(uchar** pp, uchar* pend, int length, int isconstr, Elist** pelist) +{ + int err; + uchar* p; + uchar* pstart; + uchar* pold; + Elist* ans; + Elem elem; + Elist* lve; + Elist* lveold; + + err = ASN_OK; + ans = nil; + p = *pp; + if(!isconstr) + err = ASN_EPRIM; + else { + /* constructed, either definite or indefinite length */ + lve = nil; + pstart = p; + for(;;) { + if(length >= 0 && p >= pstart + length) { + if(p != pstart + length) + err = ASN_EVALLEN; + break; + } + pold = p; + err = ber_decode(&p, pend, &elem); + if(err != ASN_OK) + break; + if(elem.val.tag == VEOC) { + if(length != -1) { + p = pold; + err = ASN_EINVAL; + } + break; + } + else + lve = mkel(elem, lve); + } + if(err == ASN_OK) { + /* reverse back to original order */ + while(lve != nil) { + lveold = lve; + lve = lve->tl; + lveold->tl = ans; + ans = lveold; + } + } + } + *pp = p; + *pelist = ans; + return err; +} + +/* + * Encode e by BER rules, putting answer in *pbytes. + * This is done by first calling enc with lenonly==1 + * to get the length of the needed buffer, + * then allocating the buffer and using enc again to fill it up. + */ +static int +encode(Elem e, Bytes** pbytes) +{ + uchar* p; + Bytes* ans; + int err; + uchar uc; + + p = &uc; + err = enc(&p, e, 1); + if(err == ASN_OK) { + ans = newbytes(p-&uc); + p = ans->data; + err = enc(&p, e, 0); + *pbytes = ans; + } + return err; +} + +/* + * The various enc functions take a pointer to a pointer + * into a buffer, and encode their entity starting there, + * updating the pointer afterwards. + * If lenonly is 1, only the pointer update is done, + * allowing enc to be called first to calculate the needed + * buffer length. + * If lenonly is 0, it is assumed that the answer will fit. + */ + +static int +enc(uchar** pp, Elem e, int lenonly) +{ + int err; + int vlen; + int constr; + Tag tag; + int v; + int ilen; + uchar* p; + uchar* psave; + + p = *pp; + err = val_enc(&p, e, &constr, 1); + if(err != ASN_OK) + return err; + vlen = p - *pp; + p = *pp; + tag = e.tag; + v = tag.class|constr; + if(tag.num < 31) { + if(!lenonly) + *p = (v|tag.num); + p++; + } + else { + if(!lenonly) + *p = (v|31); + p++; + if(tag.num < 0) + return ASN_EINVAL; + uint7_enc(&p, tag.num, lenonly); + } + if(vlen < 0x80) { + if(!lenonly) + *p = vlen; + p++; + } + else { + psave = p; + int_enc(&p, vlen, 1, 1); + ilen = p-psave; + p = psave; + if(!lenonly) { + *p++ = (0x80 | ilen); + int_enc(&p, vlen, 1, 0); + } + else + p += 1 + ilen; + } + if(!lenonly) + val_enc(&p, e, &constr, 0); + else + p += vlen; + *pp = p; + return err; +} + +static int +val_enc(uchar** pp, Elem e, int *pconstr, int lenonly) +{ + int err; + uchar* p; + int kind; + int cl; + int v; + Bytes* bb = nil; + Bits* bits; + Ints* oid; + int k; + Elist* el; + char* s; + + p = *pp; + err = ASN_OK; + kind = e.tag.num; + cl = e.tag.class; + *pconstr = 0; + if(cl != Universal) { + switch(e.val.tag) { + case VBool: + kind = BOOLEAN; + break; + case VInt: + kind = INTEGER; + break; + case VBigInt: + kind = INTEGER; + break; + case VOctets: + kind = OCTET_STRING; + break; + case VReal: + kind = REAL; + break; + case VOther: + kind = OCTET_STRING; + break; + case VBitString: + kind = BIT_STRING; + break; + case VNull: + kind = NULLTAG; + break; + case VObjId: + kind = OBJECT_ID; + break; + case VString: + kind = UniversalString; + break; + case VSeq: + kind = SEQUENCE; + break; + case VSet: + kind = SETOF; + break; + } + } + switch(kind) { + case BOOLEAN: + if(is_int(&e, &v)) { + if(v != 0) + v = 255; + int_enc(&p, v, 1, lenonly); + } + else + err = ASN_EINVAL; + break; + + case INTEGER: + case ENUMERATED: + if(is_int(&e, &v)) + int_enc(&p, v, 0, lenonly); + else { + if(is_bigint(&e, &bb)) { + if(!lenonly) + memmove(p, bb->data, bb->len); + p += bb->len; + } + else + err = ASN_EINVAL; + } + break; + + case BIT_STRING: + if(is_bitstring(&e, &bits)) { + if(bits->len == 0) { + if(!lenonly) + *p = 0; + p++; + } + else { + v = bits->unusedbits; + if(v < 0 || v > 7) + err = ASN_EINVAL; + else { + if(!lenonly) { + *p = v; + memmove(p+1, bits->data, bits->len); + } + p += 1 + bits->len; + } + } + } + else + err = ASN_EINVAL; + break; + + case OCTET_STRING: + case ObjectDescriptor: + case EXTERNAL: + case REAL: + case EMBEDDED_PDV: + bb = nil; + switch(e.val.tag) { + case VOctets: + bb = e.val.u.octetsval; + break; + case VReal: + bb = e.val.u.realval; + break; + case VOther: + bb = e.val.u.otherval; + break; + } + if(bb != nil) { + if(!lenonly) + memmove(p, bb->data, bb->len); + p += bb->len; + } + else + err = ASN_EINVAL; + break; + + case NULLTAG: + break; + + case OBJECT_ID: + if(is_oid(&e, &oid)) { + for(k = 0; k < oid->len; k++) { + v = oid->data[k]; + if(k == 0) { + v *= 40; + if(oid->len > 1) + v += oid->data[++k]; + } + uint7_enc(&p, v, lenonly); + } + } + else + err = ASN_EINVAL; + break; + + case SEQUENCE: + case SETOF: + el = nil; + if(e.val.tag == VSeq) + el = e.val.u.seqval; + else if(e.val.tag == VSet) + el = e.val.u.setval; + else + err = ASN_EINVAL; + if(el != nil) { + *pconstr = CONSTR_MASK; + for(; el != nil; el = el->tl) { + err = enc(&p, el->hd, lenonly); + if(err != ASN_OK) + break; + } + } + break; + + case NumericString: + case PrintableString: + case TeletexString: + case VideotexString: + case IA5String: + case UTCTime: + case GeneralizedTime: + case GraphicString: + case VisibleString: + case GeneralString: + case UniversalString: + case BMPString: + if(e.val.tag == VString) { + s = e.val.u.stringval; + if(s != nil) { + v = strlen(s); + if(!lenonly) + memmove(p, s, v); + p += v; + } + } + else + err = ASN_EINVAL; + break; + + default: + err = ASN_EINVAL; + } + *pp = p; + return err; +} + +/* + * Encode num as unsigned 7 bit values with top bit 1 on all bytes + * except last, only putting in bytes if !lenonly. + */ +static void +uint7_enc(uchar** pp, int num, int lenonly) +{ + int n; + int v; + int k; + uchar* p; + + p = *pp; + n = 1; + v = num >> 7; + while(v > 0) { + v >>= 7; + n++; + } + if(lenonly) + p += n; + else { + for(k = (n - 1)*7; k > 0; k -= 7) + *p++= ((num >> k)|0x80); + *p++ = (num&0x7F); + } + *pp = p; +} + +/* + * Encode num as unsigned or signed integer, + * only putting in bytes if !lenonly. + * Encoding is length followed by bytes to concatenate. + */ +static void +int_enc(uchar** pp, int num, int unsgned, int lenonly) +{ + int v; + int n; + int prevv; + int k; + uchar* p; + + p = *pp; + v = num; + if(v < 0) + v = -(v + 1); + n = 1; + prevv = v; + v >>= 8; + while(v > 0) { + prevv = v; + v >>= 8; + n++; + } + if(!unsgned && (prevv&0x80)) + n++; + if(lenonly) + p += n; + else { + for(k = (n - 1)*8; k >= 0; k -= 8) + *p++ = (num >> k); + } + *pp = p; +} + +static int +ints_eq(Ints* a, Ints* b) +{ + int alen; + int i; + + alen = a->len; + if(alen != b->len) + return 0; + for(i = 0; i < alen; i++) + if(a->data[i] != b->data[i]) + return 0; + return 1; +} + +/* + * Look up o in tab (which must have nil entry to terminate). + * Return index of matching entry, or -1 if none. + */ +static int +oid_lookup(Ints* o, Ints** tab) +{ + int i; + + for(i = 0; tab[i] != nil; i++) + if(ints_eq(o, tab[i])) + return i; + return -1; +} + +/* + * Return true if *pe is a SEQUENCE, and set *pseq to + * the value of the sequence if so. + */ +static int +is_seq(Elem* pe, Elist** pseq) +{ + if(pe->tag.class == Universal && pe->tag.num == SEQUENCE && pe->val.tag == VSeq) { + *pseq = pe->val.u.seqval; + return 1; + } + return 0; +} + +static int +is_set(Elem* pe, Elist** pset) +{ + if(pe->tag.class == Universal && pe->tag.num == SETOF && pe->val.tag == VSet) { + *pset = pe->val.u.setval; + return 1; + } + return 0; +} + +static int +is_int(Elem* pe, int* pint) +{ + if(pe->tag.class == Universal) { + if(pe->tag.num == INTEGER && pe->val.tag == VInt) { + *pint = pe->val.u.intval; + return 1; + } + else if(pe->tag.num == BOOLEAN && pe->val.tag == VBool) { + *pint = pe->val.u.boolval; + return 1; + } + } + return 0; +} + +/* + * for convience, all VInt's are readable via this routine, + * as well as all VBigInt's + */ +static int +is_bigint(Elem* pe, Bytes** pbigint) +{ + int v, n, i; + + if(pe->tag.class == Universal && pe->tag.num == INTEGER) { + if(pe->val.tag == VBigInt) + *pbigint = pe->val.u.bigintval; + else if(pe->val.tag == VInt){ + v = pe->val.u.intval; + for(n = 1; n < 4; n++) + if((1 << (8 * n)) > v) + break; + *pbigint = newbytes(n); + for(i = 0; i < n; i++) + (*pbigint)->data[i] = (v >> ((n - 1 - i) * 8)); + }else + return 0; + return 1; + } + return 0; +} + +static int +is_bitstring(Elem* pe, Bits** pbits) +{ + if(pe->tag.class == Universal && pe->tag.num == BIT_STRING && pe->val.tag == VBitString) { + *pbits = pe->val.u.bitstringval; + return 1; + } + return 0; +} + +static int +is_octetstring(Elem* pe, Bytes** poctets) +{ + if(pe->tag.class == Universal && pe->tag.num == OCTET_STRING && pe->val.tag == VOctets) { + *poctets = pe->val.u.octetsval; + return 1; + } + return 0; +} + +static int +is_oid(Elem* pe, Ints** poid) +{ + if(pe->tag.class == Universal && pe->tag.num == OBJECT_ID && pe->val.tag == VObjId) { + *poid = pe->val.u.objidval; + return 1; + } + return 0; +} + +static int +is_string(Elem* pe, char** pstring) +{ + if(pe->tag.class == Universal) { + switch(pe->tag.num) { + case NumericString: + case PrintableString: + case TeletexString: + case VideotexString: + case IA5String: + case GraphicString: + case VisibleString: + case GeneralString: + case UniversalString: + case BMPString: + if(pe->val.tag == VString) { + *pstring = pe->val.u.stringval; + return 1; + } + } + } + return 0; +} + +static int +is_time(Elem* pe, char** ptime) +{ + if(pe->tag.class == Universal + && (pe->tag.num == UTCTime || pe->tag.num == GeneralizedTime) + && pe->val.tag == VString) { + *ptime = pe->val.u.stringval; + return 1; + } + return 0; +} + + +/* + * malloc and return a new Bytes structure capable of + * holding len bytes. (len >= 0) + */ +static Bytes* +newbytes(int len) +{ + Bytes* ans; + + ans = (Bytes*)emalloc(OFFSETOF(data[0], Bytes) + len); + ans->len = len; + return ans; +} + +/* + * newbytes(len), with data initialized from buf + */ +static Bytes* +makebytes(uchar* buf, int len) +{ + Bytes* ans; + + ans = newbytes(len); + memmove(ans->data, buf, len); + return ans; +} + +static void +freebytes(Bytes* b) +{ + if(b != nil) + free(b); +} + +/* + * Make a new Bytes, containing bytes of b1 followed by those of b2. + * Either b1 or b2 or both can be nil. + */ +static Bytes* +catbytes(Bytes* b1, Bytes* b2) +{ + Bytes* ans; + int n; + + if(b1 == nil) { + if(b2 == nil) + ans = newbytes(0); + else + ans = makebytes(b2->data, b2->len); + } + else if(b2 == nil) { + ans = makebytes(b1->data, b1->len); + } + else { + n = b1->len + b2->len; + ans = newbytes(n); + ans->len = n; + memmove(ans->data, b1->data, b1->len); + memmove(ans->data+b1->len, b2->data, b2->len); + } + return ans; +} + +/* len is number of ints */ +static Ints* +newints(int len) +{ + Ints* ans; + + ans = (Ints*)emalloc(OFFSETOF(data[0], Ints) + len*sizeof(int)); + ans->len = len; + return ans; +} + +static Ints* +makeints(int* buf, int len) +{ + Ints* ans; + + ans = newints(len); + if(len > 0) + memmove(ans->data, buf, len*sizeof(int)); + return ans; +} + +static void +freeints(Ints* b) +{ + if(b != nil) + free(b); +} + +/* len is number of bytes */ +static Bits* +newbits(int len) +{ + Bits* ans; + + ans = (Bits*)emalloc(OFFSETOF(data[0], Bits) + len); + ans->len = len; + ans->unusedbits = 0; + return ans; +} + +static Bits* +makebits(uchar* buf, int len, int unusedbits) +{ + Bits* ans; + + ans = newbits(len); + memmove(ans->data, buf, len); + ans->unusedbits = unusedbits; + return ans; +} + +static void +freebits(Bits* b) +{ + if(b != nil) + free(b); +} + +static Elist* +mkel(Elem e, Elist* tail) +{ + Elist* el; + + el = (Elist*)emalloc(sizeof(Elist)); + el->hd = e; + el->tl = tail; + return el; +} + +static int +elistlen(Elist* el) +{ + int ans = 0; + while(el != nil) { + ans++; + el = el->tl; + } + return ans; +} + +/* Frees elist, but not fields inside values of constituent elems */ +static void +freeelist(Elist* el) +{ + Elist* next; + + while(el != nil) { + next = el->tl; + free(el); + el = next; + } +} + +/* free any allocated structures inside v (recursively freeing Elists) */ +static void +freevalfields(Value* v) +{ + Elist* el; + Elist* l; + if(v == nil) + return; + switch(v->tag) { + case VOctets: + freebytes(v->u.octetsval); + break; + case VBigInt: + freebytes(v->u.bigintval); + break; + case VReal: + freebytes(v->u.realval); + break; + case VOther: + freebytes(v->u.otherval); + break; + case VBitString: + freebits(v->u.bitstringval); + break; + case VObjId: + freeints(v->u.objidval); + break; + case VString: + if (v->u.stringval) + free(v->u.stringval); + break; + case VSeq: + el = v->u.seqval; + for(l = el; l != nil; l = l->tl) + freevalfields(&l->hd.val); + if (el) + freeelist(el); + break; + case VSet: + el = v->u.setval; + for(l = el; l != nil; l = l->tl) + freevalfields(&l->hd.val); + if (el) + freeelist(el); + break; + } +} + +/* end of general ASN1 functions */ + + + + + +/*=============================================================*/ +/* + * Decode and parse an X.509 Certificate, defined by this ASN1: + * Certificate ::= SEQUENCE { + * certificateInfo CertificateInfo, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING } + * + * CertificateInfo ::= SEQUENCE { + * version [0] INTEGER DEFAULT v1 (0), + * serialNumber INTEGER, + * signature AlgorithmIdentifier, + * issuer Name, + * validity Validity, + * subject Name, + * subjectPublicKeyInfo SubjectPublicKeyInfo } + * (version v2 has two more fields, optional unique identifiers for + * issuer and subject; since we ignore these anyway, we won't parse them) + * + * Validity ::= SEQUENCE { + * notBefore UTCTime, + * notAfter UTCTime } + * + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + * + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFER, + * parameters ANY DEFINED BY ALGORITHM OPTIONAL } + * + * Name ::= SEQUENCE OF RelativeDistinguishedName + * + * RelativeDistinguishedName ::= SETOF SIZE(1..MAX) OF AttributeTypeAndValue + * + * AttributeTypeAndValue ::= SEQUENCE { + * type OBJECT IDENTIFER, + * value DirectoryString } + * (selected attributes have these Object Ids: + * commonName {2 5 4 3} + * countryName {2 5 4 6} + * localityName {2 5 4 7} + * stateOrProvinceName {2 5 4 8} + * organizationName {2 5 4 10} + * organizationalUnitName {2 5 4 11} + * ) + * + * DirectoryString ::= CHOICE { + * teletexString TeletexString, + * printableString PrintableString, + * universalString UniversalString } + * + * See rfc1423, rfc2437 for AlgorithmIdentifier, subjectPublicKeyInfo, signature. + * + * Not yet implemented: + * CertificateRevocationList ::= SIGNED SEQUENCE{ + * signature AlgorithmIdentifier, + * issuer Name, + * lastUpdate UTCTime, + * nextUpdate UTCTime, + * revokedCertificates + * SEQUENCE OF CRLEntry OPTIONAL} + * CRLEntry ::= SEQUENCE{ + * userCertificate SerialNumber, + * revocationDate UTCTime} + */ + +typedef struct CertX509 { + int serial; + char* issuer; + char* validity_start; + char* validity_end; + char* subject; + int publickey_alg; + Bytes* publickey; + int signature_alg; + Bytes* signature; +} CertX509; + +/* Algorithm object-ids */ +enum { + ALG_rsaEncryption, + ALG_md2WithRSAEncryption, + ALG_md4WithRSAEncryption, + ALG_md5WithRSAEncryption, + ALG_sha1WithRSAEncryption, + ALG_md5, + NUMALGS +}; +typedef struct Ints7 { + int len; + int data[7]; +} Ints7; +static Ints7 oid_rsaEncryption = {7, 1, 2, 840, 113549, 1, 1, 1 }; +static Ints7 oid_md2WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 2 }; +static Ints7 oid_md4WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 3 }; +static Ints7 oid_md5WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 4 }; +static Ints7 oid_sha1WithRSAEncryption ={7, 1, 2, 840, 113549, 1, 1, 5 }; +static Ints7 oid_md5 ={6, 1, 2, 840, 113549, 2, 5, 0 }; +static Ints *alg_oid_tab[NUMALGS+1] = { + (Ints*)&oid_rsaEncryption, + (Ints*)&oid_md2WithRSAEncryption, + (Ints*)&oid_md4WithRSAEncryption, + (Ints*)&oid_md5WithRSAEncryption, + (Ints*)&oid_sha1WithRSAEncryption, + (Ints*)&oid_md5, + nil +}; +static DigestFun digestalg[NUMALGS+1] = { md5, md5, md5, md5, sha1, md5, nil }; + +static void +freecert(CertX509* c) +{ + if (!c) return; + if(c->issuer != nil) + free(c->issuer); + if(c->validity_start != nil) + free(c->validity_start); + if(c->validity_end != nil) + free(c->validity_end); + if(c->subject != nil) + free(c->subject); + freebytes(c->publickey); + freebytes(c->signature); +} + +/* + * Parse the Name ASN1 type. + * The sequence of RelativeDistinguishedName's gives a sort of pathname, + * from most general to most specific. Each element of the path can be + * one or more (but usually just one) attribute-value pair, such as + * countryName="US". + * We'll just form a "postal-style" address string by concatenating the elements + * from most specific to least specific, separated by commas. + * Return name-as-string (which must be freed by caller). + */ +static char* +parse_name(Elem* e) +{ + Elist* el; + Elem* es; + Elist* esetl; + Elem* eat; + Elist* eatl; + char* s; + enum { MAXPARTS = 100 }; + char* parts[MAXPARTS]; + int i; + int plen; + char* ans = nil; + + if(!is_seq(e, &el)) + goto errret; + i = 0; + plen = 0; + while(el != nil) { + es = &el->hd; + if(!is_set(es, &esetl)) + goto errret; + while(esetl != nil) { + eat = &esetl->hd; + if(!is_seq(eat, &eatl) || elistlen(eatl) != 2) + goto errret; + if(!is_string(&eatl->tl->hd, &s) || i>=MAXPARTS) + goto errret; + parts[i++] = s; + plen += strlen(s) + 2; /* room for ", " after */ + esetl = esetl->tl; + } + el = el->tl; + } + if(i > 0) { + ans = (char*)emalloc(plen); + *ans = '\0'; + while(--i >= 0) { + s = parts[i]; + strcat(ans, s); + if(i > 0) + strcat(ans, ", "); + } + } + +errret: + return ans; +} + +/* + * Parse an AlgorithmIdentifer ASN1 type. + * Look up the oid in oid_tab and return one of OID_rsaEncryption, etc.., + * or -1 if not found. + * For now, ignore parameters, since none of our algorithms need them. + */ +static int +parse_alg(Elem* e) +{ + Elist* el; + Ints* oid; + + if(!is_seq(e, &el) || el == nil || !is_oid(&el->hd, &oid)) + return -1; + return oid_lookup(oid, alg_oid_tab); +} + +static CertX509* +decode_cert(Bytes* a) +{ + int ok = 0; + int n; + CertX509* c = nil; + Elem ecert; + Elem* ecertinfo; + Elem* esigalg; + Elem* esig; + Elem* eserial; + Elem* eissuer; + Elem* evalidity; + Elem* esubj; + Elem* epubkey; + Elist* el; + Elist* elcert = nil; + Elist* elcertinfo = nil; + Elist* elvalidity = nil; + Elist* elpubkey = nil; + Bits* bits = nil; + Bytes* b; + Elem* e; + + if(decode(a->data, a->len, &ecert) != ASN_OK) + goto errret; + + c = (CertX509*)emalloc(sizeof(CertX509)); + c->serial = -1; + c->issuer = nil; + c->validity_start = nil; + c->validity_end = nil; + c->subject = nil; + c->publickey_alg = -1; + c->publickey = nil; + c->signature_alg = -1; + c->signature = nil; + + /* Certificate */ + if(!is_seq(&ecert, &elcert) || elistlen(elcert) !=3) + goto errret; + ecertinfo = &elcert->hd; + el = elcert->tl; + esigalg = &el->hd; + c->signature_alg = parse_alg(esigalg); + el = el->tl; + esig = &el->hd; + + /* Certificate Info */ + if(!is_seq(ecertinfo, &elcertinfo)) + goto errret; + n = elistlen(elcertinfo); + if(n < 6) + goto errret; + eserial =&elcertinfo->hd; + el = elcertinfo->tl; + /* check for optional version, marked by explicit context tag 0 */ + if(eserial->tag.class == Context && eserial->tag.num == 0) { + eserial = &el->hd; + if(n < 7) + goto errret; + el = el->tl; + } + + if(parse_alg(&el->hd) != c->signature_alg) + goto errret; + el = el->tl; + eissuer = &el->hd; + el = el->tl; + evalidity = &el->hd; + el = el->tl; + esubj = &el->hd; + el = el->tl; + epubkey = &el->hd; + if(!is_int(eserial, &c->serial)) { + if(!is_bigint(eserial, &b)) + goto errret; + c->serial = -1; /* else we have to change cert struct */ + } + c->issuer = parse_name(eissuer); + if(c->issuer == nil) + goto errret; + /* Validity */ + if(!is_seq(evalidity, &elvalidity)) + goto errret; + if(elistlen(elvalidity) != 2) + goto errret; + e = &elvalidity->hd; + if(!is_time(e, &c->validity_start)) + goto errret; + e->val.u.stringval = nil; /* string ownership transfer */ + e = &elvalidity->tl->hd; + if(!is_time(e, &c->validity_end)) + goto errret; + e->val.u.stringval = nil; /* string ownership transfer */ + + /* resume CertificateInfo */ + c->subject = parse_name(esubj); + if(c->subject == nil) + goto errret; + + /* SubjectPublicKeyInfo */ + if(!is_seq(epubkey, &elpubkey)) + goto errret; + if(elistlen(elpubkey) != 2) + goto errret; + + c->publickey_alg = parse_alg(&elpubkey->hd); + if(c->publickey_alg < 0) + goto errret; + if(!is_bitstring(&elpubkey->tl->hd, &bits)) + goto errret; + if(bits->unusedbits != 0) + goto errret; + c->publickey = makebytes(bits->data, bits->len); + + /*resume Certificate */ + if(c->signature_alg < 0) + goto errret; + if(!is_bitstring(esig, &bits)) + goto errret; + c->signature = makebytes(bits->data, bits->len); + ok = 1; + +errret: + freevalfields(&ecert.val); /* recurses through lists, too */ + if(!ok){ + freecert(c); + c = nil; + } + return c; +} + +/* + * RSAPublickKey :: SEQUENCE { + * modulus INTEGER, + * publicExponent INTEGER + * } + */ +static RSApub* +decode_rsapubkey(Bytes* a) +{ + Elem e; + Elist *el; + mpint *mp; + RSApub* key; + + key = rsapuballoc(); + if(decode(a->data, a->len, &e) != ASN_OK) + goto errret; + if(!is_seq(&e, &el) || elistlen(el) != 2) + goto errret; + + key->n = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + + el = el->tl; + key->ek = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + return key; +errret: + rsapubfree(key); + return nil; +} + +/* + * RSAPrivateKey ::= SEQUENCE { + * version Version, + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER -- (inverse of q) mod p } + */ +static RSApriv* +decode_rsaprivkey(Bytes* a) +{ + int version; + Elem e; + Elist *el; + mpint *mp; + RSApriv* key; + + key = rsaprivalloc(); + if(decode(a->data, a->len, &e) != ASN_OK) + goto errret; + if(!is_seq(&e, &el) || elistlen(el) != 9) + goto errret; + if(!is_int(&el->hd, &version) || version != 0) + goto errret; + + el = el->tl; + key->pub.n = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + + el = el->tl; + key->pub.ek = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + + el = el->tl; + key->dk = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + + el = el->tl; + key->q = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + + el = el->tl; + key->p = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + + el = el->tl; + key->kq = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + + el = el->tl; + key->kp = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + + el = el->tl; + key->c2 = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + + return key; +errret: + rsaprivfree(key); + return nil; +} + +static mpint* +asn1mpint(Elem *e) +{ + Bytes *b; + mpint *mp; + int v; + + if(is_int(e, &v)) + return itomp(v, nil); + if(is_bigint(e, &b)) { + mp = betomp(b->data, b->len, nil); + freebytes(b); + return mp; + } + return nil; +} + +static mpint* +pkcs1pad(Bytes *b, mpint *modulus) +{ + int n = (mpsignif(modulus)+7)/8; + int pm1, i; + uchar *p; + mpint *mp; + + pm1 = n - 1 - b->len; + p = (uchar*)emalloc(n); + p[0] = 0; + p[1] = 1; + for(i = 2; i < pm1; i++) + p[i] = 0xFF; + p[pm1] = 0; + memcpy(&p[pm1+1], b->data, b->len); + mp = betomp(p, n, nil); + free(p); + return mp; +} + +RSApriv* +asn1toRSApriv(uchar *kd, int kn) +{ + Bytes *b; + RSApriv *key; + + b = makebytes(kd, kn); + key = decode_rsaprivkey(b); + freebytes(b); + return key; +} + +/* + * digest(CertificateInfo) + * Our ASN.1 library doesn't return pointers into the original + * data array, so we need to do a little hand decoding. + */ +static void +digest_certinfo(Bytes *cert, DigestFun digestfun, uchar *digest) +{ + uchar *info, *p, *pend; + ulong infolen; + int isconstr, length; + Tag tag; + Elem elem; + + p = cert->data; + pend = cert->data + cert->len; + if(tag_decode(&p, pend, &tag, &isconstr) != ASN_OK || + tag.class != Universal || tag.num != SEQUENCE || + length_decode(&p, pend, &length) != ASN_OK || + p+length > pend || + p+length < p) + return; + info = p; + if(ber_decode(&p, pend, &elem) != ASN_OK || elem.tag.num != SEQUENCE) + return; + infolen = p - info; + (*digestfun)(info, infolen, digest, nil); +} + +static char* +verify_signature(Bytes* signature, RSApub *pk, uchar *edigest, Elem **psigalg) +{ + Elem e; + Elist *el; + Bytes *digest; + uchar *pkcs1buf, *buf; + int buflen; + mpint *pkcs1; + int nlen; + + /* one less than the byte length of the modulus */ + nlen = (mpsignif(pk->n)-1)/8; + + /* see 9.2.1 of rfc2437 */ + pkcs1 = betomp(signature->data, signature->len, nil); + mpexp(pkcs1, pk->ek, pk->n, pkcs1); + pkcs1buf = nil; + buflen = mptobe(pkcs1, nil, 0, &pkcs1buf); + buf = pkcs1buf; + if(buflen != nlen || buf[0] != 1) + return "expected 1"; + buf++; + while(buf[0] == 0xff) + buf++; + if(buf[0] != 0) + return "expected 0"; + buf++; + buflen -= buf-pkcs1buf; + if(decode(buf, buflen, &e) != ASN_OK || !is_seq(&e, &el) || elistlen(el) != 2 || + !is_octetstring(&el->tl->hd, &digest)) + return "signature parse error"; + *psigalg = &el->hd; + if(memcmp(digest->data, edigest, digest->len) == 0) + return nil; + return "digests did not match"; +} + +RSApub* +X509toRSApub(uchar *cert, int ncert, char *name, int nname) +{ + char *e; + Bytes *b; + CertX509 *c; + RSApub *pk; + + 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; // take just CN part of Distinguished Name + strncpy(name, c->subject, nname); + } + pk = decode_rsapubkey(c->publickey); + freecert(c); + return pk; +} + +char* +X509verify(uchar *cert, int ncert, RSApub *pk) +{ + char *e; + Bytes *b; + CertX509 *c; + uchar digest[SHA1dlen]; + Elem *sigalg; + + b = makebytes(cert, ncert); + c = decode_cert(b); + if(c != nil) + digest_certinfo(b, digestalg[c->signature_alg], digest); + freebytes(b); + if(c == nil) + return "cannot decode cert"; + e = verify_signature(c->signature, pk, digest, &sigalg); + freecert(c); + return e; +} + +/* ------- Elem constructors ---------- */ +static Elem +Null(void) +{ + Elem e; + + e.tag.class = Universal; + e.tag.num = NULLTAG; + e.val.tag = VNull; + return e; +} + +static Elem +mkint(int j) +{ + Elem e; + + e.tag.class = Universal; + e.tag.num = INTEGER; + e.val.tag = VInt; + e.val.u.intval = j; + return e; +} + +static Elem +mkbigint(mpint *p) +{ + Elem e; + uchar *buf; + int buflen; + + e.tag.class = Universal; + e.tag.num = INTEGER; + e.val.tag = VBigInt; + buflen = mptobe(p, nil, 0, &buf); + e.val.u.bigintval = makebytes(buf, buflen); + free(buf); + return e; +} + +static Elem +mkstring(char *s) +{ + Elem e; + + e.tag.class = Universal; + e.tag.num = IA5String; + e.val.tag = VString; + e.val.u.stringval = estrdup(s); + return e; +} + +static Elem +mkoctet(uchar *buf, int buflen) +{ + Elem e; + + e.tag.class = Universal; + e.tag.num = OCTET_STRING; + e.val.tag = VOctets; + e.val.u.octetsval = makebytes(buf, buflen); + return e; +} + +static Elem +mkbits(uchar *buf, int buflen) +{ + Elem e; + + e.tag.class = Universal; + e.tag.num = BIT_STRING; + e.val.tag = VBitString; + e.val.u.bitstringval = makebits(buf, buflen, 0); + return e; +} + +static Elem +mkutc(long t) +{ + Elem e; + char utc[50]; + Tm *tm = gmtime(t); + + e.tag.class = Universal; + e.tag.num = UTCTime; + e.val.tag = VString; + snprint(utc, 50, "%.2d%.2d%.2d%.2d%.2d%.2dZ", + tm->year % 100, tm->mon+1, tm->mday, tm->hour, tm->min, tm->sec); + e.val.u.stringval = estrdup(utc); + return e; +} + +static Elem +mkoid(Ints *oid) +{ + Elem e; + + e.tag.class = Universal; + e.tag.num = OBJECT_ID; + e.val.tag = VObjId; + e.val.u.objidval = makeints(oid->data, oid->len); + return e; +} + +static Elem +mkseq(Elist *el) +{ + Elem e; + + e.tag.class = Universal; + e.tag.num = SEQUENCE; + e.val.tag = VSeq; + e.val.u.seqval = el; + return e; +} + +static Elem +mkset(Elist *el) +{ + Elem e; + + e.tag.class = Universal; + e.tag.num = SETOF; + e.val.tag = VSet; + e.val.u.setval = el; + return e; +} + +static Elem +mkalg(int alg) +{ + return mkseq(mkel(mkoid(alg_oid_tab[alg]), mkel(Null(), nil))); +} + +typedef struct Ints7pref { + int len; + int data[7]; + char prefix[4]; +} Ints7pref; +Ints7pref DN_oid[] = { + {4, 2, 5, 4, 6, 0, 0, 0, "C="}, + {4, 2, 5, 4, 8, 0, 0, 0, "ST="}, + {4, 2, 5, 4, 7, 0, 0, 0, "L="}, + {4, 2, 5, 4, 10, 0, 0, 0, "O="}, + {4, 2, 5, 4, 11, 0, 0, 0, "OU="}, + {4, 2, 5, 4, 3, 0, 0, 0, "CN="}, + {7, 1,2,840,113549,1,9,1, "E="}, +}; + +static Elem +mkname(Ints7pref *oid, char *subj) +{ + return mkset(mkel(mkseq(mkel(mkoid((Ints*)oid), mkel(mkstring(subj), nil))), nil)); +} + +static Elem +mkDN(char *dn) +{ + int i, j, nf; + char *f[20], *prefix, *d2 = estrdup(dn); + Elist* el = nil; + + nf = tokenize(d2, f, nelem(f)); + for(i=nf-1; i>=0; i--){ + for(j=0; j<nelem(DN_oid); j++){ + prefix = DN_oid[j].prefix; + if(strncmp(f[i],prefix,strlen(prefix))==0){ + el = mkel(mkname(&DN_oid[j],f[i]+strlen(prefix)), el); + break; + } + } + } + free(d2); + return mkseq(el); +} + + +uchar* +X509gen(RSApriv *priv, char *subj, ulong valid[2], int *certlen) +{ + int serial = 0; + uchar *cert = nil; + RSApub *pk = rsaprivtopub(priv); + Bytes *certbytes, *pkbytes, *certinfobytes, *sigbytes; + Elem e, certinfo, issuer, subject, pubkey, validity, sig; + uchar digest[MD5dlen], *buf; + int buflen; + mpint *pkcs1; + + e.val.tag = VInt; /* so freevalfields at errret is no-op */ + issuer = mkDN(subj); + subject = mkDN(subj); + pubkey = mkseq(mkel(mkbigint(pk->n),mkel(mkint(mptoi(pk->ek)),nil))); + if(encode(pubkey, &pkbytes) != ASN_OK) + goto errret; + freevalfields(&pubkey.val); + pubkey = mkseq( + mkel(mkalg(ALG_rsaEncryption), + mkel(mkbits(pkbytes->data, pkbytes->len), + nil))); + freebytes(pkbytes); + validity = mkseq( + mkel(mkutc(valid[0]), + mkel(mkutc(valid[1]), + nil))); + certinfo = mkseq( + mkel(mkint(serial), + mkel(mkalg(ALG_md5WithRSAEncryption), + mkel(issuer, + mkel(validity, + mkel(subject, + mkel(pubkey, + nil))))))); + if(encode(certinfo, &certinfobytes) != ASN_OK) + goto errret; + md5(certinfobytes->data, certinfobytes->len, digest, 0); + freebytes(certinfobytes); + sig = mkseq( + mkel(mkalg(ALG_md5), + mkel(mkoctet(digest, MD5dlen), + nil))); + if(encode(sig, &sigbytes) != ASN_OK) + goto errret; + pkcs1 = pkcs1pad(sigbytes, pk->n); + freebytes(sigbytes); + rsadecrypt(priv, pkcs1, pkcs1); + buflen = mptobe(pkcs1, nil, 0, &buf); + mpfree(pkcs1); + e = mkseq( + mkel(certinfo, + mkel(mkalg(ALG_md5WithRSAEncryption), + mkel(mkbits(buf, buflen), + nil)))); + free(buf); + if(encode(e, &certbytes) != ASN_OK) + goto errret; + if(certlen) + *certlen = certbytes->len; + cert = certbytes->data; +errret: + freevalfields(&e.val); + return cert; +} + +uchar* +X509req(RSApriv *priv, char *subj, int *certlen) +{ + /* RFC 2314, PKCS #10 Certification Request Syntax */ + int version = 0; + uchar *cert = nil; + RSApub *pk = rsaprivtopub(priv); + Bytes *certbytes, *pkbytes, *certinfobytes, *sigbytes; + Elem e, certinfo, subject, pubkey, sig; + uchar digest[MD5dlen], *buf; + int buflen; + mpint *pkcs1; + + e.val.tag = VInt; /* so freevalfields at errret is no-op */ + subject = mkDN(subj); + pubkey = mkseq(mkel(mkbigint(pk->n),mkel(mkint(mptoi(pk->ek)),nil))); + if(encode(pubkey, &pkbytes) != ASN_OK) + goto errret; + freevalfields(&pubkey.val); + pubkey = mkseq( + mkel(mkalg(ALG_rsaEncryption), + mkel(mkbits(pkbytes->data, pkbytes->len), + nil))); + freebytes(pkbytes); + certinfo = mkseq( + mkel(mkint(version), + mkel(subject, + mkel(pubkey, + nil)))); + if(encode(certinfo, &certinfobytes) != ASN_OK) + goto errret; + md5(certinfobytes->data, certinfobytes->len, digest, 0); + freebytes(certinfobytes); + sig = mkseq( + mkel(mkalg(ALG_md5), + mkel(mkoctet(digest, MD5dlen), + nil))); + if(encode(sig, &sigbytes) != ASN_OK) + goto errret; + pkcs1 = pkcs1pad(sigbytes, pk->n); + freebytes(sigbytes); + rsadecrypt(priv, pkcs1, pkcs1); + buflen = mptobe(pkcs1, nil, 0, &buf); + mpfree(pkcs1); + e = mkseq( + mkel(certinfo, + mkel(mkalg(ALG_md5), + mkel(mkbits(buf, buflen), + nil)))); + free(buf); + if(encode(e, &certbytes) != ASN_OK) + goto errret; + if(certlen) + *certlen = certbytes->len; + cert = certbytes->data; +errret: + freevalfields(&e.val); + return cert; +} + +static char* +tagdump(Tag tag) +{ + if(tag.class != Universal) + return smprint("class%d,num%d", tag.class, tag.num); + switch(tag.num){ + case BOOLEAN: return "BOOLEAN"; break; + case INTEGER: return "INTEGER"; break; + case BIT_STRING: return "BIT STRING"; break; + case OCTET_STRING: return "OCTET STRING"; break; + case NULLTAG: return "NULLTAG"; break; + case OBJECT_ID: return "OID"; break; + case ObjectDescriptor: return "OBJECT_DES"; break; + case EXTERNAL: return "EXTERNAL"; break; + case REAL: return "REAL"; break; + case ENUMERATED: return "ENUMERATED"; break; + case EMBEDDED_PDV: return "EMBEDDED PDV"; break; + case SEQUENCE: return "SEQUENCE"; break; + case SETOF: return "SETOF"; break; + case NumericString: return "NumericString"; break; + case PrintableString: return "PrintableString"; break; + case TeletexString: return "TeletexString"; break; + case VideotexString: return "VideotexString"; break; + case IA5String: return "IA5String"; break; + case UTCTime: return "UTCTime"; break; + case GeneralizedTime: return "GeneralizedTime"; break; + case GraphicString: return "GraphicString"; break; + case VisibleString: return "VisibleString"; break; + case GeneralString: return "GeneralString"; break; + case UniversalString: return "UniversalString"; break; + case BMPString: return "BMPString"; break; + default: + return smprint("Universal,num%d", tag.num); + } +} + +static void +edump(Elem e) +{ + Value v; + Elist *el; + int i; + + print("%s{", tagdump(e.tag)); + v = e.val; + switch(v.tag){ + case VBool: print("Bool %d",v.u.boolval); break; + case VInt: print("Int %d",v.u.intval); break; + case VOctets: print("Octets[%d] %.2x%.2x...",v.u.octetsval->len,v.u.octetsval->data[0],v.u.octetsval->data[1]); break; + case VBigInt: print("BigInt[%d] %.2x%.2x...",v.u.bigintval->len,v.u.bigintval->data[0],v.u.bigintval->data[1]); break; + case VReal: print("Real..."); break; + case VOther: print("Other..."); break; + case VBitString: print("BitString..."); break; + case VNull: print("Null"); break; + case VEOC: print("EOC..."); break; + case VObjId: print("ObjId"); + for(i = 0; i<v.u.objidval->len; i++) + print(" %d", v.u.objidval->data[i]); + break; + case VString: print("String \"%s\"",v.u.stringval); break; + case VSeq: print("Seq\n"); + for(el = v.u.seqval; el!=nil; el = el->tl) + edump(el->hd); + break; + case VSet: print("Set\n"); + for(el = v.u.setval; el!=nil; el = el->tl) + edump(el->hd); + break; + } + print("}\n"); +} + +void +asn1dump(uchar *der, int len) +{ + Elem e; + + if(decode(der, len, &e) != ASN_OK){ + print("didn't parse\n"); + exits("didn't parse"); + } + edump(e); +} + +void +X509dump(uchar *cert, int ncert) +{ + char *e; + Bytes *b; + CertX509 *c; + RSApub *pk; + uchar digest[SHA1dlen]; + Elem *sigalg; + + print("begin X509dump\n"); + b = makebytes(cert, ncert); + c = decode_cert(b); + if(c != nil) + digest_certinfo(b, digestalg[c->signature_alg], digest); + freebytes(b); + if(c == nil){ + print("cannot decode cert"); + return; + } + + print("serial %d\n", c->serial); + print("issuer %s\n", c->issuer); + print("validity %s %s\n", c->validity_start, c->validity_end); + print("subject %s\n", c->subject); + pk = decode_rsapubkey(c->publickey); + print("pubkey e=%B n(%d)=%B\n", pk->ek, mpsignif(pk->n), pk->n); + + print("sigalg=%d digest=%.*H\n", c->signature_alg, MD5dlen, digest); + e = verify_signature(c->signature, pk, digest, &sigalg); + if(e==nil){ + e = "nil (meaning ok)"; + print("sigalg=\n"); + if(sigalg) + edump(*sigalg); + } + print("self-signed verify_signature returns: %s\n", e); + + rsapubfree(pk); + freecert(c); + print("end X509dump\n"); +} |