9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
* [9fans] APE libsec
@ 2013-02-04 11:31 Yaroslav
  2013-02-04 11:44 ` Yaroslav
  2013-02-04 13:06 ` lucio
  0 siblings, 2 replies; 13+ messages in thread
From: Yaroslav @ 2013-02-04 11:31 UTC (permalink / raw)
  To: 9fans

[-- Attachment #1: Type: text/plain, Size: 842 bytes --]

Hi,

I've noticed there are libsec/libmp became available for APE programs, and
dove into replacing OpenSSL calls with libsec equivalents. However, there
are still a reference to the native libc lurks somewhere in the ports as
the linker reports conflicts like these:

checkenv: incompatible type signatures
a9aa533a(/386/lib/ape/libap.a(freopen)) and
aebfd74f(/386/lib/libc.a(calloc)) for open
ppanic: incompatible type signatures
4dcf09c4(/386/lib/ape/libap.a(_IO_putc)) and
55e8553c(/386/lib/libc.a(calloc)) for write
malloc: /386/lib/libc.a(calloc): redefinition: malloc
(1070) TEXT malloc+0(SB),$24
free: /386/lib/libc.a(calloc): redefinition: free
...

Could someone suggest a method for tracking down the library/object file
which contains the loader instruction to load /386/lib/libc.a?

Thanks.
--
- Yaroslav

[-- Attachment #2: Type: text/html, Size: 1136 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9fans] APE libsec
  2013-02-04 11:31 [9fans] APE libsec Yaroslav
@ 2013-02-04 11:44 ` Yaroslav
  2013-02-04 16:11   ` Jeff Sickel
  2013-02-04 13:06 ` lucio
  1 sibling, 1 reply; 13+ messages in thread
From: Yaroslav @ 2013-02-04 11:44 UTC (permalink / raw)
  To: 9fans

[-- Attachment #1: Type: text/plain, Size: 1796 bytes --]

Aha! This ape/mp.h references the native lib not ape one. Applying the fix
below and recompiling ape/lib/mp and ape/lib/sec eliminates the reference
to /386/lib/libc.a, leaving just a few of unresolved references:
pcc -o 8.out bitmap.8 cache.8 channels.8 cliprdr.8 ewmhints.8 frpc.8 iso.8
licence.8 mcs.8 mppc.8 orders.8 pstcache.8 rdesktop.8 rdp.8 rdp5.8
seamless.8 secure.8 tcp.8 snarf.8 xkeymap.8 xwin.8
mpkaratsuba: undefined: mallocz
_PIPE: argv0: not defined
pcc: 8l: 8l 97689: error

The mp.h fix:

/n/sources/plan9/sys/include/ape/mp.h:5,12 - /sys/include/ape/mp.h:5,12
  #ifndef __LIBMP_H_
  #define __LIBMP_H_

- #pragma src "/sys/src/libmp"
- #pragma lib "libmp.a"
+ #pragma src "/sys/src/ape/lib/mp"
+ #pragma lib "/$M/lib/ape/libmp.a"

  typedef unsigned int mpdigit; /* from /$objtype/include/u.h */




2013/2/4 Yaroslav <yarikos@gmail.com>

> Hi,
>
> I've noticed there are libsec/libmp became available for APE programs, and
> dove into replacing OpenSSL calls with libsec equivalents. However, there
> are still a reference to the native libc lurks somewhere in the ports as
> the linker reports conflicts like these:
>
> checkenv: incompatible type signatures
> a9aa533a(/386/lib/ape/libap.a(freopen)) and
> aebfd74f(/386/lib/libc.a(calloc)) for open
> ppanic: incompatible type signatures
> 4dcf09c4(/386/lib/ape/libap.a(_IO_putc)) and
> 55e8553c(/386/lib/libc.a(calloc)) for write
> malloc: /386/lib/libc.a(calloc): redefinition: malloc
> (1070) TEXT malloc+0(SB),$24
> free: /386/lib/libc.a(calloc): redefinition: free
> ...
>
> Could someone suggest a method for tracking down the library/object file
> which contains the loader instruction to load /386/lib/libc.a?
>
> Thanks.
> --
> - Yaroslav
>



--
- Yaroslav

[-- Attachment #2: Type: text/html, Size: 3148 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9fans] APE libsec
  2013-02-04 11:31 [9fans] APE libsec Yaroslav
  2013-02-04 11:44 ` Yaroslav
@ 2013-02-04 13:06 ` lucio
  2013-02-04 15:46   ` erik quanstrom
  1 sibling, 1 reply; 13+ messages in thread
From: lucio @ 2013-02-04 13:06 UTC (permalink / raw)
  To: 9fans

> Could someone suggest a method for tracking down the library/object file
> which contains the loader instruction to load /386/lib/libc.a?

It's not that you don't want libc references (what you would you have
instead?), you need to recompile some modules so they are up to date.

Point me to your work, in general I find it faster to fix these things
than to figure how to explain them (I can explain afterwards), if you
don't mind.

I have noted your next message, I think you chopped off more than was
required.

++L




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9fans] APE libsec
  2013-02-04 13:06 ` lucio
@ 2013-02-04 15:46   ` erik quanstrom
  2013-02-04 15:50     ` lucio
  0 siblings, 1 reply; 13+ messages in thread
From: erik quanstrom @ 2013-02-04 15:46 UTC (permalink / raw)
  To: 9fans

On Mon Feb  4 08:07:57 EST 2013, lucio@proxima.alt.za wrote:
> > Could someone suggest a method for tracking down the library/object file
> > which contains the loader instruction to load /386/lib/libc.a?
>
> It's not that you don't want libc references (what you would you have
> instead?), you need to recompile some modules so they are up to date.

it is exactly that you don't want libc references in ape!
libc will drag in its own functions, and bring in the incorrect
versions of things.

- erik



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9fans] APE libsec
  2013-02-04 15:46   ` erik quanstrom
@ 2013-02-04 15:50     ` lucio
  0 siblings, 0 replies; 13+ messages in thread
From: lucio @ 2013-02-04 15:50 UTC (permalink / raw)
  To: 9fans

> it is exactly that you don't want libc references in ape!
> libc will drag in its own functions, and bring in the incorrect
> versions of things.

Oops, I forgot about libap.a!

Sorry, everyone.

++L




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9fans] APE libsec
  2013-02-04 11:44 ` Yaroslav
@ 2013-02-04 16:11   ` Jeff Sickel
  2013-02-05  8:32     ` yaroslav
  0 siblings, 1 reply; 13+ messages in thread
From: Jeff Sickel @ 2013-02-04 16:11 UTC (permalink / raw)
  To: Fans of the OS Plan 9 from Bell Labs

Whew, the libsec.h file was correct on the first push.

APE builds of libmp and libsec are being rolled in to support a new Python 2.7.3+
port.  Like what you've started doing, the new Python port will not have OpenSSL
linked in and instead uses libsec.

-jas

On Feb 4, 2013, at 5:44 AM, Yaroslav <yarikos@gmail.com> wrote:

> The mp.h fix:
> 
> /n/sources/plan9/sys/include/ape/mp.h:5,12 - /sys/include/ape/mp.h:5,12
>   #ifndef __LIBMP_H_
>   #define __LIBMP_H_
>   
> - #pragma	src	"/sys/src/libmp"
> - #pragma	lib	"libmp.a"
> + #pragma	src	"/sys/src/ape/lib/mp"
> + #pragma	lib	"/$M/lib/ape/libmp.a"
>   
>   typedef unsigned int	mpdigit;	/* from /$objtype/include/u.h */




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9fans] APE libsec
  2013-02-04 16:11   ` Jeff Sickel
@ 2013-02-05  8:32     ` yaroslav
  2013-02-05  8:41       ` Jens Staal
  2013-02-05 15:38       ` erik quanstrom
  0 siblings, 2 replies; 13+ messages in thread
