Plan 9 from Bell Labs’s /usr/web/sources/contrib/gabidiaz/root/sys/src/cmd/snmpfs/ber.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include "ber.h"

void *
emalloc(int n)
{
	void *p;
	if(n==0)
		n=1;
	p = malloc(n);
	if(p == nil){
		fprint(2,"out of memory");
		abort();
		exits("out of memory");
	}
	memset(p, 0, n);
	setmalloctag(p, getcallerpc(&n));
	return p;
}

char*
estrdup(char *s)
{
	char *d, *d0;

	if(!s)
		return 0;
	d0 = emalloc(strlen(s)+1);
	d = d0;
	while(*d++ = *s++)
		;
	setmalloctag(d0,getcallerpc(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.
 */
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.
 */
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.
 */
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) */
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 */
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++;
		if ( v > 0x30 ) // MOD: To support unknown classes
			ptag->class = v; 
		else
			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;
	if ( ptag->class > 0x30 ) {	// MOD: To support unknown classes
		ptag->num = ptag->class;
		err = ASN_OK;
	}
	*pp = p;
	return err;
}

/* Decode a length field */
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  */
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 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.
 */
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.
 */
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);
	}
	/* MOD i've got a null value that must be decoded, so p == pend is valid, . . . */
	//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)
 */
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)
 */
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.
 */
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.
 */

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)
			if ( tag.class > 0x30 )	/* MOD for custom objects */
				*p = v;
			else
				*p = (v|tag.num);
		p++;
	}
	else {
		if(!lenonly)
			if ( tag.class > 0x30 ) /* MOD for custom objects */
				*p = v;
			else
				*p = (v|31);
		p++;
		if(tag.num < 0)
			return ASN_EINVAL;
		

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

int
val_enc(uchar** pp, Elem e, int *pconstr, int lenonly)
{
	int err;
	uchar* p;
	int kind;
	int cl;
	int v;
	Bytes* bb = nil;
	Bits* bits;
	Ints* oid;
	int k;
	Elist* el;
	char* s;

	p = *pp;
	err = ASN_OK;
	kind = e.tag.num;
	cl = e.tag.class;
	*pconstr = 0;
	if(cl != Universal) {
		switch(e.val.tag) {
		case VBool:
			kind = BOOLEAN;
			break;
		case VInt:
			kind = INTEGER;
			break;
		case VBigInt:
			kind = INTEGER;
			break;
		case VOctets:
			kind = OCTET_STRING;
			break;
		case VReal:
			kind = REAL;
			break;
		case VOther:
			kind = OCTET_STRING;
			break;
		case VBitString:
			kind = BIT_STRING;
			break;
		case VNull:
			kind = NULLTAG;
			break;
		case VObjId:
			kind = OBJECT_ID;
			break;
		case VString:
			kind = UniversalString;
			break;
		case VSeq:
			kind = SEQUENCE;
			break;
		case VSet:
			kind = SETOF;
			break;
		}
	}
	switch(kind) {
	case BOOLEAN:
		if(is_int(&e, &v)) {
			if(v != 0)
				v = 255;
			 int_enc(&p, v, 1, lenonly);
		}
		else
			err = ASN_EINVAL;
		break;

	case INTEGER:
	case ENUMERATED:
		if(is_int(&e, &v))
			int_enc(&p, v, 0, lenonly);
		else {
			if(is_bigint(&e, &bb)) {
				if(!lenonly)
					memmove(p, bb->data, bb->len);
				p += bb->len;
			}
			else
				err = ASN_EINVAL;
		}
		break;

	case BIT_STRING:
		if(is_bitstring(&e, &bits)) {
			if(bits->len == 0) {
				if(!lenonly)
					*p = 0;
				p++;
			}
			else {
				v = bits->unusedbits;
				if(v < 0 || v > 7)
					err = ASN_EINVAL;
				else {
					if(!lenonly) {
						*p = v;
						memmove(p+1, bits->data, bits->len);
					}
					p += 1 + bits->len;
				}
			}
		}
		else
			err = ASN_EINVAL;
		break;

	case OCTET_STRING:
	case ObjectDescriptor:
	case EXTERNAL:
	case REAL:
	case EMBEDDED_PDV:
		bb = nil;
		switch(e.val.tag) {
		case VOctets:
			bb = e.val.u.octetsval;
			break;
		case VReal:
			bb = e.val.u.realval;
			break;
		case VOther:
			bb = e.val.u.otherval;
			break;
		}
		if(bb != nil) {
			if(!lenonly)
				memmove(p, bb->data, bb->len);
			p += bb->len;
		}
			else
				err = ASN_EINVAL;
		break;

	case NULLTAG:
		break;

	case OBJECT_ID:
		if(is_oid(&e, &oid)) {
			for(k = 0; k < oid->len; k++) {
				 
				v = oid->data[k];

				if(k == 0) {
					v *= 40;
					if(oid->len > 1)
						v += oid->data[++k];
				}
				
				uint7_enc(&p, v, lenonly);
			}
		}
		else
			err = ASN_EINVAL;
		break;

	case SEQUENCE:
	case SETOF:
		el = nil;
		if(e.val.tag == VSeq)
			el = e.val.u.seqval;
		else if(e.val.tag == VSet)
			el = e.val.u.setval;
		else
			err = ASN_EINVAL;
		if(el != nil) {
			*pconstr = CONSTR_MASK;
			for(; el != nil; el = el->tl) {
				err = enc(&p, el->hd, lenonly);
				if(err != ASN_OK)
					break;
			}
		}
		break;

	case NumericString:
	case PrintableString:
	case TeletexString:
	case VideotexString:
	case IA5String:
	case UTCTime:
	case GeneralizedTime:
	case GraphicString:
	case VisibleString:
	case GeneralString:
	case UniversalString:
	case BMPString:
		if(e.val.tag == VString) {
			s = e.val.u.stringval;
			if(s != nil) {
				v = strlen(s);
				if(!lenonly)
					memmove(p, s, v);
				p += v;
			}
		}
		else
			err = ASN_EINVAL;
		break;

	default:
		err = ASN_EINVAL;
	}
	*pp = p;
	return err;
}

/*
 * Encode num as unsigned 7 bit values with top bit 1 on all bytes
 * except last, only putting in bytes if !lenonly.
 */
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.
 */
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;
}

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

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

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

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

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

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

