#include <u.h>
#include <libc.h>
#include <mp.h>
#include <libsec.h>

/*=============================================================*/
/*  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 UTF8String 12
#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[];
};

struct Ints {
       int     len;
       int     data[];
};

struct Bits {
       int     len;            /* number of bytes */
       int     unusedbits;     /* unused bits in last byte */
       uchar   data[];         /* 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      encode(Elem e, Bytes** pbytes);
static int      oid_lookup(Ints* o, Ints** tab);
static void     freevalfields(Value* v);
static mpint    *asn1mpint(Elem *e);
static void     edump(Elem);

#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)
               sysfatal("out of memory");
       memset(p, 0, n);
       setmalloctag(p, getcallerpc(&n));
       return p;
}

static char*
estrdup(char *s)
{
       char *d;
       int n;

       n = strlen(s)+1;
       d = emalloc(n);
       memmove(d, s, n);
       return d;
}


/*
* 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;
       int err;

       err = ber_decode(&p, &a[alen], pelem);
       if(err == ASN_OK && p != &a[alen])
               err = ASN_EVALLEN;
       return err;
}

/*
* 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;

       memset(pelem, 0, sizeof(*pelem));
       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 UTF8String:
       case NumericString:
       case PrintableString:
       case TeletexString:
       case VideotexString:
       case IA5String:
       case UTCTime:
       case GeneralizedTime:
       case GraphicString:
       case VisibleString:
       case GeneralString:
       case UniversalString:
       case BMPString:
               err = octet_decode(&p, pend, length, isconstr, &va);
               if(err == ASN_OK) {
                       uchar *s;
                       char *d;
                       Rune r;
                       int n;

                       switch(kind){
                       case UniversalString:
                               n = va->len / 4;
                               d = emalloc(n*UTFmax+1);
                               pval->u.stringval = d;
                               s = va->data;
                               while(n > 0){
                                       r = s[0]<<24 | s[1]<<16 | s[2]<<8 | s[3];
                                       if(r == 0)
                                               break;
                                       n--;
                                       s += 4;
                                       d += runetochar(d, &r);
                               }
                               *d = 0;
                               break;
                       case BMPString:
                               n = va->len / 2;
                               d = emalloc(n*UTFmax+1);
                               pval->u.stringval = d;
                               s = va->data;
                               while(n > 0){
                                       r = s[0]<<8 | s[1];
                                       if(r == 0)
                                               break;
                                       n--;
                                       s += 2;
                                       d += runetochar(d, &r);
                               }
                               *d = 0;
                               break;
                       default:
                               n = va->len;
                               d = emalloc(n+1);
                               pval->u.stringval = d;
                               s = va->data;
                               while(n > 0){
                                       if((*d = *s) == 0)
                                               break;
                                       n--;
                                       s++;
                                       d++;
                               }
                               *d = 0;
                               break;
                       }
                       if(n != 0){
                               err = ASN_EINVAL;
                               free(pval->u.stringval);
                       } else
                               pval->tag = VString;
                       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);
                               freevalfields(&elem.val);
                               freebytes(ans);
                               ans = newans;
                               break;

                       case VEOC:
                               if(length == -1)
                                       goto cloop_done;
                               /* no break */
                       default:
                               freevalfields(&elem.val);
                               p = pold;
                               err = ASN_EINVAL;
                               goto cloop_done;
                       }
               }