From: yaroslav @ 2013-02-05  8:32 UTC (permalink / raw)
  To: jas; +Cc: 9fans

[-- Attachment #1: Type: text/plain, Size: 1434 bytes --]

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

[-- Attachment #2: x509-ape.c --]
[-- Type: text/plain, Size: 55116 bytes --]

#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");
}

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9fans] APE libsec
  2013-02-05  8:32     ` yaroslav
@ 2013-02-05  8:41       ` Jens Staal
  2013-02-05 15:28         ` erik quanstrom
  2013-02-05 15:38       ` erik quanstrom
  1 sibling, 1 reply; 13+ messages in thread
From: Jens Staal @ 2013-02-05  8:41 UTC (permalink / raw)
  To: Fans of the OS Plan 9 from Bell Labs

Sorry if this may sound ignorant and clueless, but would a similar port of the
Plan9 thread(2) to APE be possible - in particular in a form where stuff are
exposed through the headers as a standard pthreads solution?

Right now I have noticed that I use GNU Pth extensively under APE, but a more
"native" pthread implementation would be cool.





^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9fans] APE libsec
  2013-02-05  8:41       ` Jens Staal
@ 2013-02-05 15:28         ` erik quanstrom
  0 siblings, 0 replies; 13+ messages in thread
From: erik quanstrom @ 2013-02-05 15:28 UTC (permalink / raw)
  To: 9fans

