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