diff --git a/.gitattributes b/.gitattributes index f52fd70ecd691d75de2b3725a4c8ea448846f8e5..4f158de9a4c0e0a156ce174e6100cb83ee00b608 100644 --- a/.gitattributes +++ b/.gitattributes @@ -55,6 +55,7 @@ testfont binary /lib/modules/Sql.pmod/postgres.pike foreign_ident /lib/modules/Sql.pmod/sql.pike foreign_ident /lib/modules/Sql.pmod/sql_result.pike foreign_ident +/lib/modules/Standards.pmod/ASN1.pmod/decode.pike foreign_ident /src/backend.c foreign_ident /src/builtin_functions.c foreign_ident /src/configure.in foreign_ident diff --git a/lib/modules/Standards.pmod/ASN1.pmod/Encode.pmod b/lib/modules/Standards.pmod/ASN1.pmod/Encode.pmod new file mode 100644 index 0000000000000000000000000000000000000000..9b38843ebe483287d6a0845496c1a8b4db8d12c6 --- /dev/null +++ b/lib/modules/Standards.pmod/ASN1.pmod/Encode.pmod @@ -0,0 +1,255 @@ +/* encode.pmod + * + * Encodes various asn.1 objects according to the Distinguished + * Encoding Rules (DER) */ + +#if 0 +#define WERROR werror +#else +#define WERROR(x) +#endif + +class asn1_object +{ + constant cls = 0; + constant tag = 0; + constant constructed = 0; + + int get_cls() { return cls; } + int get_tag() { return tag; } + + string to_base_128(int n) + { + /* Convert tag number to base 128 */ + array(int) digits = ({ }); + + /* Array is built in reverse order, least significant digit first */ + while(n) + { + digits += ({ (n & 0x7f) | 0x80 }); + n >>= 7; + } + digits[0] &= 0x7f; + + return sprintf("%@c", reverse(digits)); + } + + string encode_tag() + { + int tag = get_tag(); + int cls = get_cls(); + if (tag < 31) + return sprintf("%c", (cls << 6) | (constructed << 5) | tag); + + return sprintf("%c%s", (cls << 6) | (constructed << 5) | 0x1f, + to_base_128(tag) ); + } + + string encode_length(int|object len) + { + if (len < 0x80) + return sprintf("%c", len); + string s = Gmp.mpz(len)->digits(256); + if (strlen(s) >= 0x80) + throw( ({ "asn1.encode.asn1_object->encode_length: Max length exceeded.\n", + backtrace() }) ); + return sprintf("%c%s", strlen(s) | 0x80, s); + } + + string build_der(string contents) + { + string data = encode_tag() + encode_length(strlen(contents)) + contents; + // WERROR(sprintf("build_der: '%s'\n", Crypto.string_to_hex(data))); + WERROR(sprintf("build_der: '%s'\n", data)); + return data; + } +} + +class asn1_compound +{ + inherit asn1_object; + + constant constructed = 1; + array(object) elements; + + void create(object ...args) + { + elements = args; + WERROR(sprintf("asn1_compound: %O\n", elements)); + } +} + +class asn1_integer +{ + inherit asn1_object; + constant tag = 2; + + object value; + + void create(int|object n) { value = Gmp.mpz(n); } + + string der() + { + string s; + + if (value < 0) + { + object n = value + Gmp.pow(256, (- 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 build_der(s); + } +} + +class asn1_bitstring +{ + inherit asn1_object; + constant tag = 3; + + string value; + int unused = 0; + + void create(string s) { value = s; } + + string der() + { + return build_der(sprintf("%c%s", unused, value)); + } + + int set_length(int len) + { + if (len) + { + value = value[..(len + 7)/8]; + unused = (- len) % 8; + value = sprintf("%s%c", value[..strlen(value)-2], value[-1] + & ({ 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80 })[unused]); + } else { + unused = 0; + value = ""; + } + } +} + +class asn1_octet_string +{ + inherit asn1_object; + constant tag = 4; + string value; + + void create(string s) { value = s; } + + string der() + { + return build_der(value); + } +} + +class asn1_null +{ + inherit asn1_object; + constant tag = 5; + + string der() { return build_der(""); } +} + +class asn1_identifier +{ + inherit asn1_object; + constant tag = 6; + + array(int) id; + + void create(int ...args) + { + if ( (sizeof(args) < 2) + || (args[0] > 2) + || (args[1] >= ( (args[0] < 2) ? 40 : 176) )) + throw( ({ "asn1.encode.asn1_identifier->create: Invalid object identifier.\n", + backtrace() }) ); + id = args; + } + + object append(int ...args) + { + return object_program(this_object())(@id, @args); + } + + string der() + { + return build_der(sprintf("%c%@s", 40 * id[0] + id[1], + Array.map(id[2..], to_base_128))); + } +} + +class asn1_sequence +{ + inherit asn1_compound; + constant tag = 16; + + string der() + { + return build_der(`+("", @Array.map(elements, "der"))); + } +} + +class asn1_set +{ + inherit asn1_compound; + constant tag = 17; + + int compare_octet_strings(string r, string s) + { + for(int i = 0;; i++) + { + if (i == strlen(r)) + return (i = strlen(s)) ? 0 : 1; + if (i == strlen(s)) + return -1; + if (r[i] < s[i]) + return 1; + else if (r[i] > s[i]) + return -1; + } + } + + string der() + { + WERROR(sprintf("asn1_set->der: elements = '%O\n", + elements)); + WERROR(sprintf("asn1_set->der: der(elements) = '%O\n", + Array.map(elements, "der"))); + return build_der(`+("", @Array.sort_array(Array.map(elements, "der"), + compare_octet_strings))); + } +} + +class asn1_printable_string +{ + inherit asn1_octet_string; + constant tag = 19; +} + +class asn1_T61_string +{ + inherit asn1_octet_string; + constant tag = 20; +} + +class asn1_IA5_string +{ + inherit asn1_octet_string; + constant tag = 22; +} + +class asn1_utc +{ + inherit asn1_octet_string; + constant tag = 23; +} diff --git a/lib/modules/Standards.pmod/ASN1.pmod/decode.pike b/lib/modules/Standards.pmod/ASN1.pmod/decode.pike new file mode 100644 index 0000000000000000000000000000000000000000..ded17b5530e1b135b9959bd0b8461f1870e25b35 --- /dev/null +++ b/lib/modules/Standards.pmod/ASN1.pmod/decode.pike @@ -0,0 +1,117 @@ +/* decode.pike + * + * Rudimentary support for decoding ASN.1 encoded data. + * + * $Id: decode.pike,v 1.1 1997/11/30 11:36:37 nisse Exp $ + */ + +/* BER/DER decoder + * + * Values are represented as arrays ({ tag, value }). + * Tag is either an integer tag number, or a string, in case + * the tag recognized. + * + * Values are strings, integers, or arrays */ + +inherit ADT.struct; + +array get_asn1() +{ + int|string tag = get_int(1); + int len; + string contents; + mixed value; + +#ifdef SSL3_DEBUG + werror(sprintf("decoding tag %x\n", tag)); +#endif + if ( (tag & 0x1f) == 0x1f) + throw( ({ "high tag numbers is not supported\n", backtrace() }) ); + int len = get_int(1); + if (len & 0x80) + len = get_int(len & 0x7f); + +#ifdef SSL3_DEBUG + werror(sprintf("len : %d\n", len)); +#endif + contents = get_fix_string(len); +#ifdef SSL3_DEBUG + werror(sprintf("contents: %O\n", contents)); +#endif + value = contents; /* Default is no conversion */ + if (tag & 0x20) + { + object seq = object_program(this_object())(contents); + value = ({ }); + while(! seq->is_empty()) + { + array elem = seq->get_asn1(); +#ifdef SSL3_DEBUG + // werror(sprintf("elem: %O\n", elem)); +#endif + value += ({ elem }); + } + } + switch(tag & 0xdf) + { + case 1: /* Boolean */ + if (strlen(contents) != 1) + throw( ({ "SSL.asn1: Invalid boolean value.\n", backtrace() }) ); + tag = "BOOLEAN"; + value = !!contents[0]; + break; + case 2: /* Integer */ + tag = "INTEGER"; + value = Gmp.mpz(contents, 256); + if (contents[0] & 0x80) /* Negative */ + value -= Gmp.pow(256, strlen(contents)); + break; + case 3: /* Bit string */ + tag = "BIT STRING"; + break; + case 4: /* Octet string */ + tag = "OCTET STRING"; + break; + case 5: /* Null */ + if (strlen(contents)) + throw( ({ "SSL.asn1: Invalid NULL value.\n", backtrace() }) ); + tag = "NULL"; + value = 0; + break; + case 6: /* Object id */ + { + tag = "Identifier"; + if (contents[0] < 120) + value = ({ contents[0] / 40, contents[0] % 40 }); + else + value = ({ 2, contents[0] - 80 }); + int index = 1; + while(index < strlen(contents)) + { + int id = 0; + do + { + id = id << 7 | (contents[index] & 0x7f); + } while(contents[index++] & 0x80); + value += ({ id }); + } + break; + } + case 9: /* Real */ + tag = "REAL"; + break; + case 10: /* Enumerated */ + tag = "ENUMERATED"; + break; + case 16: /* Sequence */ + tag = "SEQUENCE"; + break; + case 17: /* Set */ + tag = "SET"; + break; + default: /* Keep numeric tag */ + break; + } + + return ({ tag, value }); +}