hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

decoder_test.ha (9814B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use bufio;
      5 use bytes;
      6 use errors;
      7 use io;
      8 use memio;
      9 use os;
     10 use strings;
     11 use time::date;
     12 use types;
     13 
     14 
     15 // XXX: would be nice to just declare this as mem: memio::stream
     16 let mem: nullable *memio::stream = null;
     17 let rbuf: [os::BUFSZ]u8 = [0...];
     18 
     19 fn d(i: []u8) decoder = {
     20 	let buf = memio::fixed(i);
     21 	let h = match (mem) {
     22 	case null =>
     23 		let h = alloc(buf);
     24 		mem = h;
     25 		yield h;
     26 	case let m: *memio::stream =>
     27 		*m = buf;
     28 		yield m;
     29 	};
     30 	return derdecoder(h);
     31 };
     32 
     33 @fini fn freetdec() void = {
     34 	match (mem) {
     35 	case null => void;
     36 	case let m: *memio::stream =>
     37 		free(m);
     38 		mem = null;
     39 	};
     40 };
     41 
     42 @test fn parsetag() void = {
     43 	assert((next(&d([0x02, 0x01]))!).class == class::UNIVERSAL);
     44 	assert((next(&d([0x02, 0x01]))!).tagid == 0x02);
     45 	assert((next(&d([0x1e, 0x01]))!).tagid == 0x1e);
     46 	assert((next(&d([0x1f, 0x7f, 0x01]))!).tagid == 0x7f);
     47 	assert((next(&d([0x1f, 0x81, 0x00, 0x01]))!).tagid == 0x80);
     48 
     49 	assert((next(&d([0x1f, 0x8f, 0xff, 0xff, 0xff, 0x7f, 0x01]))!).tagid
     50 		== types::U32_MAX);
     51 	assert(next(&d([0x1f, 0x90, 0x80, 0x80, 0x80, 0x00, 0x01])) is invalid);
     52 };
     53 
     54 @test fn parselen() void = {
     55 	assert(dsz(next(&d([0x02, 0x1]))!) == 1);
     56 	assert(dsz(next(&d([0x02, 0x7f]))!) == 127);
     57 	assert(dsz(next(&d([0x02, 0x81, 0x80]))!) == 128);
     58 
     59 	// must use minimal amount of bytes for length encoding
     60 	assert(next(&d([0x02, 0x81, 0x01, 0x01])) is invalid);
     61 	assert(next(&d([0x02, 0x81, 0x7f])) is invalid);
     62 	assert(next(&d([0x02, 0x82, 0x00, 0xff])) is invalid);
     63 
     64 	// indefinite form is not allowed in DER
     65 	assert(next(&d([0x02, 0x80, 0x01, 0x00, 0x00])) is invalid);
     66 };
     67 
     68 @test fn emptydata() void = {
     69 	assert(read_bool(&d([])) is badformat);
     70 	assert(open_set(&d([])) is badformat);
     71 };
     72 
     73 @test fn seq() void = {
     74 	let dat: [_]u8 = [
     75 		0x30, 0x0a, // seq
     76 		0x01, 0x01, 0xff, // bool true
     77 		0x30, 0x05, // seq
     78 		0x30, 0x03, // seq
     79 		0x01, 0x01, 0x00, // bool false
     80 	];
     81 
     82 	let dc = &d(dat);
     83 	open_seq(dc)!;
     84 	assert(read_bool(dc)! == true);
     85 	open_seq(dc)!;
     86 	open_seq(dc)!;
     87 	assert(read_bool(dc)! == false);
     88 	close_seq(dc)!;
     89 	close_seq(dc)!;
     90 	close_seq(dc)!;
     91 	finish(dc)!;
     92 
     93 	let dc = &d(dat);
     94 	open_seq(dc)!;
     95 	assert(open_seq(dc) is invalid);
     96 
     97 	let dc = &d(dat);
     98 	open_seq(dc)!;
     99 	assert(close_seq(dc) is badformat);
    100 
    101 	let dat: [_]u8 = [
    102 		0x30, 0x07, // seq
    103 		0x0c, 0x05, 0x65, 0x66, 0x67, 0xc3, 0x96, // utf8 string
    104 	];
    105 
    106 	let dc = &d(dat);
    107 	open_seq(dc)!;
    108 	let r = strreader(dc, utag::UTF8_STRING)!;
    109 	let s = io::drain(&r)!;
    110 	defer free(s);
    111 	assert(bytes::equal([0x65, 0x66, 0x67, 0xc3, 0x96], s));
    112 
    113 	let dc = &d(dat);
    114 	let buf: [4]u8 = [0...];
    115 	open_seq(dc)!;
    116 	let r = strreader(dc, utag::UTF8_STRING)!;
    117 	assert(io::read(&r, buf)! == 3);
    118 	assert(close_seq(dc) is badformat);
    119 
    120 	// check unclosed
    121 	let dc = &d(dat);
    122 	open_seq(dc)!;
    123 	assert(finish(dc) is invalid);
    124 
    125 	let dc = &d(dat);
    126 	open_seq(dc)!;
    127 	let r = strreader(dc, utag::UTF8_STRING)!;
    128 	let s = io::drain(&r)!;
    129 	defer free(s);
    130 	assert(finish(dc) is invalid);
    131 };
    132 
    133 @test fn invalid_seq() void = {
    134 	let dat: [_]u8 = [
    135 		0x30, 0x03, // seq containing data of size 3
    136 		0x02, 0x03, 0x01, 0x02, 0x03, // int 0x010203 overflows seq
    137 	];
    138 
    139 	let dc = &d(dat);
    140 	open_seq(dc)!;
    141 
    142 	let buf: [3]u8 = [0...];
    143 	assert(read_int(dc, buf) is invalid);
    144 };
    145 
    146 @test fn read_implicit() void = {
    147 	let dat: [_]u8 = [
    148 		0x30, 0x06, // seq
    149 		0x85, 0x01, 0xff, // IMPLICIT bool true
    150 		0x01, 0x01, 0x00, // bool false
    151 	];
    152 
    153 	let dc = &d(dat);
    154 	open_seq(dc)!;
    155 	expect_implicit(dc, class::CONTEXT, 5)!;
    156 	assert(read_bool(dc)! == true);
    157 	assert(read_u16(dc) is badformat);
    158 };
    159 
    160 @test fn read_bool() void = {
    161 	assert(read_bool(&d([0x01, 0x01, 0xff]))!);
    162 	assert(read_bool(&d([0x01, 0x01, 0x00]))! == false);
    163 	assert(read_bool(&d([0x01, 0x02, 0x00, 0x00])) is invalid);
    164 	// X.690, ch. 11.1
    165 	assert(read_bool(&d([0x01, 0x01, 0x01])) is invalid);
    166 
    167 	// invalid class
    168 	assert(read_bool(&d([0x81, 0x01, 0x01])) is badformat);
    169 	// must be primitive
    170 	assert(read_bool(&d([0x21, 0x01, 0x01])) is invalid);
    171 	// invalid tag
    172 	assert(read_bool(&d([0x02, 0x01, 0x01])) is badformat);
    173 };
    174 
    175 @test fn read_null() void = {
    176 	read_null(&d([0x05, 0x00]))!;
    177 	read_null(&d([0x05, 0x01, 0x00])) is invalid;
    178 	read_null(&d([0x85, 0x00])) is invalid;
    179 	read_null(&d([0x01, 0x00])) is invalid;
    180 };
    181 
    182 @test fn read_int() void = {
    183 	let buf: [8]u8 = [0...];
    184 
    185 	assert(read_int(&d([0x02, 0x01, 0x01]), buf)! == 1);
    186 	assert(buf[0] == 0x01);
    187 	assert(read_int(&d([0x02, 0x01, 0x00]), buf)! == 1);
    188 	assert(buf[0] == 0x00);
    189 	assert(read_int(&d([0x02, 0x02, 0x01, 0x02]), buf)! == 2);
    190 	assert(buf[0] == 0x01);
    191 	assert(buf[1] == 0x02);
    192 
    193 	// must have at least one byte
    194 	assert(read_int(&d([0x02, 0x00]), buf) is invalid);
    195 	// non minimal
    196 	assert(read_int(&d([0x02, 0x02, 0x00, 0x01]), buf) is invalid);
    197 	assert(read_int(&d([0x02, 0x02, 0xff, 0x81]), buf) is invalid);
    198 
    199 	assert(read_u8(&d([0x02, 0x01, 0x00]))! == 0);
    200 	assert(read_u8(&d([0x02, 0x01, 0x01]))! == 1);
    201 	assert(read_u8(&d([0x02, 0x01, 0x7f]))! == 0x7f);
    202 	assert(read_u8(&d([0x02, 0x01, 0x80])) is invalid);
    203 	assert(read_u8(&d([0x02, 0x01, 0x81])) is invalid);
    204 	assert(read_u8(&d([0x02, 0x02, 0x00, 0x80]))! == 0x80);
    205 	assert(read_u8(&d([0x02, 0x02, 0x00, 0xff]))! == 0xff);
    206 
    207 	assert(read_u16(&d([0x02, 0x01, 0x00]))! == 0);
    208 	assert(read_u16(&d([0x02, 0x02, 0x0f, 0xff]))! == 0xfff);
    209 	assert(read_u16(&d([0x02, 0x03, 0x00, 0xff, 0xff]))! == 0xffff);
    210 	assert(read_u16(&d([0x02, 0x03, 0x01, 0xff, 0xff])) is invalid);
    211 	assert(read_u32(&d([0x02, 0x03, 0x00, 0xff, 0xff]))! == 0xffff);
    212 
    213 	let maxu64: [_]u8 = [
    214 		0x02, 0x09, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
    215 	];
    216 	assert(read_u64(&d(maxu64))! == 0xffffffffffffffff);
    217 	maxu64[2] = 0x01;
    218 	assert(read_u64(&d(maxu64)) is invalid);
    219 };
    220 
    221 @test fn read_bitstr() void = {
    222 	let buf: [8]u8 = [0...];
    223 	let bs = read_bitstr(&d([0x03, 0x01, 0x00]), buf)!;
    224 	assert(len(bs.0) == 0 && bs.1 == 0);
    225 	assert(bitstr_isset(bs, 0)! == false);
    226 
    227 	let bs = read_bitstr(&d([0x03, 0x02, 0x00, 0xff]), buf)!;
    228 	assert(bytes::equal(bs.0, [0xff]) && bs.1 == 0);
    229 	assert(bitstr_isset(bs, 0)!);
    230 	assert(bitstr_isset(bs, 7)!);
    231 
    232 	let bs = read_bitstr(&d([0x03, 0x03, 0x04, 0xab, 0xc0]), buf)!;
    233 	assert(bytes::equal(bs.0, [0xab, 0xc0]) && bs.1 == 4);
    234 	assert(bitstr_isset(bs, 0)!);
    235 	assert(bitstr_isset(bs, 1)! == false);
    236 	assert(bitstr_isset(bs, 8)!);
    237 	assert(bitstr_isset(bs, 9)!);
    238 	assert(!bitstr_isset(bs, 11)!);
    239 	assert(bitstr_isset(bs, 12) is invalid);
    240 
    241 	// unused bits must be zero
    242 	assert(read_bitstr(&d([0x03, 0x03, 0x04, 0xab, 0xc1]), buf) is invalid);
    243 	assert(read_bitstr(&d([0x03, 0x03, 0x07, 0xab, 0x40]), buf) is invalid);
    244 };
    245 
    246 let datbuf: [64]u8 = [0...];
    247 
    248 fn newdatetime(s: str, tag: utag) []u8 = {
    249 	let datetime = strings::toutf8(s);
    250 	let datsz = len(datetime): u8;
    251 	datbuf[..2] = [tag, datsz];
    252 	datbuf[2..2 + datsz] = datetime;
    253 	return datbuf[..2 + datsz];
    254 };
    255 
    256 @test fn read_utctime() void = {
    257 	let derdatetime = newdatetime("231030133710Z", utag::UTC_TIME);
    258 	let dt = read_utctime(&d(derdatetime), 2046)!;
    259 
    260 	let fbuf: [24]u8 = [0...];
    261 	assert(date::bsformat(fbuf, date::RFC3339, &dt)!
    262 		== "2023-10-30T13:37:10+0000");
    263 
    264 	let dt = read_utctime(&d(derdatetime), 2020)!;
    265 	assert(date::bsformat(fbuf, date::RFC3339, &dt)!
    266 		== "1923-10-30T13:37:10+0000");
    267 
    268 	let derdatetime = newdatetime("2310301337100", utag::UTC_TIME);
    269 	assert(read_utctime(&d(derdatetime), 2020) is error);
    270 
    271 	let derdatetime = newdatetime("231030133710", utag::UTC_TIME);
    272 	assert(read_utctime(&d(derdatetime), 2020) is error);
    273 
    274 	let derdatetime = newdatetime("231030133a10Z", utag::UTC_TIME);
    275 	assert(read_utctime(&d(derdatetime), 2020) is error);
    276 
    277 	let derdatetime = newdatetime("231330133710Z", utag::UTC_TIME);
    278 	assert(read_utctime(&d(derdatetime), 2020) is error);
    279 };
    280 
    281 @test fn read_gtime() void = {
    282 	let derdatetime = newdatetime("20231030133710Z", utag::GENERALIZED_TIME);
    283 
    284 	let dt = read_gtime(&d(derdatetime))!;
    285 
    286 	let fbuf: [32]u8 = [0...];
    287 	assert(date::bsformat(fbuf, date::RFC3339, &dt)!
    288 		== "2023-10-30T13:37:10+0000");
    289 
    290 	let derdatetime = newdatetime("20231030133710.1Z", utag::GENERALIZED_TIME);
    291 	let dt = read_gtime(&d(derdatetime))!;
    292 	assert(date::bsformat(fbuf, date::STAMPNANO, &dt)!
    293 		== "2023-10-30 13:37:10.100000000");
    294 
    295 	// must end with Z
    296 	let derdatetime = newdatetime("20231030133710", utag::GENERALIZED_TIME);
    297 	assert(read_gtime(&d(derdatetime)) is error);
    298 	let derdatetime = newdatetime("202310301337100", utag::GENERALIZED_TIME);
    299 	assert(read_gtime(&d(derdatetime)) is error);
    300 
    301 	// seconds must always be present
    302 	let derdatetime = newdatetime("202310301337", utag::GENERALIZED_TIME);
    303 	assert(read_gtime(&d(derdatetime)) is error);
    304 	let derdatetime = newdatetime("202310301337Z", utag::GENERALIZED_TIME);
    305 	assert(read_gtime(&d(derdatetime)) is error);
    306 
    307 	// fractional seconds must not end with 0. must be ommitted if 0
    308 	let derdatetime = newdatetime("20231030133710.", utag::GENERALIZED_TIME);
    309 	assert(read_gtime(&d(derdatetime)) is error);
    310 
    311 	let derdatetime = newdatetime("20231030133710.Z", utag::GENERALIZED_TIME);
    312 	assert(read_gtime(&d(derdatetime)) is error);
    313 
    314 	let derdatetime = newdatetime("20231030133710.0", utag::GENERALIZED_TIME);
    315 	assert(read_gtime(&d(derdatetime)) is error);
    316 
    317 	let derdatetime = newdatetime("20231030133710.0Z", utag::GENERALIZED_TIME);
    318 	assert(read_gtime(&d(derdatetime)) is error);
    319 
    320 	let derdatetime = newdatetime("20231030133710.10Z", utag::GENERALIZED_TIME);
    321 	assert(read_gtime(&d(derdatetime)) is error);
    322 
    323 	// TODO midnight is YYYYMMDD000000Z
    324 };
    325 
    326 @test fn read_oid() void = {
    327 	let db = oiddb {
    328 		lut = [0x03, 0x2b, 0x65, 0x70, 0x03, 0x55, 0x04, 0x03],
    329 		index = [0, 4],
    330 		names = ["ed25519", "id-at-commonName"],
    331 	};
    332 
    333 	assert(read_oid(&d([0x06, 0x03, 0x55, 0x04, 0x03]), &db)! == 1);
    334 	assert(stroid(&db, 1) == "id-at-commonName");
    335 
    336 	assert(bytes::equal([0x55, 0x04, 0x03],
    337 			read_rawoid(&d([0x06, 0x03, 0x55, 0x04, 0x03]))!));
    338 };