| 1 | // Copyright (c) 2022, 2023 blackshirt. All rights reserved. |
| 2 | // Use of this source code is governed by a MIT License |
| 3 | // that can be found in the LICENSE file. |
| 4 | module asn1 |
| 5 | |
| 6 | struct TagAndLengthTest { |
| 7 | bytes []u8 |
| 8 | tag Tag |
| 9 | explength i64 |
| 10 | lastpos int |
| 11 | err IError |
| 12 | } |
| 13 | |
| 14 | fn test_tagandlength_handling() ! { |
| 15 | // from golang asn.1 test |
| 16 | bs := [ |
| 17 | // Context Specific should be in constructed form |
| 18 | TagAndLengthTest{[u8(0x80), 0x01], Tag{.context_specific, false, 0}, 1, 2, none}, // 0b_10_0_00000 |
| 19 | TagAndLengthTest{[u8(0xa0), 0x01], Tag{.context_specific, true, 0}, 1, 2, none}, // 0b_10_1_00000 |
| 20 | TagAndLengthTest{[u8(0x02), 0x00], Tag{.universal, false, 2}, 0, 2, none}, |
| 21 | TagAndLengthTest{[u8(0xfe), 0x00], Tag{.private, true, 30}, 0, 2, none}, |
| 22 | TagAndLengthTest{[u8(0x1f), 0x1f, 0x00], Tag{.universal, false, 31}, 0, 3, none}, // high tag form |
| 23 | TagAndLengthTest{[u8(0x1f), 0x81, 0x00, 0x01], Tag{.universal, false, 128}, 1, 4, none}, |
| 24 | // the last byte tells its length in long form |
| 25 | TagAndLengthTest{[u8(0x1f), 0x81, 0x00, 0x81], Tag{.universal, false, 128}, 1, 4, error('Length: truncated length')}, |
| 26 | TagAndLengthTest{[u8(0x1f), 0x81, 0x80, 0x01, 0x00], Tag{.universal, false, 16385}, 0, 5, error('Tag number: base 128 integer too large')}, // 1x128^2 + 0x128^1 + 1x128*0 |
| 27 | TagAndLengthTest{[u8(0x00), 0x81, 0x80], Tag{.universal, false, 0}, 128, 3, none}, |
| 28 | // need one byte length |
| 29 | TagAndLengthTest{[u8(0x00), 0x83, 0x01, 0x00], Tag{.universal, false, 0}, 2, 1, error('Length: truncated length')}, |
| 30 | // normal version above |
| 31 | TagAndLengthTest{[u8(0x00), 0x83, 0x01, 0x01, 0x01], Tag{.universal, false, 0}, 65793, 5, none}, // length = 1x256^2 + 1x256^1 + 1x256^0 |
| 32 | TagAndLengthTest{[u8(0x1f), 0x85], Tag{.universal, false, 0}, 0, 2, error('Tag: truncated base 128 integer')}, |
| 33 | TagAndLengthTest{[u8(0x1f), 0x85, 0x81], Tag{.universal, false, 0}, 0, 0, error('Tag: truncated base 128 integer')}, |
| 34 | // this last bytes tell the length is in undefinite length, 0x80 |
| 35 | TagAndLengthTest{[u8(0x30), 0x80], Tag{.universal, true, 0x10}, 0, 2, error('Length: unsupported undefinite length')}, |
| 36 | // still truncated length part |
| 37 | TagAndLengthTest{[u8(0x30), 0x81], Tag{.universal, true, 0x10}, 0, 2, error('Length: truncated length')}, |
| 38 | // still in uneeded form of length |
| 39 | TagAndLengthTest{[u8(0x30), 0x81, 0x01], Tag{.universal, true, 0x10}, 1, 3, error('Length: dont needed in long form')}, |
| 40 | // its fullfill the der requirement |
| 41 | TagAndLengthTest{[u8(0x30), 0x81, 0x80], Tag{.universal, true, 0x10}, 128, 3, none}, |
| 42 | // this tell two bytes of length contains leading spurious zero's |
| 43 | TagAndLengthTest{[u8(0xa0), 0x82, 0x00, 0xff], Tag{.context_specific, true, 0}, 255, 1, error('Length: leading zeros')}, |
| 44 | TagAndLengthTest{[u8(0xa0), 0x82, 0x01, 0xff], Tag{.context_specific, true, 0}, 511, 4, none}, |
| 45 | // Superfluous zeros in the length should be an error. |
| 46 | TagAndLengthTest{[u8(0xa0), 0x82, 0x00, 0xff], Tag{.context_specific, true, 0}, 0, 4, error('Length: leading zeros')}, //{}}, |
| 47 | // Lengths up to the maximum size of an int should work. |
| 48 | TagAndLengthTest{[u8(0xa0), 0x84, 0x7f, 0xff, 0xff, 0xff], Tag{.context_specific, true, 0}, 0x7fffffff, 6, none}, //{2, 0, 0x7fffffff, true}}, |
| 49 | // Lengths that would overflow an int should be rejected. |
| 50 | TagAndLengthTest{[u8(0xa0), 0x84, 0x80, 0x00, 0x00, 0x00], Tag{.context_specific, true, 0}, 2147483648, 6, error('Length: dont needed in long form')}, //{}}, |
| 51 | // Long length form may not be used for lengths that fit in short form. |
| 52 | TagAndLengthTest{[u8(0xa0), 0x81, 0x7f], Tag{.context_specific, true, 0}, 0, 0, error('Length: dont needed in long form')}, //{}}, |
| 53 | // Tag numbers which would overflow int32 are rejected. (The number below is 2^31.) |
| 54 | TagAndLengthTest{[u8(0x1f), 0x88, 0x80, 0x80, 0x80, 0x00, 0x00], Tag{.universal, false, 0}, 0, 0, error('Negative tag number')}, //{}}, |
| 55 | // Tag numbers that fit in an int32 are valid. (The number below is 2^31 - 1.) but its bigger than max_tag_bytes_length |
| 56 | TagAndLengthTest{[u8(0x1f), 0x87, 0xFF, 0xFF, 0xFF, 0x7F, 0x00], Tag{.universal, false, 2147483647}, 0, 7, error('Tag number: base 128 integer too large')}, |
| 57 | // Long tag number form may not be used for tags that fit in short form. |
| 58 | TagAndLengthTest{[u8(0x1f), 0x1e, 0x00], Tag{.universal, false, 0}, 0, 0, error('Tag: non-minimal tag')}, //{}}, |
| 59 | ] |
| 60 | |
| 61 | for i, c in bs { |
| 62 | // dump(i) |
| 63 | tag, pos := Tag.decode(c.bytes) or { |
| 64 | assert err == c.err |
| 65 | continue |
| 66 | } |
| 67 | assert tag == c.tag |
| 68 | length, idx := Length.decode_from_offset(c.bytes, pos) or { |
| 69 | assert err == c.err |
| 70 | continue |
| 71 | } |
| 72 | assert idx == c.lastpos |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | struct LengthPackTest { |
| 77 | value int |
| 78 | expected []u8 |
| 79 | err IError |
| 80 | } |
| 81 | |
| 82 | fn test_length_pack_and_unpack_tofrom_asn() ! { |
| 83 | edata := [ |
| 84 | LengthPackTest{0, [u8(0x00)], none}, |
| 85 | LengthPackTest{10, [u8(0x0a)], none}, |
| 86 | LengthPackTest{127, [u8(0x7f)], none}, |
| 87 | LengthPackTest{255, [u8(0x81), 0xff], none}, |
| 88 | LengthPackTest{256, [u8(0x82), 0x01, 0x00], none}, |
| 89 | LengthPackTest{383, [u8(0x82), 0x01, 127], none}, |
| 90 | LengthPackTest{257, [u8(0x82), 0x01, 0x01], none}, |
| 91 | LengthPackTest{65535, [u8(0x82), 0xff, 0xff], none}, |
| 92 | LengthPackTest{65536, [u8(0x83), 0x01, 0x00, 0x00], none}, |
| 93 | LengthPackTest{16777215, [u8(0x83), 0xff, 0xff, 0xff], none}, |
| 94 | ] |
| 95 | for i, c in edata { |
| 96 | mut dst := []u8{} |
| 97 | s := Length.new(c.value)! |
| 98 | s.encode(mut dst)! |
| 99 | assert dst == c.expected |
| 100 | |
| 101 | length, idx := Length.decode(dst)! |
| 102 | |
| 103 | assert length == c.value |
| 104 | assert idx == c.expected.len |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | struct ByteLengthTest { |
| 109 | value int |
| 110 | expected []u8 |
| 111 | } |
| 112 | |
| 113 | fn test_basic_simple_length_unpack() { |
| 114 | data := [u8(0x82), 0x01, 0x7F] |
| 115 | n, pos := Length.decode(data)! |
| 116 | |
| 117 | assert n == 383 |
| 118 | assert pos == 3 |
| 119 | |
| 120 | data2 := [u8(0x82), 0x01, 0x31] |
| 121 | n2, pos2 := Length.decode(data2)! |
| 122 | assert n2 == 305 |
| 123 | assert pos2 == 3 |
| 124 | } |
| 125 | |
| 126 | fn test_length_pack_and_append() ! { |
| 127 | bdata := [ |
| 128 | ByteLengthTest{1, [u8(1)]}, |
| 129 | ByteLengthTest{127, [u8(0x7f)]}, |
| 130 | ByteLengthTest{255, [u8(0xff)]}, |
| 131 | ByteLengthTest{256, [u8(0x01), 0x00]}, |
| 132 | ByteLengthTest{383, [u8(0x01), 127]}, |
| 133 | ByteLengthTest{257, [u8(0x01), 0x01]}, |
| 134 | ByteLengthTest{7967, [u8(0x1f), 0x1f]}, |
| 135 | ByteLengthTest{65535, [u8(0xff), 0xff]}, |
| 136 | ByteLengthTest{65537, [u8(0x01), 0x00, 0x01]}, |
| 137 | ByteLengthTest{16777215, [u8(0xff), 0xff, 0xff]}, |
| 138 | ] |
| 139 | |
| 140 | for v in bdata { |
| 141 | mut dst := []u8{} |
| 142 | |
| 143 | ln := Length.new(v.value)! |
| 144 | ln.to_bytes(mut dst) |
| 145 | |
| 146 | assert dst == v.expected |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | struct LengthTest { |
| 151 | value int |
| 152 | expected int |
| 153 | } |
| 154 | |
| 155 | fn test_length_bytes_len() ! { |
| 156 | ldata := [ |
| 157 | LengthTest{1, 1}, |
| 158 | LengthTest{128, 1}, |
| 159 | LengthTest{255, 1}, |
| 160 | LengthTest{256, 2}, |
| 161 | LengthTest{383, 2}, |
| 162 | LengthTest{65535, 2}, |
| 163 | LengthTest{65536, 3}, |
| 164 | LengthTest{16777215, 3}, |
| 165 | LengthTest{16777216, 4}, |
| 166 | LengthTest{2147483647, 4}, // math.max_i32 |
| 167 | ] |
| 168 | |
| 169 | for c in ldata { |
| 170 | len := Length.new(c.value)! |
| 171 | out := len.bytes_len() |
| 172 | |
| 173 | assert out == c.expected |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | fn test_calc_length_of_length() ! { |
| 178 | data := [ |
| 179 | LengthTest{1, 1}, |
| 180 | LengthTest{128, 2}, |
| 181 | LengthTest{255, 2}, |
| 182 | LengthTest{256, 3}, |
| 183 | LengthTest{383, 3}, |
| 184 | LengthTest{65535, 3}, |
| 185 | LengthTest{65536, 4}, |
| 186 | LengthTest{16777215, 4}, |
| 187 | LengthTest{16777216, 5}, |
| 188 | LengthTest{2147483647, 5}, // math.max_i32 |
| 189 | ] |
| 190 | |
| 191 | for c in data { |
| 192 | len := Length.new(c.value)! |
| 193 | out := len.length_size()! |
| 194 | |
| 195 | assert out == c.expected |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | // ASN.1 Test Suite from https://github.com/YuryStrozhevsky/asn1-test-suite |
| 200 | fn test_tc3_absence_standard_length_block() ! { |
| 201 | value := []u8{} |
| 202 | |
| 203 | _, _ := Length.decode(value) or { |
| 204 | assert err == error('Length: truncated length') |
| 205 | return |
| 206 | } |
| 207 | } |
| 208 | |
| 209 | fn test_tc5_unnecessary_usage_long_of_length_form() ! { |
| 210 | value := [u8(0x7f), 0xff, 0x7f, 0x81, 0x01, 0x40] |
| 211 | |
| 212 | tag, pos := Tag.decode(value)! |
| 213 | // 0x9f == 0b0111_1111 |
| 214 | assert tag.class == .application |
| 215 | assert tag.constructed == true |
| 216 | assert pos == 3 |
| 217 | // the length bytes, [0x81, 0x01] dont needed in long form. |
| 218 | _, _ := Length.decode_with_rule(value, pos, .der) or { |
| 219 | assert err == error('Length: dont needed in long form') |
| 220 | return |
| 221 | } |
| 222 | } |
| 223 | |