cloop_done:
               if(err != ASN_OK){
                       freebytes(ans);
                       ans = nil;
               }
       }
       *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)
                       freeelist(lve);
               else {
                       /* 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 UTF8String:
       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)
{
       if(pe->tag.class == Universal && pe->tag.num == INTEGER && pe->val.tag == VBigInt) {
               *pbigint = pe->val.u.bigintval;
               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 UTF8String:
               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;

       if(len < 0)
               abort();
       ans = emalloc(sizeof(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)
{
       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;

       if(len < 0 || len > ((uint)-1>>1)/sizeof(int))
               abort();
       ans = emalloc(sizeof(Ints) + len*sizeof(int));
       ans->len = len;
       return ans;
}

static Ints*
makeints(int* buf, int len)
{
       Ints* ans;

       ans = newints(len);
       memmove(ans->data, buf, len*sizeof(int));
       return ans;
}

static void
freeints(Ints* b)
{
       free(b);
}

/* len is number of bytes */
static Bits*
newbits(int len)
{
       Bits* ans;

       if(len < 0)
               abort();
       ans = emalloc(sizeof(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)
{
       free(b);
}

static Elist*
mkel(Elem e, Elist* tail)
{
       Elist* el;

       el = (Elist*)emalloc(sizeof(Elist));
       setmalloctag(el, getcallerpc(&e));
       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:
               free(v->u.stringval);
               break;
       case VSeq:
               el = v->u.seqval;
               for(l = el; l != nil; l = l->tl)
                       freevalfields(&l->hd.val);
               freeelist(el);
               break;
       case VSet:
               el = v->u.setval;
               for(l = el; l != nil; l = l->tl)
                       freevalfields(&l->hd.val);
               freeelist(el);
               break;
       }
       memset(v, 0, sizeof(*v));
}

static mpint*
asn1mpint(Elem *e)
{
       Bytes *b;
       int v;

       if(is_int(e, &v))
               return itomp(v, nil);
       if(is_bigint(e, &b))
               return betomp(b->data, b->len, nil);
       return nil;
}

/* 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;
       Bits*   publickey;
       int     signature_alg;
       Bits*   signature;
       int     curve;
} CertX509;

/* Algorithm object-ids */
enum {
       ALG_rsaEncryption,
       ALG_md2WithRSAEncryption,
       ALG_md4WithRSAEncryption,
       ALG_md5WithRSAEncryption,

       ALG_sha1WithRSAEncryption,
       ALG_sha1WithRSAEncryptionOiw,

       ALG_sha256WithRSAEncryption,
       ALG_sha384WithRSAEncryption,
       ALG_sha512WithRSAEncryption,
       ALG_sha224WithRSAEncryption,

       ALG_ecPublicKey,
       ALG_sha1WithECDSA,
       ALG_sha256WithECDSA,
       ALG_sha384WithECDSA,
       ALG_sha512WithECDSA,

       ALG_md5,
       ALG_sha1,
       ALG_sha256,
       ALG_sha384,
       ALG_sha512,
       ALG_sha224,

       NUMALGS
};

typedef struct Ints15 {
       int             len;
       int             data[15];
} Ints15;

typedef struct DigestAlg {
       int             alg;
       DigestState*    (*fun)(uchar*,ulong,uchar*,DigestState*);
       int             len;
} DigestAlg;

static DigestAlg alg_md5 = { ALG_md5, md5, MD5dlen};
static DigestAlg alg_sha1 = { ALG_sha1, sha1, SHA1dlen };
static DigestAlg alg_sha256 = { ALG_sha256, sha2_256, SHA2_256dlen };
static DigestAlg alg_sha384 = { ALG_sha384, sha2_384, SHA2_384dlen };
static DigestAlg alg_sha512 = { ALG_sha512, sha2_512, SHA2_512dlen };
static DigestAlg alg_sha224 = { ALG_sha224, sha2_224, SHA2_224dlen };

/* maximum length of digest output of the digest algs above */
enum {
       MAXdlen = SHA2_512dlen,
};

static Ints15 oid_rsaEncryption = {7, 1, 2, 840, 113549, 1, 1, 1 };

static Ints15 oid_md2WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 2 };
static Ints15 oid_md4WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 3 };
static Ints15 oid_md5WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 4 };
static Ints15 oid_sha1WithRSAEncryption ={7, 1, 2, 840, 113549, 1, 1, 5 };
static Ints15 oid_sha1WithRSAEncryptionOiw ={6, 1, 3, 14, 3, 2, 29 };
static Ints15 oid_sha256WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 11 };
static Ints15 oid_sha384WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 12 };
static Ints15 oid_sha512WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 13 };
static Ints15 oid_sha224WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 14 };

static Ints15 oid_ecPublicKey = {6, 1, 2, 840, 10045, 2, 1 };
static Ints15 oid_sha1WithECDSA = {6, 1, 2, 840, 10045, 4, 1 };
static Ints15 oid_sha256WithECDSA = {7, 1, 2, 840, 10045, 4, 3, 2 };
static Ints15 oid_sha384WithECDSA = {7, 1, 2, 840, 10045, 4, 3, 3 };
static Ints15 oid_sha512WithECDSA = {7, 1, 2, 840, 10045, 4, 3, 4 };

static Ints15 oid_md5 = {6, 1, 2, 840, 113549, 2, 5 };
static Ints15 oid_sha1 = {6, 1, 3, 14, 3, 2, 26 };
static Ints15 oid_sha256= {9, 2, 16, 840, 1, 101, 3, 4, 2, 1 };
static Ints15 oid_sha384= {9, 2, 16, 840, 1, 101, 3, 4, 2, 2 };
static Ints15 oid_sha512= {9, 2, 16, 840, 1, 101, 3, 4, 2, 3 };
static Ints15 oid_sha224= {9, 2, 16, 840, 1, 101, 3, 4, 2, 4 };

static Ints *alg_oid_tab[NUMALGS+1] = {
       (Ints*)&oid_rsaEncryption,
       (Ints*)&oid_md2WithRSAEncryption,
       (Ints*)&oid_md4WithRSAEncryption,
       (Ints*)&oid_md5WithRSAEncryption,

       (Ints*)&oid_sha1WithRSAEncryption,
       (Ints*)&oid_sha1WithRSAEncryptionOiw,

       (Ints*)&oid_sha256WithRSAEncryption,
       (Ints*)&oid_sha384WithRSAEncryption,
       (Ints*)&oid_sha512WithRSAEncryption,
       (Ints*)&oid_sha224WithRSAEncryption,

       (Ints*)&oid_ecPublicKey,
       (Ints*)&oid_sha1WithECDSA,
       (Ints*)&oid_sha256WithECDSA,
       (Ints*)&oid_sha384WithECDSA,
       (Ints*)&oid_sha512WithECDSA,

       (Ints*)&oid_md5,
       (Ints*)&oid_sha1,
       (Ints*)&oid_sha256,
       (Ints*)&oid_sha384,
       (Ints*)&oid_sha512,
       (Ints*)&oid_sha224,
       nil
};

