Could you please include following changes to
/sys/src/ape/lib/sec/port/x509-ape.c: this is for X509toRSApub(2) to
accept some ms-generated certs:
/n/sources/plan9/sys/src/ape/lib/sec/port/x509-ape.c:1583,1588 - 
/sys/src/ape/lib/sec/port/x509-ape.c:1583,1590
        ALG_sha1WithRSAEncryption,
        ALG_sha1WithRSAEncryptionOiw,
        ALG_md5,
+       ALG_sha256WithRSAEncryption,
+       ALG_shaWithRSASignatureOiw,
        NUMALGS
  };
  typedef struct Ints7 {
/n/sources/plan9/sys/src/ape/lib/sec/port/x509-ape.c:1596,1601 - 
/sys/src/ape/lib/sec/port/x509-ape.c:1598,1605
  static Ints7 oid_sha1WithRSAEncryption ={7, 1, 2, 840, 113549, 1, 1, 5 };
  static Ints7 oid_sha1WithRSAEncryptionOiw ={6, 1, 3, 14, 3, 2, 29 };
  static Ints7 oid_md5 ={6, 1, 2, 840, 113549, 2, 5, 0 };
+ static Ints7 oid_sha256WithRSAEncryption ={7, 1, 2, 840, 113549, 1, 1, 11 };
+ static Ints7 oid_shaWithRSASignatureOiw ={6, 1, 3, 14, 3, 2, 15 };
  static Ints *alg_oid_tab[NUMALGS+1] = {
        (Ints*)&oid_rsaEncryption,
        (Ints*)&oid_md2WithRSAEncryption,
/n/sources/plan9/sys/src/ape/lib/sec/port/x509-ape.c:1604,1609 - 
/sys/src/ape/lib/sec/port/x509-ape.c:1608,1615
        (Ints*)&oid_sha1WithRSAEncryption,
        (Ints*)&oid_sha1WithRSAEncryptionOiw,
        (Ints*)&oid_md5,
+       (Ints*)&oid_sha256WithRSAEncryption,
+       (Ints*)&oid_shaWithRSASignatureOiw,
        nil
  };
  static DigestFun digestalg[NUMALGS+1] = { md5, md5, md5, md5, sha1, sha1, 
md5, nil };
#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 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[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);
        setmalloctag(p, getcallerpc(&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);
                                if(val.tag == VSeq || val.tag == VSet)
                                        setmalloctag(val.u.seqval, 
getcallerpc(&pp));
                        }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);
                setmalloctag(vl, getcallerpc(&pp));
                if(err == ASN_OK) {
                        pval->tag = VSeq ;
                        pval->u.seqval = vl;
                }
                break;

        case SETOF:
                err = seq_decode(&p, pend, length, isconstr, &vl);
                setmalloctag(vl, getcallerpc(&pp));
                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:
                /* 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;
        setmalloctag(ans, getcallerpc(&pp));
        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)
{
        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 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;

        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));
        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:
                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_sha1WithRSAEncryptionOiw,
        ALG_md5,
        ALG_sha256WithRSAEncryption,
        ALG_shaWithRSASignatureOiw,
        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_sha1WithRSAEncryptionOiw ={6, 1, 3, 14, 3, 2, 29 };
static Ints7 oid_md5 ={6, 1, 2, 840, 113549, 2, 5, 0 };
static Ints7 oid_sha256WithRSAEncryption ={7, 1, 2, 840, 113549, 1, 1, 11 };
static Ints7 oid_shaWithRSASignatureOiw ={6, 1, 3, 14, 3, 2, 15 };
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_md5,
        (Ints*)&oid_sha256WithRSAEncryption,
        (Ints*)&oid_shaWithRSASignatureOiw,
        nil
};
static DigestFun digestalg[NUMALGS+1] = { md5, md5, md5, md5, sha1, sha1, md5, 
nil };

static void
freecert(CertX509* c)
{
        if(!c) return;
        if(c->issuer != nil)
                free(c->issuer);
        if(c->validity_start != nil)
                free(c->validity_start);
        if(c->validity_end != nil)
                free(c->validity_end);
        if(c->subject != nil)
                free(c->subject);
        freebytes(c->publickey);
        freebytes(c->signature);
        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 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, *l;
        mpint *mp;
        RSApub* key;

        l = nil;
        key = rsapuballoc();
        if(decode(a->data, a->len, &e) != ASN_OK)
                goto errret;
        if(!is_seq(&e, &el) || elistlen(el) != 2)
                goto errret;

        l = el;

        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;

        if(l != nil)
                freeelist(l);
        return key;
errret:
        if(l != nil)
                freeelist(l);
        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)
                return;
        freevalfields(&elem.val);
        if(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;
        char *err;

        err = nil;
        pkcs1buf = nil;

        /* 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);
        buflen = mptobe(pkcs1, nil, 0, &pkcs1buf);
        buf = pkcs1buf;
        if(buflen != nlen || buf[0] != 1) {
                err = "expected 1";
                goto end;
        }
        buf++;
        while(buf[0] == 0xff)
                buf++;
        if(buf[0] != 0) {
                err = "expected 0";
                goto end;
        }
        buf++;
        buflen -= buf-pkcs1buf;
        if(decode(buf, buflen, &e) != ASN_OK || !is_seq(&e, &el) || 
elistlen(el) != 2 ||
                        !is_octetstring(&el->tl->hd, &digest)) {
                err = "signature parse error";
                goto end;
        }
        *psigalg = &el->hd;
        if(memcmp(digest->data, edigest, digest->len) == 0)
                goto end;
        err = "digests did not match";

end:
        if(pkcs1 != nil)
                mpfree(pkcs1);
        if(pkcs1buf != nil)
                free(pkcs1buf);
        return err;
}

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];
        struct tm *tm0 = gmtime(&t);

        e.tag.class = Universal;
        e.tag.num = UTCTime;
        e.val.tag = VString;
        snprint(utc, 50, "%.2d%.2d%.2d%.2d%.2d%.2dZ",
                tm0->tm_year % 100, tm0->tm_mon+1, tm0->tm_mday, tm0->tm_hour, 
tm0->tm_min, tm0->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";
        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:
                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");
}

Reply via email to