int
is_string(Elem* pe, char** pstring)
{
	if(pe->tag.class == Universal) {
		switch(pe->tag.num) {
		case NumericString:
		case PrintableString:
		case TeletexString:
		case VideotexString:
		case IA5String:
		case GraphicString:
		case VisibleString:
		case GeneralString:
		case UniversalString:
		case BMPString:
			if(pe->val.tag == VString) {
				*pstring = pe->val.u.stringval;
				return 1;
			}
		}
	}
	return 0;
}

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)
 */
Bytes*
newbytes(int len)
{
	Bytes* ans;
	ans = (Bytes*)emalloc(OFFSETOF(data[0], Bytes) + len);
	ans->len = len;
	setmalloctag(ans,getcallerpc(&len));
	return ans;
}

/*
 * newbytes(len), with data initialized from buf
 */
Bytes*
makebytes(uchar* buf, int len)
{
	Bytes* ans;

	ans = newbytes(len);
	memmove(ans->data, buf, len);
	setmalloctag(ans,getcallerpc(buf));
	return ans;
}

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.
 */
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 */
Ints*
newints(int len)
{
	Ints* ans;

	ans = (Ints*)emalloc(OFFSETOF(data[0], Ints) + len*sizeof(int));
	ans->len = len;
	setmalloctag(ans,getcallerpc(&len));
	return ans;
}

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

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

void
freeints(Ints* b)
{
	if(b != nil)
		free(b);
}

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

	ans = (Bits*)emalloc(OFFSETOF(data[0], Bits) + len);
	ans->len = len;
	ans->unusedbits = 0;
	return ans;
}

Bits*
makebits(uchar* buf, int len, int unusedbits)
{
	Bits* ans;

	ans = newbits(len);
	memmove(ans->data, buf, len);
	ans->unusedbits = unusedbits;
	return ans;
}

void
freebits(Bits* b)
{
	if(b != nil)
		free(b);
}

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

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 */
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) */
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 */
/* elem constructors */
Elem
Null(void)
{
	Elem e;

	e.tag.class = Universal;
	e.tag.num = NULLTAG;
	e.val.tag = VNull;
	return e;
}

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

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

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

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

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

Elem
mkutc(long t)
{
	Elem e;
	char utc[50];
	Tm *tm = gmtime(t);

	e.tag.class = Universal;
	e.tag.num = UTCTime;
	e.val.tag = VString;
	snprint(utc, 50, "%.2d%.2d%.2d%.2d%.2d%.2dZ",
		tm->year % 100, tm->mon+1, tm->mday, tm->hour, tm->min, tm->sec);
	e.val.u.stringval = estrdup(utc);
	return e;
}

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

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

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

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="},
};

Elem
mkname(Ints7pref *oid, char *subj)
{
	return mkset(mkel(mkseq(mkel(mkoid((Ints*)oid), mkel(mkstring(subj), nil))), nil));
}

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

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


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

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.