static DigestAlg *digestalg[NUMALGS+1] = {
       &alg_md5, &alg_md5, &alg_md5, &alg_md5,
       &alg_sha1, &alg_sha1,
       &alg_sha256, &alg_sha384, &alg_sha512, &alg_sha224,
       &alg_sha256, &alg_sha1, &alg_sha256, &alg_sha384, &alg_sha512,
       &alg_md5, &alg_sha1, &alg_sha256, &alg_sha384, &alg_sha512, &alg_sha224,
       nil
};

static Bytes* encode_digest(DigestAlg *da, uchar *digest);

static Ints15 oid_secp256r1 = {7, 1, 2, 840, 10045, 3, 1, 7};
static Ints15 oid_secp384r1 = {5, 1, 3, 132, 0, 34};

static Ints *namedcurves_oid_tab[] = {
       (Ints*)&oid_secp256r1,
       (Ints*)&oid_secp384r1,
       nil,
};
static void (*namedcurves[])(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h) = {
       secp256r1,
       secp384r1,
       nil,
};

static void
freecert(CertX509* c)
{
       if(c == nil)
               return;
       free(c->issuer);
       free(c->validity_start);
       free(c->validity_end);
       free(c->subject);
       freebits(c->publickey);
       freebits(c->signature);
       free(c);
}

/*
* 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 int
parse_curve(Elem* e)
{
       Elist* el;
       Ints* oid;

       if(!is_seq(e, &el) || elistlen(el)<2 || !is_oid(&el->tl->hd, &oid))
               return -1;
       return oid_lookup(oid, namedcurves_oid_tab);
}

static CertX509*
decode_cert(uchar *buf, int len)
{
       int ok = 0;
       int n;
       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;
       CertX509* c = nil;

       if(decode(buf, 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;
       c->curve = -1;
       if(c->publickey_alg == ALG_ecPublicKey){
               c->curve = parse_curve(&elpubkey->hd);
               if(c->curve < 0)
                       goto errret;
       }
       elpubkey = elpubkey->tl;
       if(!is_bitstring(&elpubkey->hd, &bits))
               goto errret;
       elpubkey->hd.val.u.bitstringval = nil;  /* transfer ownership */
       c->publickey = bits;

       /*resume Certificate */
       if(c->signature_alg < 0)
               goto errret;
       if(!is_bitstring(esig, &bits))
               goto errret;
       esig->val.u.bitstringval = nil; /* transfer ownership */
       c->signature = bits;
       ok = 1;

errret:
       freevalfields(&ecert.val);      /* recurses through lists, too */
       if(!ok){
               freecert(c);
               c = nil;
       }
       return c;
}

/*
*      RSAPublickKey ::= SEQUENCE {
*              modulus INTEGER,
*              publicExponent INTEGER
*      }
*/
RSApub*
asn1toRSApub(uchar *buf, int len)
{
       Elem e;
       Elist *el;
       RSApub* key;

       key = nil;
       if(decode(buf, len, &e) != ASN_OK)
               goto errret;
       if(!is_seq(&e, &el) || elistlen(el) != 2)
               goto errret;

       key = rsapuballoc();
       if((key->n = asn1mpint(&el->hd)) == nil)
               goto errret;
       el = el->tl;
       if((key->ek = asn1mpint(&el->hd)) == nil)
               goto errret;

       freevalfields(&e.val);
       return key;
errret:
       freevalfields(&e.val);
       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 }
*/
RSApriv*
asn1toRSApriv(uchar *buf, int len)
{
       int version;
       Elem e;
       Elist *el;
       Bytes *b;
       RSApriv* key = nil;

       if(decode(buf, len, &e) != ASN_OK)
               goto errret;
       if(!is_seq(&e, &el))
               goto errret;

       if(!is_int(&el->hd, &version) || version != 0)
               goto errret;

       if(elistlen(el) != 9){
               if(elistlen(el) == 3
               && parse_alg(&el->tl->hd) == ALG_rsaEncryption
               && is_octetstring(&el->tl->tl->hd, &b)){
                       key = asn1toRSApriv(b->data, b->len);
                       if(key != nil)
                               goto done;
               }
               goto errret;
       }

       key = rsaprivalloc();
       el = el->tl;
       if((key->pub.n = asn1mpint(&el->hd)) == nil)
               goto errret;

       el = el->tl;
       if((key->pub.ek = asn1mpint(&el->hd)) == nil)
               goto errret;

       el = el->tl;
       if((key->dk = asn1mpint(&el->hd)) == nil)
               goto errret;

       el = el->tl;
       if((key->q = asn1mpint(&el->hd)) == nil)
               goto errret;

       el = el->tl;
       if((key->p = asn1mpint(&el->hd)) == nil)
               goto errret;

       el = el->tl;
       if((key->kq = asn1mpint(&el->hd)) == nil)
               goto errret;

       el = el->tl;
       if((key->kp = asn1mpint(&el->hd)) == nil)
               goto errret;

       el = el->tl;
       if((key->c2 = asn1mpint(&el->hd)) == nil)
               goto errret;

done:
       freevalfields(&e.val);
       return key;
errret:
       freevalfields(&e.val);
       rsaprivfree(key);
       return nil;
}

