implement ASN1; include "sys.m"; sys: Sys; include "asn1.m"; # MasksTAG_MASK : con 16r1F;CONSTR_MASK : con 16r20;CLASS_MASK : con 16rC0; # Decoding errorsOK, ESHORT, ETOOBIG, EVALLEN, ECONSTR, EPRIM, EINVAL, EUNIMPL: con iota; debug : con 0; init(){ sys = load Sys Sys->PATH;} # Decode the whole array as a BER encoding of an ASN1 type.# If there's an error, the return string will contain the error.# Depending on the error, the returned elem may or may not# be nil.decode(a: array of byte) : (string, ref Elem){ (ecode, i, elem) := ber_decode(a, 0, len a); return (errstr(ecode, i, len a), elem);} # Like decode, but continue decoding after first element# of array ends.decode_seq(a: array of byte) : (string, list of ref Elem){ (ecode, i, elist) := seq_decode(a, 0, len a, -1, 1); return (errstr(ecode, i, len a), elist);} # 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.decode_value(a: array of byte, kind, constr: int) : (string, ref Value){ n := len a; (ecode, i, val) := value_decode(a, 0, n, n, kind, constr); return (errstr(ecode, i, len a), val);} # The rest of the decoding routines take the array (a), the# starting position (i), and the ending position +1 (n).# They return (err code, new i, [... varies]). # for debuggingber_ind := "";ber_ind_save := ""; # Decode an ASN1 (tag, length, value).ber_decode(a: array of byte, i, n: int) : (int, int, ref Elem){ if(debug) { ber_ind_save = ber_ind; ber_ind = ber_ind + " "; sys->print("%sber_decode, byte %d\n", ber_ind, i); } err, length: int; tag : Tag; val : ref Value; elem : ref Elem = nil; (err, i, tag) = tag_decode(a, i, n); if(err == OK) { (err, i, length) = length_decode(a, i, n); if(err == OK) { if(debug) sys->print("%sgot tag %s, length %d, now at byte %d\n", ber_ind, tag.tostring(), length, i); if(tag.class == Universal) (err, i, val) = value_decode(a, i, n, length, tag.num, tag.constr); else (err, i, val) = value_decode(a, i, n, length, OCTET_STRING, 0); if(val != nil) elem = ref Elem(tag, val); } } if(debug) { sys->print("%send ber_decode, byte %d\n", ber_ind, i); if(val != nil) { sys->print("%sdecode result:\n", ber_ind); print_elem(elem); } if(err != OK) sys->print("%serror: %s\n", ber_ind, errstr(err, i, i)); ber_ind = ber_ind_save; } return (err, i, elem);} # Decode a tag field. As well as Tag, return an int that# is 1 if the type is constructed, 0 if not.tag_decode(a: array of byte, i, n: int) : (int, int, Tag){ err := OK; class, num, constr: int; if(n-i >= 2) { v := int a[i++]; class = v & CLASS_MASK; if(v & CONSTR_MASK) constr = 1; else constr = 0; num = v & TAG_MASK; if(num == TAG_MASK) # long tag number (err, i, num) = uint7_decode(a, i, n); } else err = ESHORT; return (err, i, Tag(class, num, constr));} # Decode a length field. Assume it fits in a Limbo int.# If "indefinite length", return -1.length_decode(a: array of byte, i, n: int) : (int, int, int){ err := OK; num := 0; if(i < n) { v := int a[i++]; if(v & 16r80) return int_decode(a, i, n, v&16r7F, 1); else if(v == 16r80) num = -1; else num = v; } else err = ESHORT; return (err, i, num);} # Decode a value according to the encoding of the Universal# type with number "kind" and constructed/primitive according# to "constr", with given length (may be -1, for "indefinite").value_decode(a: array of byte, i, n, length, kind, constr: int) : (int, int, ref Value){ err := OK; val : ref Value; va : array of byte; if(length == -1) { if(!constr) err = EINVAL; } else if(i+length > n) err = EVALLEN; if(err != OK) return (err, i, nil); case kind { 0 => # marker for end of indefinite constructions if(length == 0) val = ref Value.EOC; else err = EINVAL; BOOLEAN => if(constr) err = ECONSTR; else if(length != 1) err = EVALLEN; else { val = ref Value.Bool(int a[0]); i++; } INTEGER or ENUMERATED => if(constr) err = ECONSTR; else if(length <= 4) { num : int; (err, i, num) = int_decode(a, i, i+length, length, 0); if(err == OK) val = ref Value.Int(num); } else { va = array[length] of byte; va[0:] = a[i:i+length]; val = ref Value.BigInt(va); i += length; } BIT_STRING => if(constr) { if(length == -1 && i+2 <= n && a[i] == byte 0 && a[i+1] == byte 0) { val = ref Value.BitString(0, nil); i += 2; } else # TODO: recurse and concat results err = EUNIMPL; } else { if(length < 2) { if(length == 1 && a[0] == byte 0) { val = ref Value.BitString(0, nil); i ++; } else err = EINVAL; } else { bitsunused := int a[i]; if(bitsunused > 7) err = EINVAL; else if(length > 16r0FFFFFFF) err = ETOOBIG; else { va = array[length-1] of byte; va[0:] = a[i+1:i+length]; val = ref Value.BitString(bitsunused, va); i += length; } } } OCTET_STRING or ObjectDescriptor => (err, i, va) = octet_decode(a, i, n, length, constr); if(err == OK) val = ref Value.Octets(va); NULL => if(constr) err = ECONSTR; else if(length != 0) err = EVALLEN; else val = ref Value.Null; OBJECT_ID => if(constr) err = ECONSTR; else if (length == 0) err = EVALLEN; else { subids : list of int = nil; iend := i+length; while(i < iend) { x : int; (err, i, x) = uint7_decode(a, i, n); if(err != OK) break; subids = x :: subids; } if(err == OK) { if(i != iend) err = EVALLEN; else { m := len subids; ia := array[m+1] of int; while(subids != nil) { y := hd subids; subids = tl subids; if(m == 1) { ia[1] = y % 40; ia[0] = y / 40; } else ia[m--] = y; } val = ref Value.ObjId(ref Oid(ia)); } } } EXTERNAL or EMBEDDED_PDV => # TODO: parse this internally va = array[length] of byte; va[0:] = a[i:i+length]; val = ref Value.Other(va); i += length; REAL => # let the appl decode, with math module if(constr) err = ECONSTR; else { va = array[length] of byte; va[0:] = a[i:i+length]; val = ref Value.Real(va); i += length; } SEQUENCE or SET=> vl : list of ref Elem; (err, i, vl) = seq_decode(a, i, n, length, constr); if(err == OK) { if(kind == SEQUENCE) val = ref Value.Seq(vl); else val = ref Value.Set(vl); } NumericString or PrintableString or TeletexString or VideotexString or IA5String or UTCTime or GeneralizedTime or GraphicString or VisibleString or GeneralString or UniversalString or BMPString => (err, i, va) = octet_decode(a, i, n, length, constr); if(err == OK) # sometimes wrong: need to do char set conversion val = ref Value.String(string va); * => va = array[length] of byte; va[0:] = a[i:i+length]; val = ref Value.Other(va); i += length; } return (err, i, val);} # 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 Limbo int.# If unsigned is not set, make sure to propagate sign bit.int_decode(a: array of byte, i, n, count, unsigned: int) : (int, int, int){ err := OK; num := 0; if(n-i >= count) { if((count > 4) || (unsigned && count == 4 && (int a[i] & 16r80))) err = ETOOBIG; else { if(!unsigned && count > 0 && count < 4 && (int a[i] & 16r80)) num = -1; # all bits set for(j := 0; j < count; j++) { v := int a[i++]; num = (num << 8) | v; } } } else err = ESHORT; return (err, i, num);} # 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 Limbo int.uint7_decode(a: array of byte, i, n: int) : (int, int, int){ err := OK; num := 0; more := 1; while(more && i < n) { v := int a[i++]; if(num & 16r7F000000) { err = ETOOBIG; break; } num <<= 7; more = v & 16r80; num |= (v & 16r7F); } if(n == i) err = ESHORT; return (err, i, num);} # Decode an octet string, recursively if constr.# We've already checked that length==-1 implies constr==1,# and otherwise that specified length fits within a[i..n].octet_decode(a: array of byte, i, n, length, constr: int) : (int, int, array of byte){ err := OK; va : array of byte; if(length >= 0 && !constr) { va = array[length] of byte; va[0:] = a[i:i+length]; i += length; } else { # constructed, either definite or indefinite length lva : list of array of byte = nil; elem : ref Elem; istart := i; totbytes := 0; cloop: for(;;) { if(length >= 0 && i >= istart+length) { if(i != istart+length) err = EVALLEN; break cloop; } oldi := i; (err, i, elem) = ber_decode(a, i, n); if(err != OK) break; pick v := elem.val { Octets => lva = v.bytes :: lva; totbytes += len v.bytes; EOC => if(length != -1) { i = oldi; err = EINVAL; } break cloop; * => i = oldi; err = EINVAL; break cloop; } } if(err == OK) { va = array[totbytes] of byte; j := totbytes; while(lva != nil) { x := hd lva; lva = tl lva; m := len x; va[j-m:] = x[0:]; j -= m; } } } return (err, i, va);} # Decode a sequence or set.# We've already checked that length==-1 implies constr==1,# and otherwise that specified length fits within a[i..n].seq_decode(a : array of byte, i, n, length, constr: int) : (int, int, list of ref Elem){ err := OK; ans : list of ref Elem = nil; if(!constr) err = EPRIM; else { # constructed, either definite or indefinite length lve : list of ref Elem = nil; elem : ref Elem; istart := i; cloop: for(;;) { if(length >= 0 && i >= istart+length) { if(i != istart+length) err = EVALLEN; break cloop; } oldi := i; (err, i, elem) = ber_decode(a, i, n); if(err != OK) break; pick v := elem.val { EOC => if(length != -1) { i = oldi; err = EINVAL; } break cloop; * => lve = elem :: lve; } } if(err == OK) { # reverse back to original order while(lve != nil) { e := hd lve; lve = tl lve; ans = e :: ans; } } } return (err, i, ans);} # Encode e by BER rulesencode(e: ref Elem) : (string, array of byte){ (err, n) := enc(nil, e, 0, 1); if(err != "") return (err, nil); b := array[n] of byte; enc(b, e, 0, 0); return ("", b);} # Encode e into array b, only putting in bytes if !lenonly.# Start at loc i, return index after.enc(b: array of byte, e: ref Elem, i, lenonly: int) : (string, int){ (err, vlen, constr) := val_enc(b, e, 0, 1); if(err != "") return (err, i); tag := e.tag; v := tag.class | constr; if(tag.num < 31) { if(!lenonly) b[i] = byte (v | tag.num); i++; } else { if(!lenonly)Lines 1–500 of 1030. Load more
implement ASN1; include "sys.m"; sys: Sys; include "asn1.m"; # Masks TAG_MASK : con 16r1F; CONSTR_MASK : con 16r20; CLASS_MASK : con 16rC0; # Decoding errors OK, ESHORT, ETOOBIG, EVALLEN, ECONSTR, EPRIM, EINVAL, EUNIMPL: con iota; debug : con 0; init() { sys = load Sys Sys->PATH; } # Decode the whole array as a BER encoding of an ASN1 type. # If there's an error, the return string will contain the error. # Depending on the error, the returned elem may or may not # be nil. decode(a: array of byte) : (string, ref Elem) { (ecode, i, elem) := ber_decode(a, 0, len a); return (errstr(ecode, i, len a), elem); } # Like decode, but continue decoding after first element # of array ends. decode_seq(a: array of byte) : (string, list of ref Elem) { (ecode, i, elist) := seq_decode(a, 0, len a, -1, 1); return (errstr(ecode, i, len a), elist); } # 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. decode_value(a: array of byte, kind, constr: int) : (string, ref Value) { n := len a; (ecode, i, val) := value_decode(a, 0, n, n, kind, constr); return (errstr(ecode, i, len a), val); } # The rest of the decoding routines take the array (a), the # starting position (i), and the ending position +1 (n). # They return (err code, new i, [... varies]). # for debugging ber_ind := ""; ber_ind_save := ""; # Decode an ASN1 (tag, length, value). ber_decode(a: array of byte, i, n: int) : (int, int, ref Elem) { if(debug) { ber_ind_save = ber_ind; ber_ind = ber_ind + " "; sys->print("%sber_decode, byte %d\n", ber_ind, i); } err, length: int; tag : Tag; val : ref Value; elem : ref Elem = nil; (err, i, tag) = tag_decode(a, i, n); if(err == OK) { (err, i, length) = length_decode(a, i, n); if(err == OK) { if(debug) sys->print("%sgot tag %s, length %d, now at byte %d\n", ber_ind, tag.tostring(), length, i); if(tag.class == Universal) (err, i, val) = value_decode(a, i, n, length, tag.num, tag.constr); else (err, i, val) = value_decode(a, i, n, length, OCTET_STRING, 0); if(val != nil) elem = ref Elem(tag, val); } } if(debug) { sys->print("%send ber_decode, byte %d\n", ber_ind, i); if(val != nil) { sys->print("%sdecode result:\n", ber_ind); print_elem(elem); } if(err != OK) sys->print("%serror: %s\n", ber_ind, errstr(err, i, i)); ber_ind = ber_ind_save; } return (err, i, elem); } # Decode a tag field. As well as Tag, return an int that # is 1 if the type is constructed, 0 if not. tag_decode(a: array of byte, i, n: int) : (int, int, Tag) { err := OK; class, num, constr: int; if(n-i >= 2) { v := int a[i++]; class = v & CLASS_MASK; if(v & CONSTR_MASK) constr = 1; else constr = 0; num = v & TAG_MASK; if(num == TAG_MASK) # long tag number (err, i, num) = uint7_decode(a, i, n); } else err = ESHORT; return (err, i, Tag(class, num, constr)); } # Decode a length field. Assume it fits in a Limbo int. # If "indefinite length", return -1. length_decode(a: array of byte, i, n: int) : (int, int, int) { err := OK; num := 0; if(i < n) { v := int a[i++]; if(v & 16r80) return int_decode(a, i, n, v&16r7F, 1); else if(v == 16r80) num = -1; else num = v; } else err = ESHORT; return (err, i, num); } # Decode a value according to the encoding of the Universal # type with number "kind" and constructed/primitive according # to "constr", with given length (may be -1, for "indefinite"). value_decode(a: array of byte, i, n, length, kind, constr: int) : (int, int, ref Value) { err := OK; val : ref Value; va : array of byte; if(length == -1) { if(!constr) err = EINVAL; } else if(i+length > n) err = EVALLEN; if(err != OK) return (err, i, nil); case kind { 0 => # marker for end of indefinite constructions if(length == 0) val = ref Value.EOC; else err = EINVAL; BOOLEAN => if(constr) err = ECONSTR; else if(length != 1) err = EVALLEN; else { val = ref Value.Bool(int a[0]); i++; } INTEGER or ENUMERATED => if(constr) err = ECONSTR; else if(length <= 4) { num : int; (err, i, num) = int_decode(a, i, i+length, length, 0); if(err == OK) val = ref Value.Int(num); } else { va = array[length] of byte; va[0:] = a[i:i+length]; val = ref Value.BigInt(va); i += length; } BIT_STRING => if(constr) { if(length == -1 && i+2 <= n && a[i] == byte 0 && a[i+1] == byte 0) { val = ref Value.BitString(0, nil); i += 2; } else # TODO: recurse and concat results err = EUNIMPL; } else { if(length < 2) { if(length == 1 && a[0] == byte 0) { val = ref Value.BitString(0, nil); i ++; } else err = EINVAL; } else { bitsunused := int a[i]; if(bitsunused > 7) err = EINVAL; else if(length > 16r0FFFFFFF) err = ETOOBIG; else { va = array[length-1] of byte; va[0:] = a[i+1:i+length]; val = ref Value.BitString(bitsunused, va); i += length; } } } OCTET_STRING or ObjectDescriptor => (err, i, va) = octet_decode(a, i, n, length, constr); if(err == OK) val = ref Value.Octets(va); NULL => if(constr) err = ECONSTR; else if(length != 0) err = EVALLEN; else val = ref Value.Null; OBJECT_ID => if(constr) err = ECONSTR; else if (length == 0) err = EVALLEN; else { subids : list of int = nil; iend := i+length; while(i < iend) { x : int; (err, i, x) = uint7_decode(a, i, n); if(err != OK) break; subids = x :: subids; } if(err == OK) { if(i != iend) err = EVALLEN; else { m := len subids; ia := array[m+1] of int; while(subids != nil) { y := hd subids; subids = tl subids; if(m == 1) { ia[1] = y % 40; ia[0] = y / 40; } else ia[m--] = y; } val = ref Value.ObjId(ref Oid(ia)); } } } EXTERNAL or EMBEDDED_PDV => # TODO: parse this internally va = array[length] of byte; va[0:] = a[i:i+length]; val = ref Value.Other(va); i += length; REAL => # let the appl decode, with math module if(constr) err = ECONSTR; else { va = array[length] of byte; va[0:] = a[i:i+length]; val = ref Value.Real(va); i += length; } SEQUENCE or SET=> vl : list of ref Elem; (err, i, vl) = seq_decode(a, i, n, length, constr); if(err == OK) { if(kind == SEQUENCE) val = ref Value.Seq(vl); else val = ref Value.Set(vl); } NumericString or PrintableString or TeletexString or VideotexString or IA5String or UTCTime or GeneralizedTime or GraphicString or VisibleString or GeneralString or UniversalString or BMPString => (err, i, va) = octet_decode(a, i, n, length, constr); if(err == OK) # sometimes wrong: need to do char set conversion val = ref Value.String(string va); * => va = array[length] of byte; va[0:] = a[i:i+length]; val = ref Value.Other(va); i += length; } return (err, i, val); } # 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 Limbo int. # If unsigned is not set, make sure to propagate sign bit. int_decode(a: array of byte, i, n, count, unsigned: int) : (int, int, int) { err := OK; num := 0; if(n-i >= count) { if((count > 4) || (unsigned && count == 4 && (int a[i] & 16r80))) err = ETOOBIG; else { if(!unsigned && count > 0 && count < 4 && (int a[i] & 16r80)) num = -1; # all bits set for(j := 0; j < count; j++) { v := int a[i++]; num = (num << 8) | v; } } } else err = ESHORT; return (err, i, num); } # 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 Limbo int. uint7_decode(a: array of byte, i, n: int) : (int, int, int) { err := OK; num := 0; more := 1; while(more && i < n) { v := int a[i++]; if(num & 16r7F000000) { err = ETOOBIG; break; } num <<= 7; more = v & 16r80; num |= (v & 16r7F); } if(n == i) err = ESHORT; return (err, i, num); } # Decode an octet string, recursively if constr. # We've already checked that length==-1 implies constr==1, # and otherwise that specified length fits within a[i..n]. octet_decode(a: array of byte, i, n, length, constr: int) : (int, int, array of byte) { err := OK; va : array of byte; if(length >= 0 && !constr) { va = array[length] of byte; va[0:] = a[i:i+length]; i += length; } else { # constructed, either definite or indefinite length lva : list of array of byte = nil; elem : ref Elem; istart := i; totbytes := 0; cloop: for(;;) { if(length >= 0 && i >= istart+length) { if(i != istart+length) err = EVALLEN; break cloop; } oldi := i; (err, i, elem) = ber_decode(a, i, n); if(err != OK) break; pick v := elem.val { Octets => lva = v.bytes :: lva; totbytes += len v.bytes; EOC => if(length != -1) { i = oldi; err = EINVAL; } break cloop; * => i = oldi; err = EINVAL; break cloop; } } if(err == OK) { va = array[totbytes] of byte; j := totbytes; while(lva != nil) { x := hd lva; lva = tl lva; m := len x; va[j-m:] = x[0:]; j -= m; } } } return (err, i, va); } # Decode a sequence or set. # We've already checked that length==-1 implies constr==1, # and otherwise that specified length fits within a[i..n]. seq_decode(a : array of byte, i, n, length, constr: int) : (int, int, list of ref Elem) { err := OK; ans : list of ref Elem = nil; if(!constr) err = EPRIM; else { # constructed, either definite or indefinite length lve : list of ref Elem = nil; elem : ref Elem; istart := i; cloop: for(;;) { if(length >= 0 && i >= istart+length) { if(i != istart+length) err = EVALLEN; break cloop; } oldi := i; (err, i, elem) = ber_decode(a, i, n); if(err != OK) break; pick v := elem.val { EOC => if(length != -1) { i = oldi; err = EINVAL; } break cloop; * => lve = elem :: lve; } } if(err == OK) { # reverse back to original order while(lve != nil) { e := hd lve; lve = tl lve; ans = e :: ans; } } } return (err, i, ans); } # Encode e by BER rules encode(e: ref Elem) : (string, array of byte) { (err, n) := enc(nil, e, 0, 1); if(err != "") return (err, nil); b := array[n] of byte; enc(b, e, 0, 0); return ("", b); } # Encode e into array b, only putting in bytes if !lenonly. # Start at loc i, return index after. enc(b: array of byte, e: ref Elem, i, lenonly: int) : (string, int) { (err, vlen, constr) := val_enc(b, e, 0, 1); if(err != "") return (err, i); tag := e.tag; v := tag.class | constr; if(tag.num < 31) { if(!lenonly) b[i] = byte (v | tag.num); i++; } else { if(!lenonly)