On Tue Feb  5 03:42:51 EST 2013, staal1978@gmail.com wrote:
> Sorry if this may sound ignorant and clueless, but would a similar port of the
> Plan9 thread(2) to APE be possible - in particular in a form where stuff are
> exposed through the headers as a standard pthreads solution?
>
> Right now I have noticed that I use GNU Pth extensively under APE, but a more
> "native" pthread implementation would be cool.

we haven't investigated.  you're welcome to try.

the immediate goal is to get hg and python working properly
on all arches we've currently got.  we're desperately trying not
to drag anything else along.

- erik



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9fans] APE libsec
  2013-02-05  8:32     ` yaroslav
  2013-02-05  8:41       ` Jens Staal
@ 2013-02-05 15:38       ` erik quanstrom
  2013-02-08 15:35         ` Yaroslav
  1 sibling, 1 reply; 13+ messages in thread
From: erik quanstrom @ 2013-02-05 15:38 UTC (permalink / raw)
  To: 9fans

looks good, but i'd do libsec at the same time.
unless you know of a compelling reason for these
to support a different set of algorithms?

- erik

 ; diffy -c /sys/src/libsec/port/x509.c /sys/src/ape/lib/sec/port/x509-ape.c
diff -c /n/dump/2013/0205/sys/src/libsec/port/x509.c /sys/src/libsec/port/x509.c
/n/dump/2013/0205/sys/src/libsec/port/x509.c:1582,1587 - /sys/src/libsec/port/x509.c:1582,1589
  	ALG_md5WithRSAEncryption,
  	ALG_sha1WithRSAEncryption,
  	ALG_sha1WithRSAEncryptionOiw,
+ 	ALG_sha256WithRSAEncryption,
+ 	ALG_shaWithRSASignatureOiw,
  	ALG_md5,
  	NUMALGS
  };
/n/dump/2013/0205/sys/src/libsec/port/x509.c:1595,1600 - /sys/src/libsec/port/x509.c:1597,1604
  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_sha256WithRSAEncryption ={7, 1, 2, 840, 113549, 1, 1, 11 };
+ static Ints7 oid_shaWithRSASignatureOiw ={6, 1, 3, 14, 3, 2, 15 };
  static Ints7 oid_md5 ={6, 1, 2, 840, 113549, 2, 5, 0 };
  static Ints *alg_oid_tab[NUMALGS+1] = {
  	(Ints*)&oid_rsaEncryption,
/n/dump/2013/0205/sys/src/libsec/port/x509.c:1603,1608 - /sys/src/libsec/port/x509.c:1607,1614
  	(Ints*)&oid_md5WithRSAEncryption,
  	(Ints*)&oid_sha1WithRSAEncryption,
  	(Ints*)&oid_sha1WithRSAEncryptionOiw,
+ 	(Ints*)&oid_sha256WithRSAEncryption,
+ 	(Ints*)&oid_shaWithRSASignatureOiw,
  	(Ints*)&oid_md5,
  	nil
  };
diff -c /n/dump/2013/0205/sys/src/ape/lib/sec/port/x509-ape.c /sys/src/ape/lib/sec/port/x509-ape.c
/n/dump/2013/0205/sys/src/ape/lib/sec/port/x509-ape.c:1582,1587 - /sys/src/ape/lib/sec/port/x509-ape.c:1582,1589
  	ALG_md5WithRSAEncryption,
  	ALG_sha1WithRSAEncryption,
  	ALG_sha1WithRSAEncryptionOiw,