/*
* 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 int
digest_certinfo(uchar *cert, int ncert, DigestAlg *da, uchar *digest)
{
       uchar *info, *p, *pend;
       int isconstr, length;
       Tag tag;
       Elem elem;

       p = cert;
       pend = cert + ncert;
       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 -1;
       info = p;
       if(ber_decode(&p, pend, &elem) != ASN_OK)
               return -1;
       freevalfields(&elem.val);
       if(elem.tag.num != SEQUENCE)
               return -1;
       (*da->fun)(info, p - info, digest, nil);
       return da->len;
}

mpint*
pkcs1padbuf(uchar *buf, int len, mpint *modulus, int blocktype)
{
       int i, n = (mpsignif(modulus)-1)/8;
       int pad = n - 2 - len;
       uchar *p;
       mpint *mp;

       if(pad < 8){
               werrstr("rsa modulus too small");
               return nil;
       }
       if((p = malloc(n)) == nil)
               return nil;
       p[0] = blocktype;
       switch(blocktype){
       default:
       case 1:
               memset(p+1, 0xFF, pad);
               break;
       case 2:
               for(i=1; i <= pad; i++)
                       p[i] = 1 + nfastrand(255);
               break;
       }
       p[1+pad] = 0;
       memmove(p+2+pad, buf, len);
       mp = betomp(p, n, nil);
       free(p);
       return mp;
}

int
pkcs1unpadbuf(uchar *buf, int len, mpint *modulus, int blocktype)
{
       uchar *p = buf + 1, *e = buf + len;

       if(len < 1 || len != (mpsignif(modulus)-1)/8 || buf[0] != blocktype)
               return -1;
       switch(blocktype){
       default:
       case 1:
               while(p < e && *p == 0xFF)
                       p++;
               break;
       case 2:
               while(p < e && *p != 0x00)
                       p++;
               break;
       }
       if(p - buf <= 8 || p >= e || *p++ != 0x00)
               return -1;
       memmove(buf, p, len = e - p);
       return len;
}

static char Ebadsig[] = "bad signature";

char*
X509rsaverifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, RSApub *pk)
{
       mpint *x, *y;
       DigestAlg **dp;
       Bytes *digest;
       uchar *buf;
       int len;
       char *err;

       x = betomp(sig, siglen, nil);
       y = rsaencrypt(pk, x, nil);
       mpfree(x);
       len = mptobe(y, nil, 0, &buf);
       mpfree(y);

       err = Ebadsig;
       len = pkcs1unpadbuf(buf, len, pk->n, 1);
       if(len == edigestlen && tsmemcmp(buf, edigest, edigestlen) == 0)
               err = nil;
       for(dp = digestalg; err != nil && *dp != nil; dp++){
               if((*dp)->len != edigestlen)
                       continue;
               digest = encode_digest(*dp, edigest);
               if(digest->len == len && tsmemcmp(digest->data, buf, len) == 0)
                       err = nil;
               freebytes(digest);
       }
       free(buf);
       return err;
}

char*
X509ecdsaverifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, ECdomain *dom, ECpub *pub)
{
       Elem e;
       Elist *el;
       mpint *r, *s;
       char *err;

       r = s = nil;
       err = Ebadsig;
       if(decode(sig, siglen, &e) != ASN_OK)
               goto end;
       if(!is_seq(&e, &el) || elistlen(el) != 2)
               goto end;
       r = asn1mpint(&el->hd);
       if(r == nil)
               goto end;
       el = el->tl;
       s = asn1mpint(&el->hd);
       if(s == nil)
               goto end;
       if(ecdsaverify(dom, pub, edigest, edigestlen, r, s))
               err = nil;
end:
       freevalfields(&e.val);
       mpfree(s);
       mpfree(r);
       return err;
}

static void
copysubject(char *name, int nname, char *subject)
{
       char *e;

       if(name == nil)
               return;
       memset(name, 0, nname);
       if(subject == nil)
               return;
       strncpy(name, subject, nname-1);
       e = strchr(name, ',');
       if(e != nil)
               *e = 0; /* take just CN part of Distinguished Name */
}

