123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- MODULE ASN1; (** AUTHOR "Patrick Hunziker"; PURPOSE "ISO 8824/8825 standard tag/length/data encoding"; *)
- (* ASN1(Abstract Syntax Notation One) is a standard that defines a formalism for the specification of abstract data types.
- PROCEDURE Decode(ASN1Stream):Triplet produces a hierarchical tree with triple.next and triplet.child dependencies.*)
- (*to do: correctly decode big integer numbers which do not fit into standard oberon types*)
- (*to do for more general ASN1 implementation: bit strings of undefined length: see ftp://ftp.rsa.com/pub/pkcs/ascii/layman.asc; not needed for certifcates*)
- (*refine standard OIDs as hex constants, for direct comparison*)
- (*to do: encoding *)
- IMPORT Streams;
- CONST
- Trace=FALSE;
- (*ASN1 class*)
- ASN1Universal*=0;
- ASN1Application*=1;
- ASN1Context*=2;
- ASN1Private*=3;
- (*
- BER_TAG_UNKNOWN 0
- BER_TAG_BOOLEAN 1
- BER_TAG_INTEGER 2
- BER_TAG_BIT_STRING 3
- BER_TAG_OCTET_STRING 4
- BER_TAG_NULL 5
- BER_TAG_OID 6
- BER_TAG_OBJECT_DESCRIPTOR 7
- BER_TAG_EXTERNAL 8
- BER_TAG_REAL 9
- BER_TAG_ENUMERATED 10
- 12 to 15 are reserved for future versions of the recommendation
- BER_TAG_PKIX_UTF8_STRING 12
- BER_TAG_SEQUENCE 16
- BER_TAG_SET 17
- BER_TAG_NUMERIC_STRING 18
- BER_TAG_PRINTABLE_STRING 19
- BER_TAG_T61_STRING 20
- BER_TAG_TELETEX_STRING BER_TAG_T61_STRING
- BER_TAG_VIDEOTEX_STRING 21
- BER_TAG_IA5_STRING 22
- BER_TAG_UTC_TIME 23
- BER_TAG_GENERALIZED_TIME 24
- BER_TAG_GRAPHIC_STRING 25
- BER_TAG_ISO646_STRING 26
- BER_TAG_GENERAL_STRING 27
- BER_TAG_VISIBLE_STRING BER_TAG_ISO646_STRING
- 28 - are reserved for future versions of the recommendation
- BER_TAG_PKIX_UNIVERSAL_STRING 28
- BER_TAG_PKIX_BMP_STRING 30
- *)
- Boolean* =1H;
- Integer* =2H;
- BitString* =3H;
- (*
- BIT STRING: BER encoding. Primitive or constructed.
- In a primitive encoding, the first contents octet gives the number of bits by which the length of the bit string
- is less than the next multiple of eight (this is called the "number of unused bits").
- The second and following contents octets give the value of the bit string, converted to an octet string.
- The conversion process is as follows:
- 1. The bit string is padded after the last bit with zero to seven bits of any value to make the length
- of the bit string a multiple of eight. If the length of the bit string is a multiple of eight
- already, no padding is done.
- 2. The padded bit string is divided into octets. The first eight bits of the padded bit string become
- the first octet, bit 8 to bit 1, and so on through the last eight bits of the padded bit string.
- In a constructed encoding, the contents octets give the concatenation of the BER encodings of consecutive substrings
- of the bit string, where each substring except the last has a length that is a multiple of eight bits.
- Example: The BER encoding of the BIT STRING value "011011100101110111" can be any of the following, among
- others, depending on the choice of padding bits, the form of length octets, and whether the encoding is primitive or constructed:
- 03 04 06 6e 5d c0 DER encoding
- 03 04 06 6e 5d e0 padded with "100000"
- 03 81 04 06 6e 5d c0 long form of length octets
- 23 09 constructed encoding: "0110111001011101" + "11"
- 03 03 00 6e 5d
- 03 02 06 c0
- DER encoding. Primitive.
- The contents octects are as for a primitive BER encoding, except that the bit string is padded with zero-valued bits.
- Example: The DER encoding of the BIT STRING value "011011100101110111" is 03 04 06 6e 5d c0
- *)
- String* =4H;
- Null* =5H;
- Oid* =6H;
- UTF8* =0CH;
- PrintableString* =13H;
- TeletexString* =14H;
- IA5String*=16H;
- (*
- UTCTime
- The UTCTime type denotes a coordinated universal time or Greenwich Mean Time (GMT) value. A UTCTime value includes
- the local time precise to either minutes or seconds, and an offset from GMT in hours and minutes. It takes any of the
- following forms:
- YYMMDDhhmmZ
- YYMMDDhhmm+hh'mm'
- YYMMDDhhmm-hh'mm'
- YYMMDDhhmmssZ
- YYMMDDhhmmss+hh'mm'
- YYMMDDhhmmss-hh'mm'
- *)
- UTCTime* =17H;
- BMPString* =1EH;
- Sequence* =30H;
- Set* =31H;
- TYPE CharString*=POINTER TO ARRAY OF CHAR;
- (* data element in DER encoded certificates: tag[class+type] - length[1..n bytes] - value[0..n bytes] *)
- TYPE Triplet* = OBJECT (* todo: 1 Byte tags are sufficient for certificates, but in general, multiple byte tags may occur in other contexts *)
- VAR tag*:LONGINT;
- class*:LONGINT;
- length*:LONGINT;
- unusedbits*:LONGINT;
- constructed*:BOOLEAN;
- definite*: BOOLEAN;
- bvalue*:BOOLEAN;
- ivalue*:LONGINT;
- svalue*:POINTER TO ARRAY OF CHAR;
- child*, curchild:Triplet;
- next*:Triplet;
- level*:LONGINT; (*pretty print*)
- PROCEDURE AppendChild*(t:Triplet);
- BEGIN
- IF child=NIL THEN child:=t; curchild:=t ELSE curchild.next:=t; curchild:=t; END;
- END AppendChild;
- PROCEDURE Print*(w: Streams.Writer);
- VAR i:LONGINT;
- BEGIN
- w.Char(09X);
- FOR i:=0 TO level-1 DO w.String("- "); END;
- w.Hex(tag,4); w.String("H[");
- w.Int(length,0); w.String("]");
- CASE tag OF
- | Boolean: IF bvalue THEN w.String("true") ELSE w.String("false") END;
- | Integer: IF length<=4 THEN w.Int(ivalue,0);ELSE PrintHexString(w,svalue^) END;
- | Oid: PrintNumericString(w, svalue^); w.String(" "); PrintHexString(w, svalue^);
- | PrintableString: PrintString(w,svalue^);
- | UTCTime: PrintString(w,svalue^);
- | BitString: w.Char("("); w.Int(unusedbits,0); w.Char(")"); IF svalue#NIL THEN PrintHexString(w, svalue^) END;
- | Sequence, Set:
- ELSE
- IF svalue#NIL THEN PrintHexString(w,svalue^);END;
- END;
- w.Ln;
- END Print;
- END Triplet;
- VAR log*: Streams.Writer;
- PROCEDURE PrintHexString*(w:Streams.Writer; CONST s: ARRAY OF CHAR);
- CONST hex="0123456789ABCDEF";
- VAR i:LONGINT; c:CHAR;
- BEGIN
- FOR i:=0 TO LEN(s)-1 DO
- c:=s[i];
- w.Char(hex[ORD(c) DIV 16]);
- w.Char(hex[ORD(c) MOD 16]);
- w.Char(" ");
- END;
- END PrintHexString;
- PROCEDURE PrintNumericString*(w:Streams.Writer; CONST s: ARRAY OF CHAR);
- VAR i:LONGINT; c:CHAR;
- BEGIN
- w.String(" ");
- FOR i:=0 TO LEN(s)-1 DO
- c:=s[i];
- w.Int(ORD(c),0);
- w.Char(".");
- END;
- END PrintNumericString;
- PROCEDURE PrintString*(w:Streams.Writer; CONST s: ARRAY OF CHAR);
- VAR i:LONGINT; c:CHAR;
- BEGIN
- FOR i:=0 TO LEN(s)-1 DO
- c:=s[i];
- IF (ORD(c)>=20H) & (ORD(c)<=7EH) THEN w.Char(c) ELSE w.Char("."); END;
- END;
- END PrintString;
- PROCEDURE Decode*(reader:Streams.Reader; level:LONGINT; VAR len:LONGINT): Triplet;
- VAR t,t1:Triplet; lengthbytes,len0,hdrlen:LONGINT; i:LONGINT; c:CHAR;
- BEGIN
- NEW(t); t.level:=level;
- len:=0; hdrlen:=0;
- reader.Char(c); INC(len); INC(hdrlen);
- IF Trace & (log#NIL) THEN log.Char("{"); log.Hex(ORD(c),0);log.Char("H"); log.Char("}"); END;
- t.class:= ORD(c) DIV 64;
- t.constructed:= ODD (ORD(c) DIV 32);
- IF ORD(c) MOD 32#31 THEN t.tag:=ORD(c) MOD 64; (* 'constructed' bit included *)
- ELSE
- REPEAT
- reader.Char(c); INC(len); INC(hdrlen);
- t.tag:=t.tag*128+ ORD(c) MOD 128;
- UNTIL ORD(c) DIV 128 # 1;
- END;
- reader.Char(c); INC(len); INC(hdrlen);
- IF ORD(c)<128 THEN t.length:=ORD(c); t.definite:=TRUE;
- ELSIF ORD(c)=128 THEN t.definite:=FALSE;
- (* to do: class=0, length=0 is used to encode end of bit stream of undefined length, see ftp://ftp.rsa.com/pub/pkcs/ascii/layman.asc*)
- ELSE
- lengthbytes:= ORD(c) MOD 128; t.definite:=TRUE;
- FOR i:=0 TO lengthbytes-1 DO (* read all length bytes here ...*)
- t.length:=256*t.length;
- reader.Char(c); INC(len); INC(hdrlen);
- t.length:=t.length+ORD(c);
- END;
- END;
- CASE t.tag OF
- | Boolean: reader.Char(c);INC(len); t.bvalue:=c#0X;
- | Integer:
- IF t.length<=4 THEN
- NEW(t.svalue,t.length);
- FOR i:=0 TO t.length-1 DO
- t.ivalue:=256*t.ivalue;
- reader.Char(c); INC(len);
- t.svalue[i]:=c;
- t.ivalue:=t.ivalue+ORD(c);
- END;
- ELSE
- NEW(t.svalue,t.length);
- reader.Bytes(t.svalue^,0,t.length,len0); len:=len+len0;
- END;
- | BitString:
- reader.Char(c); t.unusedbits:=ORD(c); INC(len);
- IF FALSE & (ORD(reader.Peek())=30H) (*rsa bit string 1024b*) THEN (*unreliable hack. to do: pass info about recursive bitstring to child*)
- IF Trace & (log#NIL) THEN log.String("(BitSRec "); log.Int(len,0); log.String(")");t.Print(log); END;
- t.child:=Decode(reader,level+1, len0); len:=len+len0;
- ELSE
- NEW(t.svalue,t.length-1);
- IF t.length>0 THEN reader.Bytes(t.svalue^,0,t.length-1,len0); len:=len+len0; END;
- END;
- | String:
- NEW(t.svalue,t.length);
- reader.Bytes(t.svalue^,0,t.length,len0);len:=len+len0;
- | Set:
- IF Trace & (log#NIL) THEN log.String("(Set "); log.Int(len,0); log.String("+)"); t.Print(log); END; (*bytes not including body*)
- t.child:=Decode(reader,level+1, len0);len:=len+len0;
- | Sequence:
- IF Trace & (log#NIL) THEN log.String("(Seq "); log.Int(len,0); log.String("+)"); t.Print(log); END; (*bytes not including body*)
- WHILE len-hdrlen<t.length DO
- t1:=Decode(reader,level+1, len0); len:=len+len0;
- (*t1.next:=t.next; t.next:=t1;*)
- t.AppendChild(t1);
- END;
- ELSE
- NEW(t.svalue,t.length);
- IF t.length>0 THEN reader.Bytes(t.svalue^,0,t.length,len0); len:=len+len0; END;
- END;
- IF Trace & (log#NIL) THEN
- CASE t.tag OF
- | Integer: log.String("(Integer "); log.Int(len,4); log.Char(")"); t.Print(log);(*bytes including header*)
- | BitString:
- IF t.constructed THEN log.String("(constrBitString "); ELSE log.String("(BitString "); END;
- log.Int(len,4); log.Char(")"); t.Print(log); (*bytes including header*)
- | String: log.String("(String ");log.Int(len,4); log.Char(")"); t.Print(log);(*bytes including header*)
- | Null: log.String("(Null ");log.Int(len,4); log.Char(")"); t.Print(log);(*bytes including header*)
- | Oid: log.String("(OID");log.Int(len,4); log.Char(")"); t.Print(log);(*bytes including header*)
- | PrintableString: log.String("(Printable");log.Int(len,4); log.Char(")"); t.Print(log);(*bytes including header*)
- | UTCTime: log.String("(UTCTime");log.Int(len,4); log.Char(")"); t.Print(log);(*bytes including header*)
- | Set:
- | Sequence:
- ELSE log.String("(t.tag="); log.Hex(t.tag,4);log.Int(len,4); log.Char(")"); t.Print(log); (*bytes including header*)
- END;
- END;
- RETURN t
- END Decode;
- END ASN1.
|