// Copyright (c) 2022, 2024 blackshirt. All rights reserved. // Use of this source code is governed by a MIT License // that can be found in the LICENSE file. module asn1 struct OidWriteTest { inp []int exp []u8 err IError } fn test_write_oid() ! { dt := [ OidWriteTest{[], [], error('ObjectIdentifier: bad oid int array')}, // empty arc OidWriteTest{[0], [u8(0x00)], error('ObjectIdentifier: bad oid int array')}, // only root arc OidWriteTest{[0, 0], [u8(0x00)], none}, OidWriteTest{[3, 0], [u8(0x00)], error('ObjectIdentifier: bad oid int array')}, // first arc, 3 is not allowed value OidWriteTest{[0, 40], [u8(0x00)], error('ObjectIdentifier: bad oid int array')}, // second arc, 40 is not allowed (its should <= 39) OidWriteTest{[1, 40], [u8(0x00)], error('ObjectIdentifier: bad oid int array')}, // second arc, 40 is not allowed (its should <= 39) OidWriteTest{[1, 2], [u8(0x2a)], none}, OidWriteTest{[2, 5], [u8(0x55)], none}, OidWriteTest{[1, 2, 840], [u8(0x2a), 0x86, 0x48], none}, OidWriteTest{[1, 2, 840, 113549], [u8(0x2a), 0x86, 0x48, 0x86, 0xF7, 0x0D], none}, OidWriteTest{[1, 2, 840, 113549, 1], [u8(0x2a), 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01], none}, ] for item in dt { oid := ObjectIdentifier.from_ints(item.inp) or { assert err == item.err continue } dst := oid.payload()! assert dst == item.exp } } struct BuildOidTest { inp []int out ObjectIdentifier err IError } fn test_oid_from_ints() ! { td := [ BuildOidTest{[1, 2], ObjectIdentifier{ value: [1, 2] }, none}, BuildOidTest{[1, 2, 3], ObjectIdentifier{ value: [1, 2, 3] }, none}, BuildOidTest{[1, 4, 4], ObjectIdentifier{ value: [1, 4, 4] }, none}, BuildOidTest{[1, 39, 6, 256], ObjectIdentifier{ value: [1, 39, 6, 256] }, none}, // second >= 40 when first < 2 not allowed BuildOidTest{[1, 40, 4], ObjectIdentifier{ value: [1, 40, 4] }, error('ObjectIdentifier: bad oid int array')}, // first value bigger than 2 was not allowed BuildOidTest{[4, 5, 6], ObjectIdentifier{ value: [4, 5, 6] }, error('ObjectIdentifier: bad oid int array')}, // second value >= 40 was not allowed when first < 2 BuildOidTest{[1, 40, 6], ObjectIdentifier{ value: [1, 40, 6] }, error('ObjectIdentifier: bad oid int array')}, BuildOidTest{[2, 50, 6], ObjectIdentifier{ value: [2, 50, 6] }, error('ObjectIdentifier: bad oid int array')}, BuildOidTest{[1, 4, 863123683], ObjectIdentifier{ value: [1, 4, 863123683] }, error('overflow parse_int result')}, BuildOidTest{[4, 0xab, 4], ObjectIdentifier{ value: [4, 0xab, 4] }, error('ObjectIdentifier: bad oid int array')}, BuildOidTest{[4, 0x0c, 4], ObjectIdentifier{ value: [4, 0x0c, 4] }, error('ObjectIdentifier: bad oid int array')}, BuildOidTest{[2], ObjectIdentifier{ value: [2] }, error('ObjectIdentifier: bad oid int array')}, ] for i, c in td { s := ObjectIdentifier.from_ints(c.inp) or { assert err == c.err continue } assert s == c.out } } struct OidStrTest { inp string out ObjectIdentifier err IError } fn test_oid_from_string() ! { td := [ OidStrTest{'1.2.840.113549', ObjectIdentifier{ value: [1, 2, 840, 113549] }, none}, OidStrTest{'1.3.6.1.3', ObjectIdentifier{ value: [1, 3, 6, 1, 3] }, none}, OidStrTest{'1.2', ObjectIdentifier{ value: [1, 2] }, none}, OidStrTest{'1.4.4', ObjectIdentifier{ value: [1, 4, 4] }, none}, OidStrTest{'1.4.x', ObjectIdentifier{ value: [1, 4, 4] }, error('common_parse_uint: syntax error x')}, // invalid char OidStrTest{'4.4.4', ObjectIdentifier{ value: [4, 4, 4] }, error('ObjectIdentifier: bad oid string')}, OidStrTest{'1.4.863123683', ObjectIdentifier{ value: [1, 4, 863123683] }, none}, OidStrTest{'4.ab.4', ObjectIdentifier{ value: [4, 0xab, 4] }, error('common_parse_uint: syntax error ab')}, // invalid char OidStrTest{'4.c.4', ObjectIdentifier{ value: [4, 0x0c, 4] }, error('common_parse_uint: syntax error c')}, // invalid char OidStrTest{'2', ObjectIdentifier{ value: [2] }, error('ObjectIdentifier: bad string oid length')}, ] for s in td { v := ObjectIdentifier.new(s.inp) or { assert err == s.err continue } assert v == s.out } } fn test_serialize_oid_basic() { // https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/object-identifier.html inp := [1, 0, 8571, 2, 1] exp := [u8(6), 5, 0x28, 0xC2, 0x7B, 0x02, 0x01] oid := ObjectIdentifier.from_ints(inp)! out := encode(oid)! assert out == exp } struct OidSerializeTest { inp []int exp []u8 err IError } fn test_serialize_decode_oid() { td := [ OidSerializeTest{[0, 0], [u8(0x06), 0x01, 0x00], none}, OidSerializeTest{[1, 2, 3], [u8(0x06), 0x02, 0x2a, 0x03], none}, OidSerializeTest{[1, 3, 6, 1, 3], [u8(0x06), 0x04, 0x2b, 0x06, 1, 3], none}, OidSerializeTest{[2, 999, 1234], [u8(0x06), 0x04, 0x88, 0x37, 0x89, 0x52], none}, OidSerializeTest{[2, 999, 3], [u8(0x06), 0x03, 0x88, 0x37, 0x03], none}, // Example of ITU-T X.690 // from https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-object-identifier OidSerializeTest{[1, 3, 6, 1, 4, 1, 311, 21, 20], [u8(0x06), 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x14], none}, // from rust-asn1 test data OidSerializeTest{[1, 2, 840, 113549], [u8(0x06), 0x06, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d], none}, OidSerializeTest{[1, 2, 3, 4], [u8(0x06), 0x03, 0x2a, 0x03, 0x04], none}, OidSerializeTest{[1, 2, 840, 133549, 1, 1, 5], [u8(0x06), 0x09, 0x2a, 0x86, 0x48, 0x88, 0x93, 0x2d, 0x01, 0x01, 0x05], none}, OidSerializeTest{[2, 100, 3], [u8(0x06), 0x03, 0x81, 0x34, 0x03], none}, OidSerializeTest{[1, 100, 3], [u8(0x06), 0x03, 0x81, 0x34, 0x03], error('ObjectIdentifier: bad oid int array')}, OidSerializeTest{[4, 100, 3], [u8(0x06), 0x03, 0x81, 0x34, 0x03], error('ObjectIdentifier: bad oid int array')}, ] for t in td { // dump(t.inp) oid := ObjectIdentifier.from_ints(t.inp) or { assert err == t.err continue } out := encode(oid) or { assert err == t.err continue } assert out == t.exp // dump(out) // decode back oidback, next := ObjectIdentifier.decode(out)! assert oidback.tag().tag_number() == int(TagType.oid) assert oidback == oid } } fn test_oid_encode_decode() ! { inp := '1.2.840.113549' src := ObjectIdentifier.new(inp)! out := encode(src)! exp := [u8(0x06), 0x06, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d] assert out == exp oidback, _ := ObjectIdentifier.decode(out)! assert oidback.str() == '${inp}' assert oidback.tag().tag_number() == 6 } fn test_tc21_long_format_of_oid_encoding_should_error_in_der() ! { data := [u8(0x06), 0x06, 0x80, 0x80, 0x51, 0x80, 0x80, 0x01] _, _ := ObjectIdentifier.decode(data) or { assert err == error('integer is not minimaly encoded') return } } fn test_tc22_too_big_value_oid() ! { data := [u8(0x06), 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x85, 0x03, 0x02, 0x02, 0x03] _, _ := ObjectIdentifier.decode(data) or { assert err == error('integer is not minimaly encoded') return } } // Taken from https://letsencrypt.org/id/docs/a-warm-welcome-to-asn1-and-der/ // // OID 1.2.840.113549.1.1.11 (representing sha256WithRSAEncryption) is encoded like so: // 06 09 2a 86 48 86 f7 0d 01 01 0b fn test_sha256withrsaencryption_oid() ! { oid := ObjectIdentifier.new('1.2.840.113549.1.1.11')! expected := [u8(0x06), 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b] out := encode(oid)! assert out == expected // decode back ob, _ := ObjectIdentifier.decode(out)! assert ob.equal(oid) }