ECpub*
X509toECpub(uchar *cert, int ncert, char *name, int nname, ECdomain *dom)
{
       CertX509 *c;
       ECpub *pub;

       c = decode_cert(cert, ncert);
       if(c == nil)
               return nil;
       copysubject(name, nname, c->subject);
       pub = nil;
       if(c->publickey_alg == ALG_ecPublicKey){
               ecdominit(dom, namedcurves[c->curve]);
               pub = ecdecodepub(dom, c->publickey->data, c->publickey->len);
               if(pub == nil)
                       ecdomfree(dom);
       }
       freecert(c);
       return pub;
}

char*
X509ecdsaverify(uchar *cert, int ncert, ECdomain *dom, ECpub *pk)
{
       char *e;
       CertX509 *c;
       int digestlen;
       uchar digest[MAXdlen];

       c = decode_cert(cert, ncert);
       if(c == nil)
               return "cannot decode cert";
       digestlen = digest_certinfo(cert, ncert, digestalg[c->signature_alg], digest);
       if(digestlen <= 0){
               freecert(c);
               return "cannot decode certinfo";
       }
       e = X509ecdsaverifydigest(c->signature->data, c->signature->len, digest, digestlen, dom, pk);
       freecert(c);
       return e;
}

RSApub*
X509toRSApub(uchar *cert, int ncert, char *name, int nname)
{
       CertX509 *c;
       RSApub *pub;

       c = decode_cert(cert, ncert);
       if(c == nil)
               return nil;
       copysubject(name, nname, c->subject);
       pub = nil;
       if(c->publickey_alg == ALG_rsaEncryption)
               pub = asn1toRSApub(c->publickey->data, c->publickey->len);
       freecert(c);
       return pub;
}

char*
X509rsaverify(uchar *cert, int ncert, RSApub *pk)
{
       char *e;
       CertX509 *c;
       int digestlen;
       uchar digest[MAXdlen];

       c = decode_cert(cert, ncert);
       if(c == nil)
               return "cannot decode cert";
       digestlen = digest_certinfo(cert, ncert, digestalg[c->signature_alg], digest);
       if(digestlen <= 0){
               freecert(c);
               return "cannot decode certinfo";
       }
       e = X509rsaverifydigest(c->signature->data, c->signature->len, digest, digestlen, pk);
       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;

       e.tag.class = Universal;
       e.tag.num = INTEGER;
       e.val.tag = VBigInt;
       e.val.u.bigintval = newbytes((mpsignif(p)+8)/8);
       if(p->sign < 0){
               mpint *s = mpnew(e.val.u.bigintval->len*8+1);
               mpleft(mpone, e.val.u.bigintval->len*8, s);
               mpadd(p, s, s);
               mptober(s, e.val.u.bigintval->data, e.val.u.bigintval->len);
               mpfree(s);
       } else {
               mptober(p, e.val.u.bigintval->data, e.val.u.bigintval->len);
       }
       return e;
}

static int
printable(char *s)
{
       int c;

       while((c = (uchar)*s++) != 0){
               if((c >= 'a' && c <= 'z')
               || (c >= 'A' && c <= 'Z')
               || (c >= '0' && c <= '9')
               || strchr("'=()+,-./:? ", c) != nil)
                       continue;
               return 0;
       }
       return 1;
}

#define DirectoryString 0

static Elem
mkstring(char *s, int t)
{
       Elem e;

       if(t == DirectoryString)
               t = printable(s) ? PrintableString : UTF8String;
       e.tag.class = Universal;
       e.tag.num = t;
       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, sizeof(utc), "%.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];
       int     stype;
} Ints7pref;
Ints7pref DN_oid[] = {
       {4, 2, 5, 4, 6, 0, 0, 0,        "C=", PrintableString},
       {4, 2, 5, 4, 8, 0, 0, 0,        "ST=",DirectoryString},
       {4, 2, 5, 4, 7, 0, 0, 0,        "L=", DirectoryString},
       {4, 2, 5, 4, 10, 0, 0, 0,       "O=", DirectoryString},
       {4, 2, 5, 4, 11, 0, 0, 0,       "OU=",DirectoryString},
       {4, 2, 5, 4, 3, 0, 0, 0,        "CN=",DirectoryString},
       {7, 1,2,840,113549,1,9,1,       "E=", IA5String},
       {7, 0,9,2342,19200300,100,1,25, "DC=",IA5String},
};

