v/vlib/x/encoding/asn1/length_test.v

223 lines
7.7 KiB
V

// Copyright (c) 2022, 2023 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 TagAndLengthTest {
bytes []u8
tag Tag
explength i64
lastpos int
err IError
}
fn test_tagandlength_handling() ! {
// from golang asn.1 test
bs := [
// Context Specific should be in constructed form
TagAndLengthTest{[u8(0x80), 0x01], Tag{.context_specific, false, 0}, 1, 2, none}, // 0b_10_0_00000
TagAndLengthTest{[u8(0xa0), 0x01], Tag{.context_specific, true, 0}, 1, 2, none}, // 0b_10_1_00000
TagAndLengthTest{[u8(0x02), 0x00], Tag{.universal, false, 2}, 0, 2, none},
TagAndLengthTest{[u8(0xfe), 0x00], Tag{.private, true, 30}, 0, 2, none},
TagAndLengthTest{[u8(0x1f), 0x1f, 0x00], Tag{.universal, false, 31}, 0, 3, none}, // high tag form
TagAndLengthTest{[u8(0x1f), 0x81, 0x00, 0x01], Tag{.universal, false, 128}, 1, 4, none},
// the last byte tells its length in long form
TagAndLengthTest{[u8(0x1f), 0x81, 0x00, 0x81], Tag{.universal, false, 128}, 1, 4, error('Length: truncated length')},
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
TagAndLengthTest{[u8(0x00), 0x81, 0x80], Tag{.universal, false, 0}, 128, 3, none},
// need one byte length
TagAndLengthTest{[u8(0x00), 0x83, 0x01, 0x00], Tag{.universal, false, 0}, 2, 1, error('Length: truncated length')},
// normal version above
TagAndLengthTest{[u8(0x00), 0x83, 0x01, 0x01, 0x01], Tag{.universal, false, 0}, 65793, 5, none}, // length = 1x256^2 + 1x256^1 + 1x256^0
TagAndLengthTest{[u8(0x1f), 0x85], Tag{.universal, false, 0}, 0, 2, error('Tag: truncated base 128 integer')},
TagAndLengthTest{[u8(0x1f), 0x85, 0x81], Tag{.universal, false, 0}, 0, 0, error('Tag: truncated base 128 integer')},
// this last bytes tell the length is in undefinite length, 0x80
TagAndLengthTest{[u8(0x30), 0x80], Tag{.universal, true, 0x10}, 0, 2, error('Length: unsupported undefinite length')},
// still truncated length part
TagAndLengthTest{[u8(0x30), 0x81], Tag{.universal, true, 0x10}, 0, 2, error('Length: truncated length')},
// still in uneeded form of length
TagAndLengthTest{[u8(0x30), 0x81, 0x01], Tag{.universal, true, 0x10}, 1, 3, error('Length: dont needed in long form')},
// its fullfill the der requirement
TagAndLengthTest{[u8(0x30), 0x81, 0x80], Tag{.universal, true, 0x10}, 128, 3, none},
// this tell two bytes of length contains leading spurious zero's
TagAndLengthTest{[u8(0xa0), 0x82, 0x00, 0xff], Tag{.context_specific, true, 0}, 255, 1, error('Length: leading zeros')},
TagAndLengthTest{[u8(0xa0), 0x82, 0x01, 0xff], Tag{.context_specific, true, 0}, 511, 4, none},
// Superfluous zeros in the length should be an error.
TagAndLengthTest{[u8(0xa0), 0x82, 0x00, 0xff], Tag{.context_specific, true, 0}, 0, 4, error('Length: leading zeros')}, //{}},
// Lengths up to the maximum size of an int should work.
TagAndLengthTest{[u8(0xa0), 0x84, 0x7f, 0xff, 0xff, 0xff], Tag{.context_specific, true, 0}, 0x7fffffff, 6, none}, //{2, 0, 0x7fffffff, true}},
// Lengths that would overflow an int should be rejected.
TagAndLengthTest{[u8(0xa0), 0x84, 0x80, 0x00, 0x00, 0x00], Tag{.context_specific, true, 0}, 2147483648, 6, error('Length: dont needed in long form')}, //{}},
// Long length form may not be used for lengths that fit in short form.
TagAndLengthTest{[u8(0xa0), 0x81, 0x7f], Tag{.context_specific, true, 0}, 0, 0, error('Length: dont needed in long form')}, //{}},
// Tag numbers which would overflow int32 are rejected. (The number below is 2^31.)
TagAndLengthTest{[u8(0x1f), 0x88, 0x80, 0x80, 0x80, 0x00, 0x00], Tag{.universal, false, 0}, 0, 0, error('Negative tag number')}, //{}},
// Tag numbers that fit in an int32 are valid. (The number below is 2^31 - 1.) but its bigger than max_tag_bytes_length
TagAndLengthTest{[u8(0x1f), 0x87, 0xFF, 0xFF, 0xFF, 0x7F, 0x00], Tag{.universal, false, 2147483647}, 0, 7, error('Tag number: base 128 integer too large')},
// Long tag number form may not be used for tags that fit in short form.
TagAndLengthTest{[u8(0x1f), 0x1e, 0x00], Tag{.universal, false, 0}, 0, 0, error('Tag: non-minimal tag')}, //{}},
]
for i, c in bs {
// dump(i)
tag, pos := Tag.decode(c.bytes) or {
assert err == c.err
continue
}
assert tag == c.tag
length, idx := Length.decode_from_offset(c.bytes, pos) or {
assert err == c.err
continue
}
assert idx == c.lastpos
}
}
struct LengthPackTest {
value int
expected []u8
err IError
}
fn test_length_pack_and_unpack_tofrom_asn() ! {
edata := [
LengthPackTest{0, [u8(0x00)], none},
LengthPackTest{10, [u8(0x0a)], none},
LengthPackTest{127, [u8(0x7f)], none},
LengthPackTest{255, [u8(0x81), 0xff], none},
LengthPackTest{256, [u8(0x82), 0x01, 0x00], none},
LengthPackTest{383, [u8(0x82), 0x01, 127], none},
LengthPackTest{257, [u8(0x82), 0x01, 0x01], none},
LengthPackTest{65535, [u8(0x82), 0xff, 0xff], none},
LengthPackTest{65536, [u8(0x83), 0x01, 0x00, 0x00], none},
LengthPackTest{16777215, [u8(0x83), 0xff, 0xff, 0xff], none},
]
for i, c in edata {
mut dst := []u8{}
s := Length.new(c.value)!
s.encode(mut dst)!
assert dst == c.expected
length, idx := Length.decode(dst)!
assert length == c.value
assert idx == c.expected.len
}
}
struct ByteLengthTest {
value int
expected []u8
}
fn test_basic_simple_length_unpack() {
data := [u8(0x82), 0x01, 0x7F]
n, pos := Length.decode(data)!
assert n == 383
assert pos == 3
data2 := [u8(0x82), 0x01, 0x31]
n2, pos2 := Length.decode(data2)!
assert n2 == 305
assert pos2 == 3
}
fn test_length_pack_and_append() ! {
bdata := [
ByteLengthTest{1, [u8(1)]},
ByteLengthTest{127, [u8(0x7f)]},
ByteLengthTest{255, [u8(0xff)]},
ByteLengthTest{256, [u8(0x01), 0x00]},
ByteLengthTest{383, [u8(0x01), 127]},
ByteLengthTest{257, [u8(0x01), 0x01]},
ByteLengthTest{7967, [u8(0x1f), 0x1f]},
ByteLengthTest{65535, [u8(0xff), 0xff]},
ByteLengthTest{65537, [u8(0x01), 0x00, 0x01]},
ByteLengthTest{16777215, [u8(0xff), 0xff, 0xff]},
]
for v in bdata {
mut dst := []u8{}
ln := Length.new(v.value)!
ln.to_bytes(mut dst)
assert dst == v.expected
}
}
struct LengthTest {
value int
expected int
}
fn test_length_bytes_len() ! {
ldata := [
LengthTest{1, 1},
LengthTest{128, 1},
LengthTest{255, 1},
LengthTest{256, 2},
LengthTest{383, 2},
LengthTest{65535, 2},
LengthTest{65536, 3},
LengthTest{16777215, 3},
LengthTest{16777216, 4},
LengthTest{2147483647, 4}, // math.max_i32
]
for c in ldata {
len := Length.new(c.value)!
out := len.bytes_len()
assert out == c.expected
}
}
fn test_calc_length_of_length() ! {
data := [
LengthTest{1, 1},
LengthTest{128, 2},
LengthTest{255, 2},
LengthTest{256, 3},
LengthTest{383, 3},
LengthTest{65535, 3},
LengthTest{65536, 4},
LengthTest{16777215, 4},
LengthTest{16777216, 5},
LengthTest{2147483647, 5}, // math.max_i32
]
for c in data {
len := Length.new(c.value)!
out := len.length_size()!
assert out == c.expected
}
}
// ASN.1 Test Suite from https://github.com/YuryStrozhevsky/asn1-test-suite
fn test_tc3_absence_standard_length_block() ! {
value := []u8{}
_, _ := Length.decode(value) or {
assert err == error('Length: truncated length')
return
}
}
fn test_tc5_unnecessary_usage_long_of_length_form() ! {
value := [u8(0x7f), 0xff, 0x7f, 0x81, 0x01, 0x40]
tag, pos := Tag.decode(value)!
// 0x9f == 0b0111_1111
assert tag.class == .application
assert tag.constructed == true
assert pos == 3
// the length bytes, [0x81, 0x01] dont needed in long form.
_, _ := Length.decode_with_rule(value, pos, .der) or {
assert err == error('Length: dont needed in long form')
return
}
}