xref: /inferno-os/appl/lib/asn1.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement ASN1;
2
3include "sys.m";
4	sys: Sys;
5
6include "asn1.m";
7
8# Masks
9TAG_MASK : con 16r1F;
10CONSTR_MASK : con 16r20;
11CLASS_MASK : con 16rC0;
12
13# Decoding errors
14OK, ESHORT, ETOOBIG, EVALLEN, ECONSTR, EPRIM, EINVAL, EUNIMPL: con iota;
15
16debug : con 0;
17
18init()
19{
20	sys = load Sys Sys->PATH;
21}
22
23# Decode the whole array as a BER encoding of an ASN1 type.
24# If there's an error, the return string will contain the error.
25# Depending on the error, the returned elem may or may not
26# be nil.
27decode(a: array of byte) : (string, ref Elem)
28{
29	(ecode, i, elem) := ber_decode(a, 0, len a);
30	return (errstr(ecode, i, len a), elem);
31}
32
33# Like decode, but continue decoding after first element
34# of array ends.
35decode_seq(a: array of byte) : (string, list of ref Elem)
36{
37	(ecode, i, elist) := seq_decode(a, 0, len a, -1, 1);
38	return (errstr(ecode, i, len a), elist);
39}
40
41# Decode the whole array as a BER encoding of an ASN1 value,
42# (i.e., the part after the tag and length).
43# Assume the value is encoded as universal tag "kind".
44# The constr arg is 1 if the value is constructed, 0 if primitive.
45# If there's an error, the return string will contain the error.
46# Depending on the error, the returned value may or may not
47# be nil.
48decode_value(a: array of byte, kind, constr: int) : (string, ref Value)
49{
50	n := len a;
51	(ecode, i, val) := value_decode(a, 0, n, n, kind, constr);
52	return (errstr(ecode, i, len a), val);
53}
54
55# The rest of the decoding routines take the array (a), the
56# starting position (i), and the ending position +1 (n).
57# They return (err code, new i, [... varies]).
58
59# for debugging
60ber_ind := "";
61ber_ind_save := "";
62
63# Decode an ASN1 (tag, length, value).
64ber_decode(a: array of byte, i, n: int) : (int, int, ref Elem)
65{
66	if(debug) {
67		ber_ind_save = ber_ind;
68		ber_ind = ber_ind + "  ";
69		sys->print("%sber_decode, byte %d\n", ber_ind, i);
70	}
71	err, length: int;
72	tag : Tag;
73	val : ref Value;
74	elem : ref Elem = nil;
75	(err, i, tag) = tag_decode(a, i, n);
76	if(err == OK) {
77		(err, i, length) = length_decode(a, i, n);
78		if(err == OK) {
79			if(debug)
80				sys->print("%sgot tag %s, length %d, now at byte %d\n",
81						ber_ind, tag.tostring(), length, i);
82			if(tag.class == Universal)
83				(err, i, val) = value_decode(a, i, n, length, tag.num, tag.constr);
84			else
85				(err, i, val) = value_decode(a, i, n, length, OCTET_STRING, 0);
86			if(val != nil)
87				elem = ref Elem(tag, val);
88		}
89	}
90	if(debug) {
91		sys->print("%send ber_decode, byte %d\n", ber_ind, i);
92		if(val != nil) {
93			sys->print("%sdecode result:\n", ber_ind);
94			print_elem(elem);
95		}
96		if(err != OK)
97			sys->print("%serror: %s\n", ber_ind, errstr(err, i, i));
98		ber_ind = ber_ind_save;
99	}
100	return (err, i, elem);
101}
102
103# Decode a tag field.  As well as Tag, return an int that
104# is 1 if the type is constructed, 0 if not.
105tag_decode(a: array of byte, i, n: int) : (int, int, Tag)
106{
107	err := OK;
108	class, num, constr: int;
109	if(n-i >= 2) {
110		v := int a[i++];
111		class = v & CLASS_MASK;
112		if(v & CONSTR_MASK)
113			constr = 1;
114		else
115			constr = 0;
116		num = v & TAG_MASK;
117		if(num == TAG_MASK)
118			# long tag number
119			(err, i, num) = uint7_decode(a, i, n);
120	}
121	else
122		err = ESHORT;
123	return (err, i, Tag(class, num, constr));
124}
125
126# Decode a length field.  Assume it fits in a Limbo int.
127# If "indefinite length", return -1.
128length_decode(a: array of byte, i, n: int) : (int, int, int)
129{
130	err := OK;
131	num := 0;
132	if(i < n) {
133		v := int a[i++];
134		if(v & 16r80)
135			return int_decode(a, i, n, v&16r7F, 1);
136		else if(v == 16r80)
137			num = -1;
138		else
139			num = v;
140	}
141	else
142		err = ESHORT;
143	return (err, i, num);
144}
145
146# Decode a value according to the encoding of the Universal
147# type with number "kind" and constructed/primitive according
148# to "constr", with given length (may be -1, for "indefinite").
149value_decode(a: array of byte, i, n, length, kind, constr: int) : (int, int, ref Value)
150{
151	err := OK;
152	val : ref Value;
153	va : array of byte;
154	if(length == -1) {
155		if(!constr)
156			err = EINVAL;
157	}
158	else if(i+length > n)
159		err = EVALLEN;
160	if(err != OK)
161		return (err, i, nil);
162	case kind {
163	0 =>
164		# marker for end of indefinite constructions
165		if(length == 0)
166			val = ref Value.EOC;
167		else
168			err = EINVAL;
169	BOOLEAN =>
170		if(constr)
171			err = ECONSTR;
172		else if(length != 1)
173			err = EVALLEN;
174		else {
175			val = ref Value.Bool(int a[0]);
176			i++;
177		}
178	INTEGER or ENUMERATED =>
179		if(constr)
180			err = ECONSTR;
181		else if(length <= 4) {
182			num : int;
183			(err, i, num) = int_decode(a, i, i+length, length, 0);
184			if(err == OK)
185				val = ref Value.Int(num);
186		}
187		else {
188			va = array[length] of byte;
189			va[0:] = a[i:i+length];
190			val = ref Value.BigInt(va);
191			i += length;
192		}
193	BIT_STRING =>
194		if(constr) {
195			if(length == -1 && i+2 <= n && a[i] == byte 0 && a[i+1] == byte 0) {
196				val = ref Value.BitString(0, nil);
197				i += 2;
198			}
199			else
200				# TODO: recurse and concat results
201				err = EUNIMPL;
202		}
203		else {
204			if(length < 2) {
205				if(length == 1 && a[0] == byte 0) {
206					val = ref Value.BitString(0, nil);
207					i ++;
208				}
209				else
210					err = EINVAL;
211			}
212			else {
213				bitsunused := int a[i];
214				if(bitsunused > 7)
215					err = EINVAL;
216				else if(length > 16r0FFFFFFF)
217					err = ETOOBIG;
218				else {
219					va = array[length-1] of byte;
220					va[0:] = a[i+1:i+length];
221					val = ref Value.BitString(bitsunused, va);
222					i += length;
223				}
224			}
225		}
226	OCTET_STRING or ObjectDescriptor =>
227		(err, i, va) = octet_decode(a, i, n, length, constr);
228		if(err == OK)
229			val = ref Value.Octets(va);
230	NULL =>
231		if(constr)
232			err = ECONSTR;
233		else if(length != 0)
234			err = EVALLEN;
235		else
236			val = ref Value.Null;
237	OBJECT_ID =>
238		if(constr)
239			err = ECONSTR;
240		else if (length == 0)
241			err = EVALLEN;
242		else {
243			subids : list of int = nil;
244			iend := i+length;
245			while(i < iend) {
246				x : int;
247				(err, i, x) = uint7_decode(a, i, n);
248				if(err != OK)
249					break;
250				subids = x :: subids;
251			}
252			if(err == OK) {
253				if(i != iend)
254					err = EVALLEN;
255				else {
256					m := len subids;
257					ia := array[m+1] of int;
258					while(subids != nil) {
259						y := hd subids;
260						subids = tl subids;
261						if(m == 1) {
262							ia[1] = y % 40;
263							ia[0] = y / 40;
264						}
265						else
266							ia[m--] = y;
267					}
268					val = ref Value.ObjId(ref Oid(ia));
269				}
270			}
271		}
272	EXTERNAL or EMBEDDED_PDV =>
273		# TODO: parse this internally
274		va = array[length] of byte;
275		va[0:] = a[i:i+length];
276		val = ref Value.Other(va);
277		i += length;
278	REAL =>
279		# let the appl decode, with math module
280		if(constr)
281			err = ECONSTR;
282		else {
283			va = array[length] of byte;
284			va[0:] = a[i:i+length];
285			val = ref Value.Real(va);
286			i += length;
287		}
288	SEQUENCE or SET=>
289		vl : list of ref Elem;
290		(err, i, vl) = seq_decode(a, i, n, length, constr);
291		if(err == OK) {
292			if(kind == SEQUENCE)
293				val = ref Value.Seq(vl);
294			else
295				val = ref Value.Set(vl);
296		}
297	NumericString or PrintableString or TeletexString
298	or VideotexString or IA5String or UTCTime
299	or GeneralizedTime or GraphicString or VisibleString
300	or GeneralString or UniversalString or BMPString =>
301		(err, i, va) = octet_decode(a, i, n, length, constr);
302		if(err == OK)
303			# sometimes wrong: need to do char set conversion
304			val = ref Value.String(string va);
305
306	* =>
307		va = array[length] of byte;
308		va[0:] = a[i:i+length];
309		val = ref Value.Other(va);
310		i += length;
311	}
312	return (err, i, val);
313}
314
315# Decode an int in format where count bytes are
316# concatenated to form value.
317# Although ASN1 allows any size integer, we return
318# an error if the result doesn't fit in a Limbo int.
319# If unsigned is not set, make sure to propagate sign bit.
320int_decode(a: array of byte, i, n, count, unsigned: int) : (int, int, int)
321{
322	err := OK;
323	num := 0;
324	if(n-i >= count) {
325		if((count > 4) || (unsigned && count == 4 && (int a[i] & 16r80)))
326			err = ETOOBIG;
327		else {
328			if(!unsigned && count > 0 && count < 4 && (int a[i] & 16r80))
329				num = -1;		# all bits set
330			for(j := 0; j < count; j++) {
331				v := int a[i++];
332				num = (num << 8) | v;
333			}
334		}
335	}
336	else
337		err = ESHORT;
338	return (err, i, num);
339}
340
341# Decode an unsigned int in format where each
342# byte except last has high bit set, and remaining
343# seven bits of each byte are concatenated to form value.
344# Although ASN1 allows any size integer, we return
345# an error if the result doesn't fit in a Limbo int.
346uint7_decode(a: array of byte, i, n: int) : (int, int, int)
347{
348	err := OK;
349	num := 0;
350	more := 1;
351	while(more && i < n) {
352		v := int a[i++];
353		if(num & 16r7F000000) {
354			err = ETOOBIG;
355			break;
356		}
357		num <<= 7;
358		more = v & 16r80;
359		num |= (v & 16r7F);
360	}
361	if(n == i)
362		err = ESHORT;
363	return (err, i, num);
364}
365
366# Decode an octet string, recursively if constr.
367# We've already checked that length==-1 implies constr==1,
368# and otherwise that specified length fits within a[i..n].
369octet_decode(a: array of byte, i, n, length, constr: int) : (int, int, array of byte)
370{
371	err := OK;
372	va : array of byte;
373	if(length >= 0 && !constr) {
374		va = array[length] of byte;
375		va[0:] = a[i:i+length];
376		i += length;
377	}
378	else {
379		# constructed, either definite or indefinite length
380		lva : list of array of byte = nil;
381		elem : ref Elem;
382		istart := i;
383		totbytes := 0;
384	    cloop:
385		for(;;) {
386			if(length >= 0 && i >= istart+length) {
387				if(i != istart+length)
388					err = EVALLEN;
389				break cloop;
390			}
391			oldi := i;
392			(err, i, elem) = ber_decode(a, i, n);
393			if(err != OK)
394				break;
395			pick v := elem.val {
396				Octets =>
397					lva = v.bytes :: lva;
398					totbytes += len v.bytes;
399				EOC =>
400					if(length != -1) {
401						i = oldi;
402						err = EINVAL;
403					}
404					break cloop;
405				* =>
406					i = oldi;
407					err = EINVAL;
408					break cloop;
409			}
410		}
411		if(err == OK) {
412			va = array[totbytes] of byte;
413			j := totbytes;
414			while(lva != nil) {
415				x := hd lva;
416				lva = tl lva;
417				m := len x;
418				va[j-m:] = x[0:];
419				j -= m;
420			}
421		}
422	}
423	return (err, i, va);
424}
425
426# Decode a sequence or set.
427# We've already checked that length==-1 implies constr==1,
428# and otherwise that specified length fits within a[i..n].
429seq_decode(a : array of byte, i, n, length, constr: int) : (int, int, list of ref Elem)
430{
431	err := OK;
432	ans : list of ref Elem = nil;
433	if(!constr)
434		err = EPRIM;
435	else {
436		# constructed, either definite or indefinite length
437		lve : list of ref Elem = nil;
438		elem : ref Elem;
439		istart := i;
440	    cloop:
441		for(;;) {
442			if(length >= 0 && i >= istart+length) {
443				if(i != istart+length)
444					err = EVALLEN;
445				break cloop;
446			}
447			oldi := i;
448			(err, i, elem) = ber_decode(a, i, n);
449			if(err != OK)
450				break;
451			pick v := elem.val {
452				EOC =>
453					if(length != -1) {
454						i = oldi;
455						err = EINVAL;
456					}
457					break cloop;
458				* =>
459					lve = elem :: lve;
460			}
461		}
462		if(err == OK) {
463			# reverse back to original order
464			while(lve != nil) {
465				e := hd lve;
466				lve = tl lve;
467				ans = e :: ans;
468			}
469		}
470	}
471	return (err, i, ans);
472}
473
474# Encode e by BER rules
475encode(e: ref Elem) : (string, array of byte)
476{
477	(err, n) := enc(nil, e, 0, 1);
478	if(err != "")
479		return (err, nil);
480	b := array[n] of byte;
481	enc(b, e, 0, 0);
482	return ("", b);
483}
484
485# Encode e into array b, only putting in bytes if !lenonly.
486# Start at loc i, return index after.
487enc(b: array of byte, e: ref Elem, i, lenonly: int) : (string, int)
488{
489	(err, vlen, constr) := val_enc(b, e, 0, 1);
490	if(err != "")
491		return (err, i);
492	tag := e.tag;
493	v := tag.class | constr;
494	if(tag.num < 31) {
495		if(!lenonly)
496			b[i] = byte (v | tag.num);
497		i++;
498	}
499	else {
500		if(!lenonly)
501			b[i] = byte (v | 31);
502		if(tag.num < 0)
503			return ("negative tag number", i);
504		i = uint7_enc(b, tag.num, i+1, lenonly);
505	}
506	if(vlen < 16r80) {
507		if(!lenonly)
508			b[i] = byte vlen;
509		i++;
510	}
511	else {
512		ilen := int_enc(b, vlen, 1, 0, 1);
513		if(!lenonly) {
514			b[i] = byte (16r80 | ilen);
515			i = int_enc(b, vlen, 1, i+1, 0);
516		}
517		else
518			i += 1+ilen;
519	}
520	if(!lenonly)
521		val_enc(b, e, i, 0);
522	i += vlen;
523	return ("", i);
524}
525
526# Encode e.val into array b, only putting in bytes if !lenonly.
527# Start at loc i, return (err, index after, constructed or primitive)
528val_enc(b: array of byte, e: ref Elem, i, lenonly: int) : (string, int, int)
529{
530	kind := e.tag.num;
531	cl := e.tag.class;
532	ok := 1;
533	v : int;
534	bb : array of byte;
535	constr := 0;
536	if(cl != Universal) {
537		pick vv := e.val {
538		Bool =>
539			kind = BOOLEAN;
540		Int =>
541			kind = INTEGER;
542		BigInt =>
543			kind = INTEGER;
544		Octets =>
545			kind = OCTET_STRING;
546		Real =>
547			kind = REAL;
548		Other =>
549			kind = OCTET_STRING;
550		BitString =>
551			kind = BIT_STRING;
552		Null =>
553			kind = NULL;
554		ObjId =>
555			kind = OBJECT_ID;
556		String =>
557			kind = UniversalString;
558		Seq =>
559			kind = SEQUENCE;
560		Set =>
561			kind = SET;
562		}
563	}
564	case kind {
565	BOOLEAN =>
566		(ok, v) = e.is_int();
567		if(ok) {
568			if(v != 0)
569				v = 255;
570			i = int_enc(b, v, 1, i, lenonly);
571		}
572	INTEGER or ENUMERATED =>
573		(ok, v) = e.is_int();
574		if(ok)
575			i = int_enc(b, v, 0, i, lenonly);
576		else {
577			(ok, bb) = e.is_bigint();
578			if(ok) {
579				if(!lenonly)
580					b[i:] = bb;
581				i += len bb;
582			}
583		}
584	BIT_STRING =>
585		(ok, v, bb) = e.is_bitstring();
586		if(ok) {
587			if(bb == nil) {
588				if(!lenonly)
589					b[i] = byte 0;
590				i++;
591			}
592			else {
593				if(v < 0 || v > 7)
594					ok = 0;
595				else {
596					if(!lenonly) {
597						b[i] = byte v;
598						b[i+1:] = bb;
599					}
600					i += 1 + len bb;
601				}
602			}
603		}
604	OCTET_STRING or ObjectDescriptor or EXTERNAL or REAL
605	or EMBEDDED_PDV =>
606		pick vv := e.val {
607		Octets or Real or Other =>
608			if(!lenonly && vv.bytes != nil)
609					b[i:] = vv.bytes;
610			i += len vv.bytes;
611		 * =>
612			ok = 0;
613		}
614	NULL =>
615		;
616	OBJECT_ID =>
617		oid : ref Oid;
618		(ok, oid) = e.is_oid();
619		if(ok) {
620			n := len oid.nums;
621			for(k := 0; k < n; k++) {
622				v = oid.nums[k];
623				if(k == 0) {
624					v *= 40;
625					if(n > 1)
626						v += oid.nums[++k];
627				}
628				i = uint7_enc(b, v, i, lenonly);
629			}
630		}
631	SEQUENCE or SET =>
632		pick vv := e.val {
633		Seq or Set =>
634			constr = CONSTR_MASK;
635			for(l := vv.l; l != nil; l = tl l) {
636				err : string;
637				(err, i) = enc(b, hd l, i, lenonly);
638				if(err != "")
639					return (err, i, 0);
640			}
641	}
642	NumericString or PrintableString or TeletexString
643	or VideotexString or IA5String or UTCTime
644	or GeneralizedTime or GraphicString or VisibleString
645	or GeneralString or UniversalString or BMPString =>
646		pick vv := e.val {
647			String =>
648				bb = array of byte vv.s;
649				if(!lenonly && bb != nil)
650					b[i:] = bb;
651				i += len bb;
652			* =>
653				ok = 0;
654		}
655	* =>
656		ok = 0;
657	}
658	if(!ok)
659		return ("bad value for encoding kind", i, constr);
660	return ("", i, constr);
661}
662
663# Encode num as unsigned 7 bit values with top bit 1 on all bytes
664# except last, into array b, only putting in bytes if !lenonly.
665# Start at loc i, return index after.
666uint7_enc(b: array of byte, num, i, lenonly: int) : int
667{
668	n := 1;
669	v := num>>7;
670	while(v > 0) {
671		v >>= 7;
672		n++;
673	}
674	if(lenonly)
675		i += n;
676	else {
677		for(k := (n-1)*7; k > 0; k -= 7)
678			b[i++] = byte ((num>>k) | 16r80);
679		b[i++] = byte (num & 16r7F);
680	}
681	return i;
682}
683
684# Encode num as unsigned or signed integer into array b,
685# only putting in bytes if !lenonly.
686# Encoding is length followed by bytes to concatenate.
687# Start at loc i, return index after.
688int_enc(b: array of byte, num, unsigned, i, lenonly: int) : int
689{
690	v := num;
691	if(v < 0)
692		v = -(v+1);
693	n := 1;
694	prevv := v;
695	v >>= 8;
696	while(v > 0) {
697		prevv = v;
698		v >>= 8;
699		n++;
700	}
701	if(!unsigned && (prevv & 16r80))
702		n++;
703	if(lenonly)
704		i += n;
705	else {
706		for(k := (n-1)*8; k >= 0; k -= 8)
707			b[i++] = byte (num>>k);
708	}
709	return i;
710}
711
712# Compare two arrays of integers; return true if they match
713intarr_eq(a: array of int, b: array of int) : int
714{
715	alen := len a;
716	if(alen != len b)
717		return 0;
718	for(i := 0; i < alen; i++)
719		if(a[i] != b[i])
720			return 0;
721	return 1;
722}
723
724# Look for o in tab; if found, return index, else return -1.
725oid_lookup(o: ref Oid, tab: array of Oid) : int
726{
727	for(i := 0; i < len tab; i++)
728		if(intarr_eq(o.nums, tab[i].nums))
729			return i;
730	return -1;
731}
732
733# If e is a SEQUENCE, return (1, e's element list)
734# else return (error, nil).
735Elem.is_seq(e: self ref Elem) : (int, list of ref Elem)
736{
737	if(e.tag.class == Universal && e.tag.num == SEQUENCE) {
738		pick v := e.val {
739		Seq =>
740			return (1, v.l);
741		}
742	}
743	return (0, nil);
744}
745
746# If e is a SET, return (1, e's element list)
747# else return (error, nil).
748Elem.is_set(e: self ref Elem) : (int, list of ref Elem)
749{
750	if(e.tag.class == Universal && e.tag.num == SET) {
751		pick v := e.val {
752		Set =>
753			return (1, v.l);
754		}
755	}
756	return (0, nil);
757}
758
759# If e is an INTEGER that fits in a limbo int, return (1, val)
760# else return (0, 0l).
761Elem.is_int(e: self ref Elem) : (int, int)
762{
763	if(e.tag.class == Universal && (e.tag.num == INTEGER || e.tag.num == BOOLEAN)) {
764		pick v := e.val {
765		Bool or
766		Int =>
767			return (1, v.v);
768		}
769	}
770	return (0, 0);
771}
772
773# If e is an INTEGER that doesn't fit in a limbo int, return (1, bytes),
774# or even if it does fit, return it as an array of bytes.
775# else return (0, nil).
776Elem.is_bigint(e: self ref Elem) : (int, array of byte)
777{
778	if(e.tag.class == Universal && e.tag.num == INTEGER) {
779		pick v := e.val {
780		BigInt =>
781			return (1, v.bytes);
782		Int =>
783			x := v.v;
784			a := array[4] of byte;
785			for(i := 0; i < 4; i++)
786				a[i] = byte ((x >> (8*(3-i))) & 16rFF);
787			for(j := 0; j < 3; j++)
788				if(a[j] != byte 0)
789					break;
790			return (1, a[j:]);
791		}
792	}
793	return (0, nil);
794}
795
796# If e is a bitstring, return (1, unused bits, bytes containing bit string),
797# else return (0, nil)
798Elem.is_bitstring(e: self ref Elem) : (int, int, array of byte)
799{
800	if(e.tag.class == Universal && e.tag.num == BIT_STRING) {
801		pick v := e.val {
802		BitString =>
803			return (1, v.unusedbits, v.bits);
804		}
805	}
806	return (0, 0, nil);
807}
808
809# If e is an octetstring, return (1, bytes),
810# else return (0, nil)
811Elem.is_octetstring(e: self ref Elem) : (int, array of byte)
812{
813	if(e.tag.class == Universal && e.tag.num == OCTET_STRING) {
814		pick v := e.val {
815		Octets =>
816			return (1, v.bytes);
817		}
818	}
819	return (0, nil);
820}
821
822# If e is an object id, return (1, ref Oid),
823# else return (0, nil)
824Elem.is_oid(e: self  ref Elem) : (int, ref Oid)
825{
826	if(e.tag.class == Universal && e.tag.num == OBJECT_ID) {
827		pick v := e.val {
828		ObjId =>
829			return (1, v.id);
830		}
831	}
832	return (0, nil);
833}
834
835# If e is some kind of string (excluding times), return (1, string),
836# else return (0, "")
837Elem.is_string(e: self ref Elem) : (int, string)
838{
839	if(e.tag.class == Universal) {
840		case e.tag.num {
841		NumericString or PrintableString or TeletexString
842		or VideotexString or IA5String or GraphicString
843		or VisibleString or GeneralString or UniversalString
844		or BMPString =>
845		pick v := e.val {
846			String =>
847				return (1, v.s);
848			}
849		}
850	}
851	return (0, nil);
852}
853
854# If e is some kind of time, return (1, string),
855# else return (0, "")
856Elem.is_time(e: self ref Elem) : (int, string)
857{
858	if(e.tag.class == Universal
859	   && (e.tag.num == UTCTime || e.tag.num == GeneralizedTime)) {
860		pick v := e.val {
861		String =>
862			return (1, v.s);
863		}
864	}
865	return (0, nil);
866}
867
868# Return printable error string for code ecode.
869# i is position where error is first noted.
870# n is the end of the passed data: if i!=n then
871# we didn't use all the data and an error should
872# be returned about that.
873errstr(ecode, i, n: int) : string
874{
875	if(ecode == OK && i == n)
876		return "";
877	err := "BER decode: ";
878	case ecode {
879		OK =>
880			err += "OK";
881		ESHORT =>
882			err += "need more data";
883		ETOOBIG =>
884			err += "value exceeds implementation limit";
885		EVALLEN =>
886			err += "value has wrong length";
887		ECONSTR =>
888			err += "value is constructed, should be primitive";
889		EPRIM =>
890			err += "value is primitive";
891		EINVAL =>
892			err += "value encoding invalid";
893		* =>
894			err += "unknown error " + string ecode;
895	}
896	if(err == "" && i != n)
897		err += "extra data";
898	err += " at byte " + string i;
899	return err;
900}
901
902# Printing functions, for debugging
903
904Tag.tostring(t: self Tag) : string
905{
906	ans := "";
907	snum := string t.num;
908	if(t.class == Universal) {
909		case t.num {
910		BOOLEAN => ans = "BOOLEAN";
911		INTEGER => ans = "INTEGER";
912		BIT_STRING => ans = "BIT STRING";
913		OCTET_STRING => ans = "OCTET STRING";
914		NULL => ans = "NULL";
915		OBJECT_ID => ans = "OBJECT IDENTIFER";
916		ObjectDescriptor => ans = "OBJECT_DES";
917		EXTERNAL => ans = "EXTERNAL";
918		REAL => ans = "REAL";
919		ENUMERATED => ans = "ENUMERATED";
920		EMBEDDED_PDV => ans = "EMBEDDED PDV";
921		SEQUENCE => ans = "SEQUENCE";
922		SET => ans = "SET";
923		NumericString => ans = "NumericString";
924		PrintableString => ans = "PrintableString";
925		TeletexString => ans = "TeletexString";
926		VideotexString => ans = "VideotexString";
927		IA5String => ans = "IA5String";
928		UTCTime => ans = "UTCTime";
929		GeneralizedTime => ans = "GeneralizedTime";
930		GraphicString => ans = "GraphicString";
931		VisibleString => ans = "VisibleString";
932		GeneralString => ans = "GeneralString";
933		UniversalString => ans = "UniversalString";
934		BMPString => ans = "BMPString";
935		* => ans = "UNIVERSAL " + snum;
936		}
937	}
938	else {
939		case t.class {
940		Application =>
941			ans = "APPLICATION " + snum;
942		Context =>
943			ans = "CONTEXT "+ snum;
944		Private =>
945			ans = "PRIVATE " + snum;
946		}
947	}
948	return ans;
949}
950
951Elem.tostring(e: self ref Elem) : string
952{
953	return estring(e, "");
954}
955
956Value.tostring(v: self ref Value) : string
957{
958	return vstring(v, "");
959}
960
961estring(e: ref Elem, indent: string) : string
962{
963	return indent + e.tag.tostring() + " " + vstring(e.val, indent);
964}
965
966vstring(val: ref Value, indent: string) : string
967{
968	ans := "";
969	pick v := val {
970		Bool or Int =>
971			ans += string v.v;
972		Octets or BigInt or Real or Other =>
973			ans += bastring(v.bytes, indent + "\t");
974		BitString =>
975			ans += " bits (unused " +string v.unusedbits + ")" +  bastring(v.bits, indent + "\t");
976		Null  or EOC =>
977			;
978		ObjId =>
979			ans += v.id.tostring();
980		String =>
981			ans += "\"" + v.s + "\"";
982		Seq or Set =>
983			ans += "{\n";
984			newindent := indent + "\t";
985			l := v.l;
986			while(l != nil) {
987				if(ans[len ans-1] != '\n')
988					ans[len ans] = '\n';
989				ans += estring(hd l, newindent);
990				l = tl l;
991			}
992			if(ans[len ans-1] != '\n')
993				ans[len ans] = '\n';
994			ans += indent + "}";
995	}
996	return ans;
997}
998
999bastring(a: array of byte, indent: string) : string
1000{
1001	if(sys == nil)
1002		sys = load Sys Sys->PATH;
1003	ans := indent;
1004	nlindent := "\n" + indent;
1005	for(i := 0; i < len a; i++) {
1006		if(i < len a - 1 && i%10 == 0)
1007			ans += nlindent ;
1008		ans += sys->sprint("%2x ", int a[i]);
1009	}
1010	return ans;
1011}
1012
1013Oid.tostring(o: self ref Oid) : string
1014{
1015	ans := "";
1016	for(i := 0; i < len o.nums; i++) {
1017		ans += string o.nums[i];
1018		if(i < len o.nums - 1)
1019			ans[len ans] = '.';
1020	}
1021	return ans;
1022}
1023
1024print_elem(e: ref Elem)
1025{
1026	s := e.tostring();
1027	a := array of byte s;
1028	sys->write(sys->fildes(1), a, len a);
1029	sys->print("\n");
1030}
1031