static Elem
mkname(Ints7pref *oid, char *subj)
{
       return mkset(mkel(mkseq(mkel(mkoid((Ints*)oid), mkel(mkstring(subj, oid->stype), 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);
}

/*
* DigestInfo ::= SEQUENCE {
*      digestAlgorithm AlgorithmIdentifier,
*      digest OCTET STRING }
*/
static Bytes*
encode_digest(DigestAlg *da, uchar *digest)
{
       Bytes *b = nil;
       Elem e = mkseq(
               mkel(mkalg(da->alg),
               mkel(mkoctet(digest, da->len),
               nil)));
       encode(e, &b);
       freevalfields(&e.val);
       return b;
}

int
asn1encodedigest(DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*), uchar *digest, uchar *buf, int len)
{
       Bytes *bytes;
       DigestAlg **dp;

       for(dp = digestalg; *dp != nil; dp++){
               if((*dp)->fun != fun)
                       continue;
               bytes = encode_digest(*dp, digest);
               if(bytes == nil)
                       break;
               if(bytes->len > len){
                       freebytes(bytes);
                       break;
               }
               len = bytes->len;
               memmove(buf, bytes->data, len);
               freebytes(bytes);
               return len;
       }
       return -1;
}

static Elem
mkcont(Elem e, int num)
{
       e = mkseq(mkel(e, nil));
       e.tag.class = Context;
       e.tag.num = num;
       return e;
}

static Elem
mkaltname(char *s)
{
       Elem e;
       int i;

       for(i=0; i<nelem(DN_oid); i++){
               if(strstr(s, DN_oid[i].prefix) != nil)
                       return mkcont(mkDN(s), 4); /* DN */
       }
       e = mkstring(s, IA5String);
       e.tag.class = Context;
       e.tag.num = strchr(s, '@') != nil ? 1 : 2; /* email : DNS */
       return e;
}

static Elist*
mkaltnames(char *alts)
{
       Elist *el;
       char *s, *p;

       if(alts == nil)
               return nil;

       el = nil;
       alts = estrdup(alts);
       for(s = alts; s != nil; s = p){
               while(*s == ' ')
                       s++;
               if(*s == '\0')
                       break;
               if((p = strchr(s, ',')) != nil)
                       *p++ = 0;
               el = mkel(mkaltname(s), el);
       }
       free(alts);
       return el;
}

static Elist*
mkextel(Elem e, Ints *oid, Elist *el)
{
       Bytes *b = nil;

       if(encode(e, &b) == ASN_OK){
               el = mkel(mkseq(
                       mkel(mkoid(oid),
                       mkel(mkoctet(b->data, b->len),
                       nil))), el);
               freebytes(b);
       }
       freevalfields(&e.val);
       return el;
}

static Ints15 oid_subjectAltName = {4, 2, 5, 29, 17 };
static Ints15 oid_extensionRequest = { 7, 1, 2, 840, 113549, 1, 9, 14};

static Elist*
mkextensions(char *alts, int req)
{
       Elist *sl, *xl;

       xl = nil;
       if((sl = mkaltnames(alts)) != nil)
               xl = mkextel(mkseq(sl), (Ints*)&oid_subjectAltName, xl);
       if(xl != nil){
               if(req) return mkel(mkcont(mkseq(
                       mkel(mkoid((Ints*)&oid_extensionRequest),
                       mkel(mkset(mkel(mkseq(xl), nil)), nil))), 0), nil);
               return mkel(mkcont(mkseq(xl), 3), nil);
       }
       return nil;
}

static char*
splitalts(char *s)
{
       int q;

       for(q = 0; *s != '\0'; s++){
               if(*s == '\'')
                       q ^= 1;
               else if(q == 0 && *s == ','){
                       *s++ = 0;
                       return s;
               }
       }
       return nil;
}

static Bytes*
encode_rsapubkey(RSApub *pk)
{
       Bytes *b = nil;
       Elem e = mkseq(
               mkel(mkbigint(pk->n),
               mkel(mpsignif(pk->ek)<32 ? mkint(mptoi(pk->ek)) : mkbigint(pk->ek),
               nil)));
       encode(e, &b);
       freevalfields(&e.val);
       return b;
}

static Bytes*
encode_rsaprivkey(RSApriv *k)
{
       Bytes *b = nil;
       RSApub *pk = &k->pub;
       Elem e = mkseq(
               mkel(mkint(0),
               mkel(mkbigint(pk->n),
               mkel(mpsignif(pk->ek)<32 ? mkint(mptoi(pk->ek)) : mkbigint(pk->ek),
               mkel(mkbigint(k->dk),
               mkel(mkbigint(k->p),
               mkel(mkbigint(k->q),
               mkel(mkbigint(k->kp),
               mkel(mkbigint(k->kq),
               mkel(mkbigint(k->c2),
               nil))))))))));
       encode(e, &b);
       freevalfields(&e.val);
       return b;
}

int
asn1encodeRSApub(RSApub *pk, uchar *buf, int len)
{
       Bytes *b = encode_rsapubkey(pk);
       if(b == nil)
               return -1;
       if(b->len > len){
               freebytes(b);
               werrstr("buffer too small");
               return -1;
       }
       memmove(buf, b->data, len = b->len);
       freebytes(b);
       return len;
}

int
asn1encodeRSApriv(RSApriv *k, uchar *buf, int len)
{
       Bytes *b;
       b = encode_rsaprivkey(k);
       if(b == nil)
               return -1;
       if(b->len > len){
               freebytes(b);
               werrstr("buffer too small");
               return -1;
       }
       memmove(buf, b->data, len = b->len);
       freebytes(b);
       return len;
}

uchar*
X509rsagen(RSApriv *priv, char *subj, ulong valid[2], int *certlen)
{
       int serial = 0, sigalg = ALG_sha256WithRSAEncryption;
       uchar *cert = nil;
       Bytes *certbytes, *pkbytes, *certinfobytes, *sigbytes;
       Elem e, certinfo;
       DigestAlg *da;
       uchar digest[MAXdlen], *buf;
       int buflen;
       mpint *pkcs1;
       char *alts;

       if((pkbytes = encode_rsapubkey(&priv->pub)) == nil)
               return nil;

       subj = estrdup(subj);
       alts = splitalts(subj);

       e = mkseq(
               mkel(mkcont(mkint(2), 0),
               mkel(mkint(serial),
               mkel(mkalg(sigalg),
               mkel(mkDN(subj),
               mkel(mkseq(
                       mkel(mkutc(valid[0]),
                       mkel(mkutc(valid[1]),
                       nil))),
               mkel(mkDN(subj),
               mkel(mkseq(
                       mkel(mkalg(ALG_rsaEncryption),
                       mkel(mkbits(pkbytes->data, pkbytes->len),
                       nil))),
               mkextensions(alts, 0)))))))));
       freebytes(pkbytes);
       if(encode(e, &certinfobytes) != ASN_OK)
               goto errret;

       da = digestalg[sigalg];
       (*da->fun)(certinfobytes->data, certinfobytes->len, digest, 0);
       freebytes(certinfobytes);
       certinfo = e;

       sigbytes = encode_digest(da, digest);
       if(sigbytes == nil)
               goto errret;
       pkcs1 = pkcs1padbuf(sigbytes->data, sigbytes->len, priv->pub.n, 1);
       freebytes(sigbytes);
       if(pkcs1 == nil)
               goto errret;

       rsadecrypt(priv, pkcs1, pkcs1);
       buflen = mptobe(pkcs1, nil, 0, &buf);
       mpfree(pkcs1);
       e = mkseq(
               mkel(certinfo,
               mkel(mkalg(sigalg),
               mkel(mkbits(buf, buflen),
               nil))));
       free(buf);
       if(encode(e, &certbytes) != ASN_OK)
               goto errret;
       if(certlen != nil)
               *certlen = certbytes->len;
       cert = (uchar*)certbytes;
       memmove(cert, certbytes->data, certbytes->len);
errret:
       freevalfields(&e.val);
       free(subj);
       return cert;
}

uchar*
X509rsareq(RSApriv *priv, char *subj, int *certlen)
{
       /* RFC 2314, PKCS #10 Certification Request Syntax */
       int version = 0, sigalg = ALG_sha256WithRSAEncryption;
       uchar *cert = nil;
       Bytes *certbytes, *pkbytes, *certinfobytes, *sigbytes;
       Elem e, certinfo;
       DigestAlg *da;
       uchar digest[MAXdlen], *buf;
       int buflen;
       mpint *pkcs1;
       char *alts;

       if((pkbytes = encode_rsapubkey(&priv->pub)) == nil)
               return nil;

       subj = estrdup(subj);
       alts = splitalts(subj);

       e = mkseq(
               mkel(mkint(version),
               mkel(mkDN(subj),
               mkel(mkseq(
                       mkel(mkalg(ALG_rsaEncryption),
                       mkel(mkbits(pkbytes->data, pkbytes->len),
                       nil))),
               mkextensions(alts, 1)))));
       freebytes(pkbytes);
       if(encode(e, &certinfobytes) != ASN_OK)
               goto errret;
       da = digestalg[sigalg];
       (*da->fun)(certinfobytes->data, certinfobytes->len, digest, 0);
       freebytes(certinfobytes);
       certinfo = e;

       sigbytes = encode_digest(da, digest);
       if(sigbytes == nil)
               goto errret;
       pkcs1 = pkcs1padbuf(sigbytes->data, sigbytes->len, priv->pub.n, 1);
       freebytes(sigbytes);
       if(pkcs1 == nil)
               goto errret;

       rsadecrypt(priv, pkcs1, pkcs1);
       buflen = mptobe(pkcs1, nil, 0, &buf);
       mpfree(pkcs1);
       e = mkseq(
               mkel(certinfo,
               mkel(mkalg(sigalg),
               mkel(mkbits(buf, buflen),
               nil))));
       free(buf);
       if(encode(e, &certbytes) != ASN_OK)
               goto errret;
       if(certlen != nil)
               *certlen = certbytes->len;
       cert = (uchar*)certbytes;
       memmove(cert, certbytes->data, certbytes->len);
errret:
       freevalfields(&e.val);
       free(subj);
       return cert;
}

static void
digestSPKI(int alg, uchar *pubkey, int npubkey, DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*), uchar *digest)
{
       Bytes *b = nil;
       Elem e = mkseq(mkel(mkalg(alg), mkel(mkbits(pubkey, npubkey), nil)));
       encode(e, &b);
       freevalfields(&e.val);
       (*fun)(b->data, b->len, digest, nil);
       freebytes(b);
}

int
X509digestSPKI(uchar *cert, int ncert, DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*), uchar *digest)
{
       CertX509 *c;

       c = decode_cert(cert, ncert);
       if(c == nil){
               werrstr("cannot decode cert");
               return -1;
       }
       digestSPKI(c->publickey_alg, c->publickey->data, c->publickey->len, fun, digest);
       freecert(c);
       return 0;
}

static char*
tagdump(Tag tag)
{
       static char buf[32];

       if(tag.class != Universal){
               snprint(buf, sizeof(buf), "class%d,num%d", tag.class, tag.num);
               return buf;
       }
       switch(tag.num){
       case BOOLEAN: return "BOOLEAN";
       case INTEGER: return "INTEGER";
       case BIT_STRING: return "BIT STRING";
       case OCTET_STRING: return "OCTET STRING";
       case NULLTAG: return "NULLTAG";
       case OBJECT_ID: return "OID";
       case ObjectDescriptor: return "OBJECT_DES";
       case EXTERNAL: return "EXTERNAL";
       case REAL: return "REAL";
       case ENUMERATED: return "ENUMERATED";
       case EMBEDDED_PDV: return "EMBEDDED PDV";
       case SEQUENCE: return "SEQUENCE";
       case SETOF: return "SETOF";
       case UTF8String: return "UTF8String";
       case NumericString: return "NumericString";
       case PrintableString: return "PrintableString";
       case TeletexString: return "TeletexString";
       case VideotexString: return "VideotexString";
       case IA5String: return "IA5String";
       case UTCTime: return "UTCTime";
       case GeneralizedTime: return "GeneralizedTime";
       case GraphicString: return "GraphicString";
       case VisibleString: return "VisibleString";
       case GeneralString: return "GeneralString";
       case UniversalString: return "UniversalString";
       case BMPString: return "BMPString";
       default:
               snprint(buf, sizeof(buf), "Universal,num%d", tag.num);
               return buf;
       }
}

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[%d]...", v.u.bitstringval->len*8 - v.u.bitstringval->unusedbits); 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;
       CertX509 *c;
       RSApub *rsapub;
       ECpub *ecpub;
       ECdomain ecdom;
       int digestlen;
       uchar digest[MAXdlen];

       print("begin X509dump\n");
       c = decode_cert(cert, ncert);
       if(c == nil){
               print("cannot decode cert\n");
               return;
       }

       digestlen = digest_certinfo(cert, ncert, digestalg[c->signature_alg], digest);
       if(digestlen <= 0){
               freecert(c);
               print("cannot decode certinfo\n");
               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);
       print("sigalg=%d digest=%.*H\n", c->signature_alg, digestlen, digest);
       print("publickey_alg=%d pubkey[%d] %.*H\n", c->publickey_alg, c->publickey->len,
               c->publickey->len, c->publickey->data);

       switch(c->publickey_alg){
       case ALG_rsaEncryption:
               rsapub = asn1toRSApub(c->publickey->data, c->publickey->len);
               if(rsapub != nil){
                       print("rsa pubkey e=%B n(%d)=%B\n", rsapub->ek, mpsignif(rsapub->n), rsapub->n);
                       e = X509rsaverifydigest(c->signature->data, c->signature->len,
                               digest, digestlen, rsapub);
                       if(e==nil)
                               e = "nil (meaning ok)";
                       print("self-signed X509rsaverifydigest returns: %s\n", e);
                       rsapubfree(rsapub);
               }
               break;
       case ALG_ecPublicKey:
               ecdominit(&ecdom, namedcurves[c->curve]);
               ecpub = ecdecodepub(&ecdom, c->publickey->data, c->publickey->len);
               if(ecpub != nil){
                       e = X509ecdsaverifydigest(c->signature->data, c->signature->len,
                               digest, digestlen, &ecdom, ecpub);
                       if(e==nil)
                               e = "nil (meaning ok)";
                       print("self-signed X509ecdsaverifydigest returns: %s\n", e);
                       ecpubfree(ecpub);
               }
               ecdomfree(&ecdom);
               break;
       }

       digestSPKI(c->publickey_alg, c->publickey->data, c->publickey->len, sha2_256, digest);
       print("publickey_thumbprint sha256=%.*[\n", SHA2_256dlen, digest);

       sha2_256(cert, ncert, digest, nil);
       print("cert_thumbprint sha256=%.*[\n", SHA2_256dlen, digest);

       sha1(cert, ncert, digest, nil);
       print("cert_thumbprint sha1=%.*H\n", SHA1dlen, digest);

       freecert(c);
       print("end X509dump\n");
}