diff --git a/lib/7.8/modules/Standards.pmod/ASN1.pmod/Types.pmod b/lib/7.8/modules/Standards.pmod/ASN1.pmod/Types.pmod new file mode 100644 index 0000000000000000000000000000000000000000..e13d762fea94e52bccf6b4473fe8445df69c4c22 --- /dev/null +++ b/lib/7.8/modules/Standards.pmod/ASN1.pmod/Types.pmod @@ -0,0 +1,1539 @@ +//! Encodes various asn.1 objects according to the Distinguished +//! Encoding Rules (DER) + +#pike 8.0 +#pragma strict_types + +#if 0 +#define WERROR werror +#else +#define WERROR(x ...) +#endif + +// Helper functions + +//! Combines tag and class to a single integer, for internal uses. +//! +//! @param cls +//! ASN1 type class (0..3). +//! @param tag +//! ASN1 type tag (1..). +//! @returns +//! The combined tag. +//! @seealso +//! @[extract_tag], @[extract_cls] +int make_combined_tag(int cls, int tag) +{ return tag << 2 | cls; } + +//! Extract ASN1 type tag from a combined tag. +//! @seealso +//! @[make_combined_tag] +int extract_tag(int i) { return i >> 2; } + +//! Extract ASN1 type class from a combined tag. +//! @seealso +//! @[make_combined_tag] +int(0..3) extract_cls(int i) { return [int(0..3)](i & 3); } + + +// Class definitions + +//! Generic, abstract base class for ASN1 data types. +class Object +{ + int cls = 0; + int tag = 0; + constant constructed = 0; + + constant type_name = ""; + + //! Return the DER payload. + string(8bit) get_der_content() + { + return ""; + } + + string(0..255) der_encode() + { + return build_der(get_der_content()); + } + + //! Get the class of this object. + //! + //! @returns + //! The class of this object. + int get_cls() { return cls; } + + //! Get the tag for this object. + //! + //! @returns + //! The tag for this object. + int get_tag() { return tag; } + + //! Get the combined tag (tag + class) for this object. + //! + //! @returns + //! the combined tag header + int get_combined_tag() { + return make_combined_tag(get_cls(), get_tag()); + } + + string(0..255) der; + + // Should be overridden by subclasses + this_program decode_primitive(string contents, + function(ADT.struct, + mapping(int:program(Object)): + Object) decoder, + mapping(int:program(Object)) types); + void begin_decode_constructed(string raw); + void decode_constructed_element(int i, object e); + this_program end_decode_constructed(int length); + + mapping(int:program(Object)) element_types(int i, + mapping(int:program(Object)) types) { + return types; i; + } + this_program init(mixed ... args) { return this; args; } + + protected string(0..255) to_base_128(int n) + { + string(0..255) ret = [string(0..127)]sprintf("%c", n&0x7f); + n >>= 7; + while( n ) + { + ret = [string(0..255)]sprintf("%c", (n&0x7f) | 0x80) + ret; + n >>= 7; + } + return ret; + } + + protected string(0..255) encode_tag() + { + int tag = get_tag(); + int cls = get_cls(); + if (tag < 31) + return [string(0..255)]sprintf("%c", + (cls << 6) | (constructed << 5) | tag); + + return [string(0..255)]sprintf("%c%s", + (cls << 6) | (constructed << 5) | 0x1f, + to_base_128(tag) ); + } + + protected string(0..255) encode_length(int|object len) + { + if (len < 0x80) + return [string(0..255)]sprintf("%c", len); + string s = Gmp.mpz(len)->digits(256); + if (sizeof(s) >= 0x80) + error("Max length exceeded.\n" ); + return [string(0..255)]sprintf("%c%s", sizeof(s) | 0x80, s); + } + + protected string(0..255) build_der(string(0..255) contents) + { + string(0..255) data = + encode_tag() + encode_length(sizeof(contents)) + contents; + WERROR("build_der: %O\n", data); + return data; + } + + string(0..255) record_der(string(0..255) s) + { + return (der = s); + } + + void record_der_contents(string(0..255) s) + { + der = build_der(s); + } + + + //! Get the DER encoded version of this object. + //! + //! @returns + //! DER encoded representation of this object. + string(0..255) get_der() { + return der || (der = der_encode()); + } + + protected void create(mixed ...args) { + WERROR("asn1_object[%s]->create\n", type_name); + if (sizeof(args)) + init(@args); + } +} + +//! Compound object primitive +class Compound +{ + inherit Object; + + constant constructed = 1; + + //! Contents of compound object. + array(Object) elements = ({ }); + + this_program init(array args) { + WERROR("asn1_compound[%s]->init(%O)\n", type_name, args); + foreach(args, mixed o) + if (!objectp(o)) + error( "Non-object argument %O\n", o ); + elements = [array(Object)]args; + WERROR("asn1_compound: %O\n", elements); + return this; + } + + void begin_decode_constructed(string(0..255) raw) { + WERROR("asn1_compound[%s]->begin_decode_constructed\n", type_name); + record_der_contents(raw); + } + + void decode_constructed_element(int i, object e) { + WERROR("asn1_compound[%s]->decode_constructed_element(%O)\n", + type_name, e); + if (i != sizeof(elements)) + error("Unexpected index!\n"); + elements += ({ e }); + } + + this_program end_decode_constructed(int length) { + if (length != sizeof(elements)) + error("Invalid length!\n"); + return this; + } + + protected mixed `[](mixed index) + { + if( intp(index) ) + return elements[index]; + return ::`[]([string]index); + } + + protected int _sizeof() + { + return sizeof(elements); + } + + protected string _sprintf(int t,mapping(string:int)|void params) { + if (params) ++params->indent; else params=([]); + return t=='O' && sprintf("%O(%*O)", this_program, params, elements); + } + + string debug_string() { + WERROR("asn1_compound[%s]->debug_string(), elements = %O\n", + type_name, elements); + return _sprintf('O'); + } +} + +//! string object primitive +class String +{ + inherit Object; + + //! value of object + string value; + + this_program init(string(0..255) s) { + value = s; + return this; + } + + string(0..255) get_der_content() { + return [string(0..255)]value; + } + + this_program decode_primitive(string(0..255) contents, + function(ADT.struct, + mapping(int:program(Object)): + Object)|void decoder, + mapping(int:program(Object))|void types) { + record_der_contents(contents); + value = contents; + return this; + } + + void begin_decode_constructed(string raw) + { + value = ""; + } + + void decode_constructed_element(int i, object(this_program) e) + { + value += e->value; + } + + this_program end_decode_constructed(int length) + { + return this; + } + + protected string _sprintf(int t) { + return t=='O' && sprintf("%O(%O)", this_program, value); + } + + string debug_string() { + WERROR("asn1_string[%s]->debug_string(), value = %O\n", type_name, value); + return _sprintf('O'); + } +} + +//! boolean object +class Boolean +{ + inherit Object; + int tag = 1; + constant type_name = "BOOLEAN"; + + //! value of object + int value; + + this_program init(int x) { + value = x; + return this; + } + + // While every non-zero value is true, the canonical true is 0xff. + string(0..255) get_der_content() + { + return value ? "\377" : "\0"; + } + + this_program decode_primitive(string(0..255) contents, + function(ADT.struct, + mapping(int:program(Object)): + Object)|void decoder, + mapping(int:program(Object))|void types) { + record_der_contents(contents); + if( contents=="" ) error("Illegal boolean value.\n"); + value = (contents != "\0"); + return this; + } + + protected string _sprintf(int t) { + return t=='O' && sprintf("%O(%s)", this_program, (value?"TRUE":"FALSE")); + } + + string debug_string() { + return value ? "TRUE" : "FALSE"; + } +} + +//! Integer object +//! All integers are represented as bignums, for simplicity +class Integer +{ + inherit Object; + int tag = 2; + constant type_name = "INTEGER"; + + //! value of object + Gmp.mpz value; + + this_object init(int|object n) { + value = Gmp.mpz(n); + WERROR("i = %s\n", value->digits()); + return this; + } + + string(0..255) get_der_content() + { + string(0..255) s; + + if (value < 0) + { + Gmp.mpz n = [object(Gmp.mpz)](value + + pow(256, ([object(Gmp.mpz)](- value))-> + size(256))); + s = n->digits(256); + if (!(s[0] & 0x80)) + s = "\377" + s; + } else { + s = value->digits(256); + if (s[0] & 0x80) + s = "\0" + s; + } + + return s; + } + + this_object decode_primitive(string(0..255) contents, + function(ADT.struct, + mapping(int:program(Object)): + Object)|void decoder, + mapping(int:program(Object))|void types) { + record_der_contents(contents); + value = Gmp.mpz(contents, 256); + if (contents[0] & 0x80) /* Negative */ + value -= pow(256, sizeof(contents)); + return this; + } + + protected string _sprintf(int t) { + if(t!='O') return UNDEFINED; + if(!value) return sprintf("%O(0)", this_program); + return sprintf("%O(%d %s)", this_program, + value->size(), value->digits()); + } + + string debug_string() { + return sprintf("INTEGER (%d) %s", value->size(), value->digits()); + } +} + +//! Enumerated object +class Enumerated +{ + inherit Integer; + int tag = 10; + constant type_name = "ENUMERATED"; +} + +class Real +{ + inherit Object; + int tag = 9; + constant type_name = "REAL"; + + float value; + + string(0..255) get_der_content() + { + string v = sprintf("%F", value); + switch(v) + { + case "\0\0\0\0" : return ""; // 0.0 + case "\200\0\0\0" : return "\x43"; // -0.0 + case "\177\200\0\0" : return "\x40"; // inf + case "\377\200\0\0" : return "\x41"; // -inf + + case "\177\300\0\0" : // nan + case "\377\300\0\0" : // -nan + return "\x42"; + } + + error("Encoding Real values not supported.\n"); + } + + this_object decode_primitive(string(0..255) contents, + function(ADT.struct, + mapping(int:program(Object)): + Object) decoder, + mapping(int:program(Object))|void types) { + if( contents=="" ) { value = 0.0; return this; } + int first = contents[0]; + switch( first ) + { + // SpecialRealValues + case 0b01000000: value = Math.inf; return this; + case 0b01000001: value = -Math.inf; return this; + case 0b01000010: value = Math.nan; return this; + case 0b01000011: value = -0.0; return this; + + // ISO 6093 + case 0b00000001: error("ISO 6093 NR1 not supported.\n"); + case 0b00000010: error("ISO 6093 NR2 not supported.\n"); + case 0b00000011: error("ISO 6093 NR3 not supported.\n"); + } + switch( first & 0xc0 ) + { + case 0x00: + error("Unknown real coding.\n"); + case 0x40: + error("Unknown SpecialRealValues code.\n"); + } + + int neg = first & 0b01000000; + int base; + switch(first & 0b00110000) + { + case 0b00000000: base = 2; break; + case 0b00010000: base = 8; break; + case 0b00100000: base = 16; break; + default: error("Unknown real base.\n"); + } + + int scaling = (first & 0b00001100) >> 2; + + int exp; + int num; + switch(first & 0b00000011) + { + case 0b00: + sscanf(contents, "%*1c%+1c%+"+(sizeof(contents)-2)+"c", exp, num); + break; + case 0b01: + sscanf(contents, "%*1c%+2c%+"+(sizeof(contents)-3)+"c", exp, num); + break; + case 0b10: + sscanf(contents, "%*1c%+3c%+"+(sizeof(contents)-4)+"c", exp, num); + break; + case 0b11: + int e_size = contents[1]; + int n_size = sizeof(contents)-2-e_size; + sscanf(contents, "%*2c%+"+e_size+"c%+"+n_size+"c", exp, num); + break; + } + + int mantissa = num * (1<<scaling); + if( neg ) mantissa = -mantissa; + value = mantissa * pow((float)base, exp); + + return this; + } + + string debug_string() + { + return sprintf("REAL %O", value); + } +} + +//! Bit string object +class BitString +{ + inherit Object; + int tag = 3; + constant type_name = "BIT STRING"; + + //! value of object + string(0..255) value; + + int unused = 0; + + this_program init(string(0..255) s) + { + value = s; + return this; + } + + string(0..255) get_der_content() + { + return [string(0..255)]sprintf("%c%s", unused, value); + } + + //! Set the bitstring value as a string with @expr{"1"@} and + //! @expr{"0"@}. + this_program set_from_ascii(string(0..255) s) + { + array v = array_sscanf(s, "%8b"*(sizeof(s)/8)+"%b"); + v[-1] = v[-1]<<((-sizeof(s))%8); + value = (string(0..255))v; + set_length(sizeof(s)); + return this; + } + + //! Sets the length of the bit string to @[len] number of bits. Will + //! only work correctly on strings longer than @[len] bits. + this_program set_length(int len) { + if (len) + { + value = value[..(len + 7)/8]; + unused = (- len) % 8; + value[-1] &= 256-(1<<unused); + } else { + unused = 0; + value = ""; + } + return this; + } + + this_program decode_primitive(string(0..255) contents, + function(ADT.struct, + mapping(int:program(Object)): + Object)|void decoder, + mapping(int:program(Object))|void types) { + record_der_contents(contents); + if (!sizeof(contents)) + return 0; + unused = contents[0]; + + if (unused >= 8) + return 0; + value = contents[1..]; + return this; + } + + void begin_decode_constructed(string raw) + { + unused = 0; + value = ""; + } + + void decode_constructed_element(int i, object(this_program) e) + { + if( unused ) error("Adding to a non-aligned bit stream.\n"); + value += e->value; + unused = e->unused; + } + + this_program end_decode_constructed(int length) + { + return this; + } + + protected string _sprintf(int t) { + if(t!='O') return UNDEFINED; + if(!value) return sprintf("%O(0)", this_program); + int size = sizeof(value)*8-unused; + if(!unused) return sprintf("%O(%d %O)", this_program, size, value); + return sprintf("%O(%d %0"+size+"s)", this_program, size, + ([object(Gmp.mpz)](Gmp.mpz(value, 256) >> unused)) + ->digits(2)); + } + + string debug_string() { + return sprintf("BIT STRING (%d) %s", + sizeof(value) * 8 - unused, + ([object(Gmp.mpz)](Gmp.mpz(value, 256) >> unused)) + ->digits(2)); + } +} + +//! Octet string object +class OctetString +{ + inherit String; + int tag = 4; + constant type_name = "OCTET STRING"; +} + +//! Null object +class Null +{ + inherit Object; + int tag = 5; + constant type_name = "NULL"; + + string(0..255) get_der_content() { return ""; } + + this_program decode_primitive(string(0..255) contents, + function(ADT.struct, + mapping(int:program(Object)): + Object)|void decoder, + mapping(int:program(Object))|void types) { + record_der_contents(contents); + return !sizeof(contents) && this; + } + + string debug_string() { return "NULL"; } +} + +//! Object identifier object +class Identifier +{ + inherit Object; + int tag = 6; + constant type_name = "OBJECT IDENTIFIER"; + + //! value of object + array(int) id; + + this_program init(int ... args) { + if ( (sizeof(args) < 2) + || (args[0] > 2) + || (args[1] >= ( (args[0] < 2) ? 40 : 176) )) + error( "Invalid object identifier.\n" ); + id = args; + return this; + } + + mixed _encode() { return id; } + void _decode(array(int) data) { id=data; } + + //! Returns a new @[Identifier] object with @[args] appended to the + //! ID path. + this_program append(int ... args) { + return this_program(@id, @args); + } + + string(0..255) get_der_content() + { + return [string(0..255)]sprintf("%s%@s", + to_base_128(40 * id[0] + id[1]), + map(id[2..], to_base_128)); + } + + this_program decode_primitive(string(0..255) contents, + function(ADT.struct, + mapping(int:program(Object)): + Object)|void decoder, + mapping(int:program(Object))|void types) { + record_der_contents(contents); + + if (contents[0] < 120) + id = ({ contents[0] / 40, contents[0] % 40 }); + else + id = ({ 2, contents[0] - 80 }); + int index = 1; + while(index < sizeof(contents)) + { + int element = 0; + do { + element = element << 7 | (contents[index] & 0x7f); + } while(contents[index++] & 0x80); + id += ({ element }); + } + return this; + } + + protected string _sprintf(int t) { + if(t!='O') return UNDEFINED; + if(!id) return sprintf("%O(0)", this_program); + return sprintf("%O(%s)", this_program, (array(string))id*"."); + } + + string debug_string() { + return "IDENTIFIER " + (array(string)) id * "."; + } + + protected int __hash() + { + return hash(get_der()); + } + + protected int(0..1) `==(mixed other) { + return (objectp(other) && + (this_program == object_program(other)) && + equal(id, ([object(Identifier)]other)->id)); + } + + protected int(0..1) `<(mixed other) { + if( !objectp(other) || + (this_program != object_program(other)) ) + return 0; + array oid = ([object(Identifier)]other)->id; + for( int i; i<min(sizeof(id),sizeof(oid)); i++ ) + { + if( id[i] < oid[i] ) return 1; + if( id[i] > oid[i] ) return 0; + } + return sizeof(id) < sizeof(oid); + } +} + +//! Checks if a Pike string can be encoded with UTF8. That is +//! always the case... +int(1..1) asn1_utf8_valid (string s) +{ + return 1; +} + +//! UTF8 string object +//! +//! Character set: ISO/IEC 10646-1 (compatible with Unicode). +//! +//! Variable width encoding, see rfc2279. +class UTF8String +{ + inherit String; + int tag = 12; + constant type_name = "UTF8String"; + + string(0..255) get_der_content() + { + return string_to_utf8(value); + } + + this_program decode_primitive(string(0..255) contents, + function(ADT.struct, + mapping(int:program(Object)): + Object)|void decoder, + mapping(int:program(Object))|void types) { + der = contents; + if (catch { + value = utf8_to_string(contents); + }) + return 0; + + return this; + } +} + +//! Sequence object +class Sequence +{ + inherit Compound; + int tag = 16; + constant type_name = "SEQUENCE"; + + string(0..255) get_der_content() + { + WERROR("ASN1.Sequence: elements = '%O\n", elements); + array(string) a = elements->get_der(); + WERROR("ASN1.Sequence: der_encode(elements) = '%O\n", a); + return [string(0..255)]`+("", @ a); + } + + this_program decode_primitive(string(0..255) contents, + function(ADT.struct, + mapping(int:program(Object)): + Object) decoder, + mapping(int:program(Object)) types) { + der = contents; + elements = ({}); + ADT.struct struct = ADT.struct(contents); + while (!struct->is_empty()) { + elements += ({ decoder(struct, types) }); + } + return this; + } +} + +//! Set object +class Set +{ + inherit Compound; + int tag = 17; + constant type_name = "SET"; + + int(-1..1) compare_octet_strings(string r, string s) + { + if (r == s) return 0; + + for(int i = 0;; i++) { + if (i == sizeof(r)) + return (i = sizeof(s)) ? 0 : -1; + if (i == sizeof(s)) + return 1; + if (r[i] < s[i]) + return -1; + else if (r[i] > s[i]) + return 1; + } + } + + string get_der_content() { + WERROR("asn1_set->der: elements = '%O\n", elements); + array(string) a = elements->get_der(); + WERROR("asn1_set->der: der_encode(elements) = '%O\n", a); + return [string(0..255)] + `+("", @[array(string)] + Array.sort_array(a, compare_octet_strings)); + } +} + +Regexp asn1_printable_invalid_chars = Regexp("([^-A-Za-z0-9 '()+,./:=?])"); + +//! Checks if a Pike string can be encoded as a @[PrintableString]. +int(0..1) asn1_printable_valid (string s) { + if (global.String.width(s)!=8) return 0; + return !asn1_printable_invalid_chars->match(s); +} + +//! PrintableString object +class PrintableString +{ + inherit String; + int tag = 19; + constant type_name = "PrintableString"; +} + +Regexp asn1_teletex_invalid_chars = Regexp ("([\\\\{}\240����\255�])"); + +//! +int(0..1) asn1_teletex_valid (string s) +{ + if (global.String.width(s)!=8) + // T.61 encoding of wide strings not implemented. + return 0; + return !asn1_teletex_invalid_chars->match (s); +} + +//! TeletexString object +//! +//! Avoid this one; it seems to be common that this type is used to +//! label strings encoded with the ISO 8859-1 character set (use +//! asn1_broken_teletex_string for that). From +//! http://www.mindspring.com/~asn1/nlsasn.htm: +//! +//! /.../ Types GeneralString, VideotexString, TeletexString +//! (T61String), and GraphicString exist in earlier versions +//! [pre-1994] of ASN.1. They are considered difficult to use +//! correctly by applications providing national language support. +//! Varying degrees of application support for T61String values seems +//! to be most common in older applications. Correct support is made +//! more difficult, as character values available in type T61String +//! have changed with the addition of new register entries from the +//! 1984 through 1997 versions. +//! +//! This implementation is based on the description of T.61 and T.51 +//! in "Some functions for representing T.61 characters from the +//! X.500 Directory Service in ISO 8859 terminals (Version 0.2. July +//! 1994.)" by Enrique Silvestre Mora (mora@@si.uji.es), Universitat +//! Jaume I, Spain, found in the package +//! ftp://pereiii.uji.es/pub/uji-ftp/unix/ldap/iso-t61.translation.tar.Z +//! +//! The translation is only complete for 8-bit latin 1 strings. It +//! encodes strictly to T.61, but decodes from the superset T.51. +//! +//! @note +//! CCITT Recommendation T.61 is also known as ISO-IR 103:1985 +//! (graphic characters) and ISO-IR 106:1985 and ISO-IR 107:1985 +//! (control characters). +//! +//! @seealso +//! @[Charset] +class TeletexString +{ + inherit String; + int tag = 20; + constant type_name = "TeletexString"; // Alias: T61String + +#define ENC_ERR(char) char +#define DEC_ERR(str) str +#define DEC_COMB_MARK "\300" + +#define GR(char) "\301" char /* Combining grave accent */ +#define AC(char) "\302" char /* Combining acute accent */ +#define CI(char) "\303" char /* Combining circumflex accent */ +#define TI(char) "\304" char /* Combining tilde */ +#define MA(char) "\305" char /* Combining macron */ +#define BR(char) "\306" char /* Combining breve */ +#define DA(char) "\307" char /* Combining dot above */ +#define DI(char) "\310" char /* Combining diaeresis */ +#define RA(char) "\312" char /* Combining ring above */ +#define CE(char) "\313" char /* Combining cedilla */ +#define UN(char) "\314" char /* Combining underscore (note 6) */ +#define DO(char) "\315" char /* Combining double acute accent */ +#define OG(char) "\316" char /* Combining ogonek */ +#define CA(char) "\317" char /* Combining caron */ + + constant encode_from = ({ + /*"#", "$",*/ "�", // Note 3 + "\\", "{", "}", // Note 7 + "\240", // No-break space (note 7) + "�", // Multiplication sign + "�", // Division sign + "�", // Superscript one + "�", // Registered sign (note 7) + "�", // Copyright sign (note 7) + "�", // Not sign (note 7) + "�", // Broken bar (note 7) + "�", // Latin capital ligature ae + "�", // Feminine ordinal indicator + "�", // Latin capital letter o with stroke + "�", // Masculine ordinal indicator + "�", // Latin capital letter thorn + "�", // Latin small ligature ae + "�", // Latin small letter eth + "�", // Latin small letter o with stroke + "�", // Latin small letter sharp s + "�", // Latin small letter thorn + "\255", // Soft hyphen (note 7) + "�", // Latin capital letter eth (no equivalent) + // Combinations + "^", "`", "~", // Note 4 + "�", "�", "�", "�", + "�", "�", "�", "�", "�", "�", + "�", "�", "�", "�", "�", "�", + "�", + "�", + "�", "�", "�", "�", + "�", "�", "�", "�", + "�", "�", "�", "�", + "�", "�", "�", "�", + "�", + "�", + "�", "�", "�", "�", "�", + "�", "�", "�", "�", "�", + "�", "�", "�", "�", + "�", "�", "�", "�", + "�", + "�", + }); + + constant encode_to = ({ + /*"#", "$",*/ "\250", // Note 3 + ENC_ERR("\\"), ENC_ERR("{"), ENC_ERR("}"), // Note 7 + ENC_ERR("\240"), // No-break space (note 7) + "\264", // Multiplication sign + "\270", // Division sign + "\321", // Superscript one + ENC_ERR("�"), // Registered sign (note 7) + ENC_ERR("�"), // Copyright sign (note 7) + ENC_ERR("�"), // Not sign (note 7) + ENC_ERR("�"), // Broken bar (note 7) + "\341", // Latin capital ligature ae + "\343", // Feminine ordinal indicator + "\351", // Latin capital letter o with stroke + "\353", // Masculine ordinal indicator + "\354", // Latin capital letter thorn + "\361", // Latin small ligature ae + "\363", // Latin small letter eth + "\371", // Latin small letter o with stroke + "\373", // Latin small letter sharp s + "\374", // Latin small letter thorn + ENC_ERR("\255"), // Soft hyphen (note 7) + ENC_ERR("�"), // Latin capital letter eth (no equivalent) + // Combinations + CI(" "), GR(" "), TI(" "), // Note 4 + AC(" "), DI(" "), MA(" "), CE(" "), + GR("A"), AC("A"), CI("A"), TI("A"), DI("A"), RA("A"), + GR("a"), AC("a"), CI("a"), TI("a"), DI("a"), RA("a"), + CE("C"), + CE("c"), + GR("E"), AC("E"), CI("E"), DI("E"), + GR("e"), AC("e"), CI("e"), DI("e"), + GR("I"), AC("I"), CI("I"), DI("I"), + GR("i"), AC("i"), CI("i"), DI("i"), + TI("N"), + TI("n"), + GR("O"), AC("O"), CI("O"), TI("O"), DI("O"), + GR("o"), AC("o"), CI("o"), TI("o"), DI("o"), + GR("U"), AC("U"), CI("U"), DI("U"), + GR("u"), AC("u"), CI("u"), DI("u"), + GR("Y"), + GR("y"), + }); + + constant decode_from = ({ + /*"#", "$",*/ "\244", "\246", "\250", // Note 3 + /*"^", "`", "~",*/ // Note 4 + "\251", // Left single quotation mark (note 7) + "\252", // Left double quotation mark (note 7) + "\254", // Leftwards arrow (note 7) + "\255", // Upwards arrow (note 7) + "\256", // Rightwards arrow (note 7) + "\257", // Downwards arrow (note 7) + "\264", // Multiplication sign + "\270", // Division sign + "\271", // Right single quotation mark (note 7) + "\272", // Right double quotation mark (note 7) + "\300", // Note 5 + GR(""), // Combining grave accent + AC(""), // Combining acute accent + CI(""), // Combining circumflex accent + TI(""), // Combining tilde + MA(""), // Combining macron + BR(""), // Combining breve + DA(""), // Combining dot above + DI(""), // Combining diaeresis + "\311", // Note 5 + RA(""), // Combining ring above + CE(""), // Combining cedilla + UN(""), // Combining underscore (note 6) + DO(""), // Combining double acute accent + OG(""), // Combining ogonek + CA(""), // Combining caron + "\320", // Em dash (note 7) + "\321", // Superscript one + "\322", // Registered sign (note 7) + "\323", // Copyright sign (note 7) + "\324", // Trade mark sign (note 7) + "\325", // Eighth note (note 7) + "\326", // Not sign (note 7) + "\327", // Broken bar (note 7) + "\330", "\331", "\332", "\333", // Note 2 + "\334", // Vulgar fraction one eighth (note 7) + "\335", // Vulgar fraction three eighths (note 7) + "\336", // Vulgar fraction five eighths (note 7) + "\337", // Vulgar fraction seven eighths (note 7) + "\340", // Ohm sign + "\341", // Latin capital ligature ae + "\342", // Latin capital letter d with stroke + "\343", // Feminine ordinal indicator + "\344", // Latin capital letter h with stroke + "\345", // Note 2 + "\346", // Latin capital ligature ij + "\347", // Latin capital letter l with middle dot + "\350", // Latin capital letter l with stroke + "\351", // Latin capital letter o with stroke + "\352", // Latin capital ligature oe + "\353", // Masculine ordinal indicator + "\354", // Latin capital letter thorn + "\355", // Latin capital letter t with stroke + "\356", // Latin capital letter eng + "\357", // Latin small letter n preceded by apostrophe + "\360", // Latin small letter kra + "\361", // Latin small ligature ae + "\362", // Latin small letter d with stroke + "\363", // Latin small letter eth + "\364", // Latin small letter h with stroke + "\365", // Latin small letter dotless i + "\366", // Latin small ligature ij + "\367", // Latin small letter l with middle dot + "\370", // Latin small letter l with stroke + "\371", // Latin small letter o with stroke + "\372", // Latin small ligature oe + "\373", // Latin small letter sharp s + "\374", // Latin small letter thorn + "\375", // Latin small letter t with stroke + "\376", // Latin small letter eng + "\377", // Soft hyphen (note 7) + }); + + constant decode_to = ({ + /*"#", "$",*/ "$", "#", "\244", // Note 3 + /*"^", "`", "~",*/ // Note 4 + DEC_ERR("\251"), // Left single quotation mark (note 7) + DEC_ERR("\252"), // Left double quotation mark (note 7) + DEC_ERR("\254"), // Leftwards arrow (note 7) + DEC_ERR("\255"), // Upwards arrow (note 7) + DEC_ERR("\256"), // Rightwards arrow (note 7) + DEC_ERR("\257"), // Downwards arrow (note 7) + "�", // Multiplication sign + "�", // Division sign + DEC_ERR("\271"), // Right single quotation mark (note 7) + DEC_ERR("\272"), // Right double quotation mark (note 7) + DEC_ERR("\300"), // Note 5 + DEC_COMB_MARK GR(""), // Combining grave accent + DEC_COMB_MARK AC(""), // Combining acute accent + DEC_COMB_MARK CI(""), // Combining circumflex accent + DEC_COMB_MARK TI(""), // Combining tilde + DEC_COMB_MARK MA(""), // Combining macron + DEC_COMB_MARK BR(""), // Combining breve + DEC_COMB_MARK DA(""), // Combining dot above + DEC_COMB_MARK DI(""), // Combining diaeresis + DEC_ERR("\311"), // Note 5 + DEC_COMB_MARK RA(""), // Combining ring above + DEC_COMB_MARK CE(""), // Combining cedilla + DEC_COMB_MARK UN(""), // Combining underscore (note 6) + DEC_COMB_MARK DO(""), // Combining double acute accent + DEC_COMB_MARK OG(""), // Combining ogonek + DEC_COMB_MARK CA(""), // Combining caron + DEC_ERR("\320"), // Em dash (note 7) + "�", // Superscript one + "�", // Registered sign (note 7) + "�", // Copyright sign (note 7) + DEC_ERR("\324"), // Trade mark sign (note 7) + DEC_ERR("\325"), // Eighth note (note 7) + "�", // Not sign (note 7) + "�", // Broken bar (note 7) + DEC_ERR("\330"), DEC_ERR("\331"), DEC_ERR("\332"), DEC_ERR("\333"), // Note 2 + DEC_ERR("\334"), // Vulgar fraction one eighth (note 7) + DEC_ERR("\335"), // Vulgar fraction three eighths (note 7) + DEC_ERR("\336"), // Vulgar fraction five eighths (note 7) + DEC_ERR("\337"), // Vulgar fraction seven eighths (note 7) + DEC_ERR("\340"), // Ohm sign + "�", // Latin capital ligature ae + DEC_ERR("\342"), // Latin capital letter d with stroke + "�", // Feminine ordinal indicator + DEC_ERR("\344"), // Latin capital letter h with stroke + DEC_ERR("\345"), // Note 2 + DEC_ERR("\346"), // Latin capital ligature ij + DEC_ERR("\347"), // Latin capital letter l with middle dot + DEC_ERR("\350"), // Latin capital letter l with stroke + "�", // Latin capital letter o with stroke + DEC_ERR("\352"), // Latin capital ligature oe + "�", // Masculine ordinal indicator + "�", // Latin capital letter thorn + DEC_ERR("\355"), // Latin capital letter t with stroke + DEC_ERR("\356"), // Latin capital letter eng + DEC_ERR("\357"), // Latin small letter n preceded by apostrophe + DEC_ERR("\360"), // Latin small letter kra + "�", // Latin small ligature ae + DEC_ERR("\362"), // Latin small letter d with stroke + "�", // Latin small letter eth + DEC_ERR("\364"), // Latin small letter h with stroke + DEC_ERR("\365"), // Latin small letter dotless i + DEC_ERR("\366"), // Latin small ligature ij + DEC_ERR("\367"), // Latin small letter l with middle dot + DEC_ERR("\370"), // Latin small letter l with stroke + "�", // Latin small letter o with stroke + DEC_ERR("\372"), // Latin small ligature oe + "�", // Latin small letter sharp s + "�", // Latin small letter thorn + DEC_ERR("\375"), // Latin small letter t with stroke + DEC_ERR("\376"), // Latin small letter eng + "\255", // Soft hyphen (note 7) + }); + + constant decode_comb = ([ + GR(" "): "`", + AC(" "): "�", + CI(" "): "^", + TI(" "): "~", + DI(" "): "�", + // RA(" "): DEC_ERR(RA(" ")), + MA(" "): "�", + // BR(" "): DEC_ERR(BR(" ")), + // DA(" "): DEC_ERR(DA(" ")), + CE(" "): "�", + // DO(" "): DEC_ERR(DO(" ")), + // OG(" "): DEC_ERR(OG(" ")), + // CA(" "): DEC_ERR(CA(" ")), + GR("A"): "�", AC("A"): "�", CI("A"): "�", TI("A"): "�", DI("A"): "�", RA("A"): "�", + GR("a"): "�", AC("a"): "�", CI("a"): "�", TI("a"): "�", DI("a"): "�", RA("a"): "�", + CE("C"): "�", + CE("c"): "�", + GR("E"): "�", AC("E"): "�", CI("E"): "�", DI("E"): "�", + GR("e"): "�", AC("e"): "�", CI("e"): "�", DI("e"): "�", + GR("I"): "�", AC("I"): "�", CI("I"): "�", DI("I"): "�", + GR("i"): "�", AC("i"): "�", CI("i"): "�", DI("i"): "�", + TI("N"): "�", + TI("n"): "�", + GR("O"): "�", AC("O"): "�", CI("O"): "�", TI("O"): "�", DI("O"): "�", + GR("o"): "�", AC("o"): "�", CI("o"): "�", TI("o"): "�", DI("o"): "�", + GR("U"): "�", AC("U"): "�", CI("U"): "�", DI("U"): "�", + GR("u"): "�", AC("u"): "�", CI("u"): "�", DI("u"): "�", + GR("Y"): "�", + GR("y"): "�", + ]); + + /* Notes from Moras paper: + + (1) All characters in 0xC0-0xCF are non-spacing characters. They are + all diacritical marks. To be represented stand-alone, they need to + be followed by a SPACE (0x20). They can appear, also, before + letters if the couple is one of the defined combinations. + + (2) Reserved for future standardization. + + (3) Current terminals may send and receive 0xA6 and 0xA4 for the NUMBER + SIGN and DOLLAR SIGN, respectively. When receiving codes 0x23 and + 0x24, they may interpret them as NUMBER SIGN and CURRENCY SIGN, + respectively. Future applications should code the NUMBER SIGN, + DOLLAR SIGN and CURRENCY SIGN as 0x23, 0x24 and 0xA8, respectively. + + (4) Terminals should send only the codes 0xC1, 0xC3 and 0xC4, followed + by SPACE (0x20) for stand-alone GRAVE ACCENT, CIRCUMFLEX ACCENT and + TILDE, respectively. Nevertheless the terminal shall interpret the + codes 0x60, 0x5E and 0x7E as GRAVE, CIRCUMFLEX and TILDE, + respectively. + + (5) This code position is reserved and shall not be used. + + (6) It is recommended to implement the "underline" function by means of + the control function SGR(4) instead of the "non-spacing underline" + graphic character. + + (7) Not used in current teletex service (Recommendation T.61). + */ + +#undef GR +#undef AC +#undef CI +#undef TI +#undef MA +#undef BR +#undef DA +#undef DI +#undef RA +#undef CE +#undef UN +#undef DO +#undef OG +#undef CA + + string(0..255) get_der_content() + { + return [string(0..255)]replace(value, [array(string)]encode_from, + [array(string(0..255))]encode_to); + } + + this_program decode_primitive (string(0..255) contents, + function(ADT.struct, + mapping(int:program(Object)): + Object)|void decoder, + mapping(int:program(Object))|void types) { + der = contents; + + array(string) parts = + replace (contents, [array(string)]decode_from, + [array(string)]decode_to) / DEC_COMB_MARK; + value = parts[0]; + foreach (parts[1..], string part) + value += (decode_comb[part[..1]] || DEC_ERR(part[..1])) + part[2..]; + + return this; + } + +#undef ENC_ERR +#undef DEC_ERR +#undef DEC_COMB_MARK +} + +//! +int(0..1) asn1_broken_teletex_valid (string s) +{ + return global.String.width(s)==8; +} + +//! (broken) TeletexString object +//! +//! Encodes and decodes latin1, but labels it TeletexString, as is +//! common in many broken programs (e.g. Netscape 4.0X). +class BrokenTeletexString +{ + inherit String; + int tag = 20; + constant type_name = "TeletexString"; // Alias: T61String +} + +Regexp asn1_IA5_invalid_chars = Regexp ("([\200-\377])"); + +//! +int(0..1) asn1_IA5_valid (string s) +{ + if (global.String.width(s)!=8) return 0; + return !asn1_printable_invalid_chars->match (s); +} + +//! IA5 String object +//! +//! Character set: ASCII. Fixed width encoding with 1 octet per +//! character. +class IA5String +{ + inherit String; + int tag = 22; + constant type_name = "IA5STRING"; +} + +//! +class VisibleString { + inherit String; + int tag = 26; + constant type_name = "VisibleString"; +} + +//! UTCTime +//! +//! RFC 2459 4.1.2.5.1 +class UTC +{ + inherit String; + int tag = 23; + constant type_name = "UTCTime"; + + //! + this_program set_posix(int t) + { + object second = Calendar.ISO_UTC.Second(t); + + // RFC 2459 4.1.2.5.1: + // + // Where YY is greater than or equal to 50, the year shall be + // interpreted as 19YY; and + // + // Where YY is less than 50, the year shall be interpreted as 20YY. + if (second->year_no() >= 2050) + error( "Times later than 2049 not supported.\n" ); + if (second->year_no() < 1950) + error( "Times earlier than 1950 not supported.\n" ); + + value = sprintf("%02d%02d%02d%02d%02d%02dZ", + [int]second->year_no() % 100, + [int]second->month_no(), + [int]second->month_day(), + [int]second->hour_no(), + [int]second->minute_no(), + [int]second->second_no()); + return this; + } + + //! + int get_posix() + { + if( !value || sizeof(value)!=13 ) error("Data not UTC date string.\n"); + + array t = (array(int))(value[..<1]/2); + if(t[0]>49) + t[0]+=1900; + else + t[0]+=2000; + + return [int]Calendar.ISO_UTC.Second(@t)->unix_time(); + } +} + +//! +int(0..0) asn1_universal_valid (string s) +{ + return 0; s; // Return 0 since the UniversalString isn't implemented. +} + +//! Universal String object +//! +//! Character set: ISO/IEC 10646-1 (compatible with Unicode). +//! Fixed width encoding with 4 octets per character. +//! +//! @fixme +//! The encoding is very likely UCS-4, but that's not yet verified. +class UniversalString +{ + inherit OctetString; + int tag = 28; + constant type_name = "UniversalString"; + + string get_der_content() { + error( "Encoding not implemented\n" ); + } + + this_program decode_primitive (string contents, + function(ADT.struct, + mapping(int:program(Object)): + Object)|void decoder, + mapping(int:program(Object))|void types) { + error( "Decoding not implemented\n" ); contents; + } +} + +//! +int(0..1) asn1_bmp_valid (string s) +{ + return global.String.width(s)<32; +} + +//! BMP String object +//! +//! Character set: ISO/IEC 10646-1 (compatible with Unicode). +//! Fixed width encoding with 2 octets per character. +//! +//! FIXME: The encoding is very likely UCS-2, but that's not yet verified. +class BMPString +{ + inherit OctetString; + int tag = 30; + constant type_name = "BMPString"; + + string get_der_content() { + return string_to_unicode (value); + } + + this_program decode_primitive (string(0..255) contents, + function(ADT.struct, + mapping(int:program(Object)): + Object)|void decoder, + mapping(int:program(Object))|void types) { + der = contents; + value = unicode_to_string (contents); + return this; + } +} + +//! Meta-instances handle a particular explicit tag and set of types. +//! Once cloned this object works as a factory for Compound objects +//! with the cls and tag that the meta object was initialized with. +//! +//! @example +//! MetaExplicit m = MetaExplicit(1,2); +//! Compound c = m(Integer(3)); +class MetaExplicit +{ + int real_tag; + int real_cls; + + mapping(int:program(Object)) valid_types; + + class `() { + inherit Compound; + constant type_name = "EXPLICIT"; + constant constructed = 1; + + int get_tag() { return real_tag; } + int get_cls() { return real_cls; } + + Object contents; + + array(Object) `elements() { return contents ? ({ contents }) : ({}); } + void `elements=(array(Object) args) + { + if (sizeof(args) > 1) error("Invalid number of elements.\n"); + contents = sizeof(args) && args[0]; + } + + int `combined_tag() { + return get_combined_tag(); + } + + this_program init(Object o) { + contents = o; + return this; + } + + string get_der_content() { + WERROR("asn1_explicit->der: contents = '%O\n", contents); + return contents->get_der(); + } + + this_program decode_constructed_element(int i, Object e) { + if (i) + error("Unexpected index!\n"); + contents = e; + return this; + } + + this_program end_decode_constructed(int length) { + if (length != 1) + error("length != 1!\n"); + return this; + } + + mapping(int:program(Object)) element_types(int i, + mapping(int:program(Object)) types) { + if (i) + error("Unexpected index!\n"); + return valid_types || types; + } + + protected string _sprintf(int t) { + if (t != 'O') return UNDEFINED; + if ((real_cls == 2) && (real_tag <= 3)) { + // Special case for the convenience variants further below. + return sprintf("%O.TaggedType%d(%O)", + global::this, real_tag, contents); + } + return sprintf("%O(%s %d %O)", this_program, type_name, + real_tag, contents); + } + + string debug_string() { + return type_name + "[" + (int) real_tag + "]"; + } + } + + //! + protected void create(int cls, int tag, + mapping(int:program(Object))|void types) { + real_cls = cls; + real_tag = tag; + valid_types = types; + } +} + +//! Some common explicit tags for convenience. +//! +//! These are typically used to indicate which +//! of several optional fields are present. +//! +//! @example +//! Eg RFC 5915 section 3: +//! @code +//! ECPrivateKey ::= SEQUENCE { +//! version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), +//! privateKey OCTET STRING, +//! parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, +//! publicKey [1] BIT STRING OPTIONAL +//! } +//! @endcode +//! The presence of the fields @tt{parameters@} and @tt{publicKey@} above +//! are indicated with @[TaggedType0] and @[TaggedType1] respectively. +MetaExplicit TaggedType0 = MetaExplicit(2, 0); +MetaExplicit TaggedType1 = MetaExplicit(2, 1); +MetaExplicit TaggedType2 = MetaExplicit(2, 2); +MetaExplicit TaggedType3 = MetaExplicit(2, 3); + +constant meta_explicit = MetaExplicit; +constant asn1_object = Object; +constant asn1_compound = Compound; +constant asn1_string = String; +constant asn1_boolean = Boolean; +constant asn1_integer = Integer; +constant asn1_enumerated = Enumerated; +constant asn1_bit_string = BitString; +constant asn1_octet_string = OctetString; +constant asn1_null = Null; +constant asn1_identifier = Identifier; +constant asn1_utf8_string = UTF8String; +constant asn1_sequence = Sequence; +constant asn1_set = Set; +constant asn1_printable_string = PrintableString; +constant asn1_teletex_string = TeletexString; +constant asn1_broken_teletex_string = BrokenTeletexString; +constant asn1_IA5_string = IA5String; +constant asn1_utc = UTC; +constant asn1_universal_string = UniversalString; +constant asn1_bmp_string = BMPString;