| #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*)(void*)&oid_rsaEncryption, |
| (Ints*)(void*)&oid_md2WithRSAEncryption, |
| (Ints*)(void*)&oid_md4WithRSAEncryption, |
| (Ints*)(void*)&oid_md5WithRSAEncryption, |
| (Ints*)(void*)&oid_sha1WithRSAEncryption, |
| (Ints*)(void*)&oid_md5, |
| nil |
| }; |
| static DigestFun digestalg[NUMALGS+1] = { md5, md5, md5, md5, sha1, md5, 0 }; |
| |
| 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; |
| } |
| |
| /* |
| * DSAPrivateKey ::= SEQUENCE{ |
| * version Version, |
| * p INTEGER, |
| * q INTEGER, |
| * g INTEGER, -- alpha |
| * pub_key INTEGER, -- key |
| * priv_key INTEGER, -- secret |
| * } |
| */ |
| static DSApriv* |
| decode_dsaprivkey(Bytes* a) |
| { |
| int version; |
| Elem e; |
| Elist *el; |
| mpint *mp; |
| DSApriv* key; |
| |
| key = dsaprivalloc(); |
| if(decode(a->data, a->len, &e) != ASN_OK) |
| goto errret; |
| if(!is_seq(&e, &el) || elistlen(el) != 6) |
| goto errret; |
| version=-1; |
| if(!is_int(&el->hd, &version) || version != 0) |
| { |
| fprint(2, "version %d\n", version); |
| goto errret; |
| } |
| |
| el = el->tl; |
| key->pub.p = mp = asn1mpint(&el->hd); |
| if(mp == nil) |
| goto errret; |
| |
| el = el->tl; |
| key->pub.q = mp = asn1mpint(&el->hd); |
| if(mp == nil) |
| goto errret; |
| |
| el = el->tl; |
| key->pub.alpha = mp = asn1mpint(&el->hd); |
| if(mp == nil) |
| goto errret; |
| |
| el = el->tl; |
| key->pub.key = mp = asn1mpint(&el->hd); |
| if(mp == nil) |
| goto errret; |
| |
| el = el->tl; |
| key->secret = mp = asn1mpint(&el->hd); |
| if(mp == nil) |
| goto errret; |
| |
| return key; |
| errret: |
| dsaprivfree(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; |
| } |
| |
| DSApriv* |
| asn1toDSApriv(uchar *kd, int kn) |
| { |
| Bytes *b; |
| DSApriv *key; |
| |
| b = makebytes(kd, kn); |
| key = decode_dsaprivkey(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"); |
| } |