hare

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

decoder_test.ha (9792B)


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