+ 	ALG_sha256WithRSAEncryption,
+ 	ALG_shaWithRSASignatureOiw,
  	ALG_md5,
  	NUMALGS
  };
/n/dump/2013/0205/sys/src/ape/lib/sec/port/x509-ape.c:1595,1600 - /sys/src/ape/lib/sec/port/x509-ape.c:1597,1604
  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_sha256WithRSAEncryption ={7, 1, 2, 840, 113549, 1, 1, 11 };
+ static Ints7 oid_shaWithRSASignatureOiw ={6, 1, 3, 14, 3, 2, 15 };
  static Ints7 oid_md5 ={6, 1, 2, 840, 113549, 2, 5, 0 };
  static Ints *alg_oid_tab[NUMALGS+1] = {
  	(Ints*)&oid_rsaEncryption,
/n/dump/2013/0205/sys/src/ape/lib/sec/port/x509-ape.c:1603,1608 - /sys/src/ape/lib/sec/port/x509-ape.c:1607,1614
  	(Ints*)&oid_md5WithRSAEncryption,
  	(Ints*)&oid_sha1WithRSAEncryption,
  	(Ints*)&oid_sha1WithRSAEncryptionOiw,
+ 	(Ints*)&oid_sha256WithRSAEncryption,
+ 	(Ints*)&oid_shaWithRSASignatureOiw,
  	(Ints*)&oid_md5,
  	nil
  };



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9fans] APE libsec
  2013-02-05 15:38       ` erik quanstrom
@ 2013-02-08 15:35         ` Yaroslav
  2013-02-09  2:46           ` Jeff Sickel
  0 siblings, 1 reply; 13+ messages in thread
From: Yaroslav @ 2013-02-08 15:35 UTC (permalink / raw)
  To: Fans of the OS Plan 9 from Bell Labs

[-- Attachment #1: Type: text/plain, Size: 242 bytes --]

2013/2/5 erik quanstrom <quanstro@quanstro.net>

> looks good, but i'd do libsec at the same time.
> unless you know of a compelling reason for these
> to support a different set of algorithms?
>

Agree.

/n/sources/patch/x509-oids

[-- Attachment #2: Type: text/html, Size: 716 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9fans] APE libsec
  2013-02-08 15:35         ` Yaroslav
@ 2013-02-09  2:46           ` Jeff Sickel
  2013-02-12 21:27             ` michaelian ennis
  0 siblings, 1 reply; 13+ messages in thread
From: Jeff Sickel @ 2013-02-09  2:46 UTC (permalink / raw)
  To: Fans of the OS Plan 9 from Bell Labs


On Feb 8, 2013, at 9:35 AM, Yaroslav <yarikos@gmail.com> wrote:

>
> 2013/2/5 erik quanstrom <quanstro@quanstro.net>
> looks good, but i'd do libsec at the same time.
> unless you know of a compelling reason for these
> to support a different set of algorithms?
>
> Agree.
>
> /n/sources/patch/x509-oids

I really think the APE variant should be picked up as
a(n s)ed script in the mkfile.  That way we can track
a single x509.c file.




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9fans] APE libsec
  2013-02-09  2:46           ` Jeff Sickel
@ 2013-02-12 21:27             ` michaelian ennis
  0 siblings, 0 replies; 13+ messages in thread
From: michaelian ennis @ 2013-02-12 21:27 UTC (permalink / raw)
  To: Fans of the OS Plan 9 from Bell Labs

I notice that there are several ANSI C implementations of crypto
routines contained in pycrypto if that is of any use.

https://github.com/dlitz/pycrypto/tree/master/src

Ian



^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2013-02-12 21:27 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-02-04 11:31 [9fans] APE libsec Yaroslav
2013-02-04 11:44 ` Yaroslav
2013-02-04 16:11   ` Jeff Sickel
2013-02-05  8:32     ` yaroslav
2013-02-05  8:41       ` Jens Staal
2013-02-05 15:28         ` erik quanstrom
2013-02-05 15:38       ` erik quanstrom
2013-02-08 15:35         ` Yaroslav
2013-02-09  2:46           ` Jeff Sickel
2013-02-12 21:27             ` michaelian ennis
2013-02-04 13:06 ` lucio
2013-02-04 15:46   ` erik quanstrom
2013-02-04 15:50     ` lucio

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).