hare

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

decoder.ha (19166B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use bytes;
      5 use io;
      6 use math::{bit_size};
      7 use os;
      8 use strings;
      9 use time::chrono;
     10 use time::date;
     11 use types;
     12 
     13 
     14 def TAGMASK: u8 = 0x1f;
     15 def MAX_CONS_DEPTH: size = 32;
     16 
     17 // Each DER entry starts with an header that describes the content.
     18 export type head = struct {
     19 
     20 	// Tells whether the data is constructed and encapsulates multiple
     21 	// other data fields; or primitive and the value follows.
     22 	cons: bool,
     23 
     24 	// Class info
     25 	class: class,
     26 
     27 	// Tag id of the data
     28 	tagid: u32,
     29 
     30 	// Start position in stream
     31 	start: size,
     32 
     33 	// Start position of data in stream
     34 	data: size,
     35 
     36 	// End position in stream
     37 	end: size,
     38 
     39 	implicit: bool,
     40 };
     41 
     42 fn head_endpos(d: head) size = d.end;
     43 
     44 // Size of current element (header size + data size)
     45 export fn sz(d: head) size = d.end - d.start;
     46 
     47 // Size of the encoded data.
     48 export fn dsz(d: head) size = d.end - d.data;
     49 
     50 export type decoder = struct {
     51 	src: io::handle,
     52 	pos: size,
     53 	cstack: [MAX_CONS_DEPTH]head,
     54 	cstackp: size,
     55 	next: (void | head),
     56 	cur: (void | head),
     57 	unbuf: [3]u8,
     58 	unbufn: u8,
     59 	implicit: bool,
     60 };
     61 
     62 // Creates a new DER decoder that reads from 'src'. A buffered stream (see
     63 // [[bufio::]]) is recommended for efficiency, as the decoder performs mostly
     64 // short reads.
     65 //
     66 // Each entry must be read in its entirety before the next one is attended to.
     67 // The user must call [[finish]] when finished with the decoder to ensure that
     68 // the entire input was read correctly.
     69 export fn derdecoder(src: io::handle) decoder = {
     70 	return decoder {
     71 		src = src,
     72 		pos = 0,
     73 		cstackp = 0,
     74 		cur = void,
     75 		next = void,
     76 		implicit = false,
     77 		...
     78 	};
     79 };
     80 
     81 // Verifies that the entire input to the decoder was read.
     82 export fn finish(d: *decoder) (void | error) = {
     83 	if (d.cstackp != 0 || d.next is head) return invalid;
     84 	match (d.cur) {
     85 	case void =>
     86 		return;
     87 	case let h: head =>
     88 		if (h.end != d.pos) return invalid;
     89 	};
     90 };
     91 
     92 // Returns last opened cons or void if none is open.
     93 fn curcons(d: *decoder) (void | head) = {
     94 	if (d.cstackp == 0) {
     95 		return;
     96 	};
     97 	return d.cstack[d.cstackp-1];
     98 };
     99 
    100 // Peeks the header of the next data field. Fails with [[badformat]] if no data
    101 // follows.
    102 export fn peek(d: *decoder) (head | error) = {
    103 	match (trypeek(d)?) {
    104 	case io::EOF =>
    105 		return badformat;
    106 	case let h: head =>
    107 		return h;
    108 	};
    109 };
    110 
    111 // Tries to peek the header of the next data field, or returns EOF if none
    112 // exists.
    113 export fn trypeek(d: *decoder) (head | error | io::EOF) = {
    114 	if (!(d.next is void)) {
    115 		return d.next: head;
    116 	};
    117 
    118 	if (is_endofcons(d)) return io::EOF;
    119 
    120 	match (parse_header(d)?) {
    121 	case io::EOF =>
    122 		const unreaddata = d.unbufn > 0;
    123 		if (d.cstackp != 0 || unreaddata) {
    124 			return badformat;
    125 		};
    126 		return io::EOF;
    127 	case let dh: head =>
    128 		d.next = dh;
    129 		return dh;
    130 	};
    131 };
    132 
    133 // Cons is open and end is reached.
    134 fn is_endofcons(d: *decoder) bool = {
    135 	match (curcons(d)) {
    136 	case void =>
    137 		return false;
    138 	case let cur: head =>
    139 		return d.pos == head_endpos(cur);
    140 	};
    141 };
    142 
    143 // Returns the next data element, or [[badformat]] on EOF.
    144 fn next(d: *decoder) (head | error) = {
    145 	match (trynext(d)?) {
    146 	case io::EOF =>
    147 		return badformat;
    148 	case let dh: head =>
    149 		return dh;
    150 	};
    151 };
    152 
    153 fn trynext(d: *decoder) (head | error | io::EOF) = {
    154 	if (d.next is head) {
    155 		let dh = d.next: head;
    156 		d.cur = dh;
    157 		d.next = void;
    158 		dh.implicit = d.implicit;
    159 		d.implicit = false;
    160 		return dh;
    161 	};
    162 
    163 	if (is_endofcons(d)) return io::EOF;
    164 
    165 	let dh = match (parse_header(d)?) {
    166 	case io::EOF =>
    167 		return io::EOF;
    168 	case let dh: head =>
    169 		yield dh;
    170 	};
    171 
    172 	d.cur = dh;
    173 	dh.implicit = d.implicit;
    174 	d.implicit = false;
    175 	return dh;
    176 };
    177 
    178 fn parse_header(d: *decoder) (head | error | io::EOF) = {
    179 	const consend = match (curcons(d)) {
    180 	case void =>
    181 		yield types::SIZE_MAX;
    182 	case let h: head =>
    183 		yield h.end;
    184 	};
    185 
    186 	if (d.pos == consend) return invalid;
    187 
    188 	const epos = d.pos;
    189 	const id = match (tryscan_byte(d)?) {
    190 	case io::EOF =>
    191 		d.cur = void;
    192 		return io::EOF;
    193 	case let id: u8 =>
    194 		yield id;
    195 	};
    196 
    197 	const class = ((id & 0xc0) >> 6): class;
    198 
    199 	let tagid: u32 = id & TAGMASK;
    200 	if (tagid == TAGMASK) {
    201 		tagid = parse_longtag(d, consend - d.pos)?;
    202 	};
    203 	const l = parse_len(d, consend - d.pos)?;
    204 	const hl = d.pos - epos;
    205 
    206 	const end = epos + hl + l;
    207 	if (end > consend) return invalid;
    208 
    209 	return head {
    210 		class = class,
    211 		cons = ((id >> 5) & 1) == 1,
    212 		tagid = tagid,
    213 		start = epos,
    214 		data = epos + hl,
    215 		end = end,
    216 		implicit = d.implicit,
    217 		...
    218 	};
    219 };
    220 
    221 fn tryscan_byte(d: *decoder) (u8 | io::EOF | error) = {
    222 	let buf: [1]u8 = [0...];
    223 	match (io::readall(d.src, buf)?) {
    224 	case io::EOF =>
    225 		return io::EOF;
    226 	case size =>
    227 		d.pos += 1;
    228 		return buf[0];
    229 	};
    230 };
    231 
    232 fn scan_byte(d: *decoder) (u8 | error) = {
    233 	match (tryscan_byte(d)?) {
    234 	case io::EOF =>
    235 		return truncated;
    236 	case let b: u8 =>
    237 		return b;
    238 	};
    239 };
    240 
    241 // Reads data of current entry and advances pointer. Data must have been opened
    242 // using [[next]] or [[trynext]]. [[io::EOF]] is returned on end of data.
    243 fn dataread(d: *decoder, buf: []u8) (size | io::EOF | io::error) = {
    244 	let cur = match (d.cur) {
    245 	case void =>
    246 		abort("primitive must be opened with [[next]] or [[trynext]]");
    247 	case let dh: head =>
    248 		yield dh;
    249 	};
    250 
    251 	const dataleft = head_endpos(cur) - d.pos + d.unbufn;
    252 	if (dataleft == 0) {
    253 		return io::EOF;
    254 	};
    255 
    256 	let n = 0z;
    257 	if (d.unbufn > 0) {
    258 		const max = if (d.unbufn > len(buf)) len(buf): u8 else d.unbufn;
    259 		buf[..max] = d.unbuf[..max];
    260 		d.unbufn -= max;
    261 		n += max;
    262 	};
    263 
    264 	const max = if (dataleft < len(buf) - n) dataleft else len(buf) - n;
    265 
    266 	match (io::read(d.src, buf[n..n + max])?) {
    267 	case io::EOF =>
    268 		// there should be data left
    269 		return wrap_err(truncated);
    270 	case let sz: size =>
    271 		d.pos += sz;
    272 		return n + sz;
    273 	};
    274 };
    275 
    276 // unread incomplete utf8 runes.
    277 fn dataunread(d: *decoder, buf: []u8) void = {
    278 	assert(len(buf) + d.unbufn <= len(d.unbuf));
    279 
    280 	d.unbuf[d.unbufn..d.unbufn + len(buf)] = buf;
    281 	d.unbufn += len(buf): u8;
    282 };
    283 
    284 fn dataeof(d: *decoder) bool = {
    285 	match (d.cur) {
    286 	case void =>
    287 		return true;
    288 	case let h: head =>
    289 		return d.pos + d.unbufn == head_endpos(h);
    290 	};
    291 };
    292 
    293 fn parse_longtag(p: *decoder, max: size) (u32 | error) = {
    294 	// XXX: u32 too much?
    295 	let tag: u32 = 0;
    296 	let maxbits = size(u32) * 8;
    297 	let nbits = 0z;
    298 
    299 	for (let i = 0z; i < max; i += 1) {
    300 		let b = scan_byte(p)?;
    301 		const part = b & 0x7f;
    302 
    303 		nbits += if (tag == 0) bit_size(part) else 7;
    304 		if (nbits > maxbits) {
    305 			// overflows u32
    306 			return invalid;
    307 		};
    308 
    309 		tag = (tag << 7) + part;
    310 		if (tag == 0) {
    311 			// first tag part must not be 0
    312 			return invalid;
    313 		};
    314 
    315 		if ((b >> 7) == 0) {
    316 			return tag;
    317 		};
    318 	};
    319 	return invalid; // max has been reached
    320 };
    321 
    322 fn parse_len(p: *decoder, max: size) (size | error) = {
    323 	if (max == 0) return invalid;
    324 
    325 	const b = scan_byte(p)?;
    326 	if (b == 0xff) {
    327 		return invalid;
    328 	};
    329 	if (b >> 7 == 0) {
    330 		// short form
    331 		return b: size;
    332 	};
    333 
    334 	let l = 0z;
    335 	const n = b & 0x7f;
    336 	if (n == 0) {
    337 		// Indefinite encoding is not supported in DER.
    338 		return invalid;
    339 	};
    340 
    341 	if (n > size(size)) {
    342 		// would cause a size overflow
    343 		return invalid;
    344 	};
    345 
    346 	if (n + 1 > max) return invalid;
    347 
    348 	for (let i = 0z; i < n; i += 1) {
    349 		const b = scan_byte(p)?;
    350 		l = (l << 8) + b;
    351 		if (l == 0) {
    352 			// Leading zeroes means minimum number of bytes for
    353 			// length encoding has not been used.
    354 			return invalid;
    355 		};
    356 	};
    357 
    358 	if (l <= 0x7f) {
    359 		// Could've used short form.
    360 		return invalid;
    361 	};
    362 
    363 	return l;
    364 };
    365 
    366 // Expects an IMPLICIT defined data field having class 'c' and tag 'tag'.
    367 // If the requirements are met, a read call (i.e. one of the "read_" or "reader"
    368 // functions) must follow to read the actual data as stored.
    369 export fn expect_implicit(d: *decoder, c: class, tag: u32) (void | error) = {
    370 	let h = peek(d)?;
    371 	expect_tag(h, c, tag)?;
    372 	d.implicit = true;
    373 };
    374 
    375 // Opens an EXPLICIT encoded field of given class 'c' and 'tag'. The user must
    376 // call [[close_explicit]] after containing data has been read.
    377 export fn open_explicit(d: *decoder, c: class, tag: u32) (void | error) =
    378 	open_cons(d, c, tag);
    379 
    380 // Closes an EXPLICIT encoded field.
    381 export fn close_explicit(d: *decoder) (void | badformat) = close_cons(d);
    382 
    383 
    384 // Opens a constructed value of given 'class' and 'tagid'. Fails if not a
    385 // constructed value or the encoded value has an unexpected tag.
    386 fn open_cons(d: *decoder, class: class, tagid: u32) (void | error) = {
    387 	let dh = next(d)?;
    388 	if (!dh.cons) {
    389 		return invalid;
    390 	};
    391 
    392 	expect_tag(dh, class, tagid)?;
    393 
    394 	if (d.cstackp == len(d.cstack)) {
    395 		return badformat;
    396 	};
    397 
    398 	d.cstack[d.cstackp] = dh;
    399 	d.cstackp += 1;
    400 };
    401 
    402 // Closes current constructed value. badformat is returend, if not all data has
    403 // been read.
    404 fn close_cons(d: *decoder) (void | badformat) = {
    405 	if (d.implicit) {
    406 		// a datafield marked implicit has not been read
    407 		return badformat;
    408 	};
    409 
    410 	match (curcons(d)) {
    411 	case void =>
    412 		abort("No constructed value open");
    413 	case let h: head =>
    414 		if (d.pos != head_endpos(h) || d.unbufn > 0) {
    415 			// All data must have been read before closing the seq
    416 			return badformat;
    417 		};
    418 	};
    419 
    420 	d.cstackp -= 1;
    421 };
    422 
    423 // Opens a sequence. Call [[close_seq]] after reading.
    424 export fn open_seq(d: *decoder) (void | error) =
    425 	open_cons(d, class::UNIVERSAL, utag::SEQUENCE: u32)?;
    426 
    427 // Closes the current sequence. If the caller has not read all of the data
    428 // present in the encoded seqeunce, [[badformat]] is returned.
    429 export fn close_seq(d: *decoder) (void | badformat) = close_cons(d);
    430 
    431 // Opens a set. Note that sets must be ordered according to DER, but this module
    432 // does not validate this constraint. Call [[close_set]] after reading.
    433 export fn open_set(d: *decoder) (void | error) =
    434 	open_cons(d, class::UNIVERSAL, utag::SET: u32)?;
    435 
    436 // Closes the current set. If the caller has not read all of the data present in
    437 // the encoded seqeunce, [[badformat]] is returned.
    438 export fn close_set(d: *decoder) (void | badformat) = close_cons(d);
    439 
    440 fn expect_tag(h: head, class: class, tagid: u32) (void | invalid | badformat) = {
    441 	if (class == class::UNIVERSAL && (tagid == utag::SEQUENCE
    442 			|| tagid == utag::SET) && !h.cons) {
    443 		return invalid;
    444 	};
    445 
    446 	if (h.implicit) {
    447 		return;
    448 	};
    449 
    450 	if (h.class != class || h.tagid != tagid) {
    451 		return badformat;
    452 	};
    453 };
    454 
    455 fn expect_utag(dh: head, tag: utag) (void | invalid | badformat) =
    456 	expect_tag(dh, class::UNIVERSAL, tag: u32);
    457 
    458 fn read_bytes(d: *decoder, buf: []u8) (size | error) = {
    459 	match (dataread(d, buf)) {
    460 	case io::EOF =>
    461 		return 0z;
    462 	case let n: size =>
    463 		if (!dataeof(d)) {
    464 			return badformat;
    465 		};
    466 		return n;
    467 	};
    468 };
    469 
    470 fn read_nbytes(d: *decoder, buf: []u8) (size | error) = {
    471 	const n = read_bytes(d, buf)?;
    472 	if (n != len(buf)) {
    473 		return badformat;
    474 	};
    475 	return n;
    476 };
    477 
    478 // Reads a boolean value.
    479 export fn read_bool(d: *decoder) (bool | error) = {
    480 	let dh = next(d)?;
    481 	expect_utag(dh, utag::BOOLEAN)?;
    482 	if (dsz(dh) != 1) {
    483 		return invalid;
    484 	};
    485 
    486 	let b = scan_byte(d)?;
    487 
    488 	if (b != 0x00 && b != 0xff) {
    489 		return invalid;
    490 	};
    491 
    492 	return b == 0xff;
    493 };
    494 
    495 fn validate_intprefix(i: []u8) (void | error) = {
    496 	switch (len(i)) {
    497 	case 0 =>
    498 		return invalid;
    499 	case 1 =>
    500 		return;
    501 	case =>
    502 		// An int must be encoded using the minimal number of bytes
    503 		// possible as defined in X.690 s8.3.2
    504 		if ((i[0] == 0x00 && i[1] >> 7 == 0)
    505 			|| (i[0] == 0xff && i[1] >> 7 == 1)) {
    506 			return invalid;
    507 		};
    508 	};
    509 };
    510 
    511 // Reads an arbitrary-length integer into 'buf' and returns its length in bytes.
    512 // Fails if the encoded integer size exceeds the buffer size. The integer is
    513 // stored in big endian, and negative values are stored with two's compliment.
    514 // The minimum integer size is one byte.
    515 export fn read_int(d: *decoder, buf: []u8) (size | error) = {
    516 	assert(len(buf) > 0);
    517 
    518 	let dh = next(d)?;
    519 	expect_utag(dh, utag::INTEGER)?;
    520 	const n = read_bytes(d, buf)?;
    521 	validate_intprefix(buf[..n])?;
    522 	return n;
    523 };
    524 
    525 // Similar to [[read_int]], but returns [[badformat]] if the encoded value is
    526 // signed. Discards the most significant zero bytes.
    527 export fn read_uint(d: *decoder, buf: []u8) (size | error) = {
    528 	let s = read_int(d, buf)?;
    529 	if (buf[0] & 0x80 == 0x80) {
    530 		return badformat;
    531 	};
    532 	if (buf[0] == 0) {
    533 		buf[..s-1] = buf[1..s];
    534 		s -= 1;
    535 	};
    536 	return s;
    537 };
    538 
    539 fn read_ux(d: *decoder, x: u8) (u64 | error) = {
    540 	assert(x <= 8);
    541 	let b: [9]u8 = [0...];
    542 	const n = read_int(d, b[..x+1])?;
    543 
    544 	if (b[0] & 0x80 != 0) {
    545 		// sign bit is set
    546 		return invalid;
    547 	};
    548 
    549 	const s = if (b[0] == 0x00) 1u8 else 0u8;
    550 	if (n - s > x) {
    551 		return invalid;
    552 	};
    553 
    554 	let r = 0u64;
    555 	for (let i = s; i < n; i += 1) {
    556 		r <<= 8;
    557 		r += b[i];
    558 	};
    559 	return r;
    560 };
    561 
    562 // Reads an integer that is expected to fit into u8.
    563 export fn read_u8(d: *decoder) (u8 | error) = read_ux(d, 1)?: u8;
    564 
    565 // Reads an integer that is expected to fit into u16.
    566 export fn read_u16(d: *decoder) (u16 | error) = read_ux(d, 2)?: u16;
    567 
    568 // Reads an integer that is expected to fit into u32.
    569 export fn read_u32(d: *decoder) (u32 | error) = read_ux(d, 4)?: u32;
    570 
    571 // Reads an integer that is expected to fit into u64.
    572 export fn read_u64(d: *decoder) (u64 | error) = read_ux(d, 8)?;
    573 
    574 // Reads a bitstring value. The result tuple contains the bitstring and the
    575 // number of unused bits in the last byte. The [[bitstr_isset]] function may be
    576 // used to check for set bits.
    577 export fn read_bitstr(d: *decoder, buf: []u8) (([]u8, u8) | error) = {
    578 	let dh = next(d)?;
    579 	expect_utag(dh, utag::BITSTRING)?;
    580 
    581 	let unused: [1]u8 = [0...];
    582 	match (dataread(d, unused)?) {
    583 	case io::EOF =>
    584 		return invalid;
    585 	case let n: size =>
    586 		if (n != 1) {
    587 			return invalid;
    588 		};
    589 	};
    590 	const unused = unused[0];
    591 	if (unused > 7) {
    592 		return invalid;
    593 	};
    594 
    595 	const n = read_bytes(d, buf)?;
    596 	const mask = (1 << unused) - 1;
    597 	if (n > 0 && buf[n-1] & mask != 0) {
    598 		// unused bits must be zero
    599 		return invalid;
    600 	};
    601 	return (buf[..n], unused);
    602 };
    603 
    604 // Checks whether bit at 'pos' is set in given bitstring. 'pos' starts from 0,
    605 // which is the highest order bit in the first byte.
    606 export fn bitstr_isset(bitstr: ([]u8, u8), pos: size) (bool | invalid) = {
    607 	const i = pos / 8;
    608 	if (i >= len(bitstr.0)) {
    609 		return false;
    610 	};
    611 	let b = bitstr.0[i];
    612 
    613 	const j = pos - i * 8;
    614 	if (i == len(bitstr.0) - 1 && j >= (8 - bitstr.1)) {
    615 		return invalid;
    616 	};
    617 	const mask = (1 << (7 - j));
    618 	return mask & b == mask;
    619 };
    620 
    621 // Returns an [[io::reader]] for octet string data.
    622 export fn octetstrreader(d: *decoder) (bytestream | error) = {
    623 	// TODO add limit?
    624 	let dh = next(d)?;
    625 	expect_utag(dh, utag::OCTET_STRING)?;
    626 	return newbytereader(d);
    627 };
    628 
    629 // Read an octet string into 'buf', returning its length. Returns [[badformat]]
    630 // if 'buf' is too small.
    631 export fn read_octetstr(d: *decoder, buf: []u8) (size | error) = {
    632 	assert(len(buf) > 0);
    633 
    634 	let dh = next(d)?;
    635 	expect_utag(dh, utag::OCTET_STRING)?;
    636 	return read_bytes(d, buf);
    637 };
    638 
    639 // Reads a null entry.
    640 export fn read_null(d: *decoder) (void | error) = {
    641 	let dh = next(d)?;
    642 	expect_utag(dh, utag::NULL)?;
    643 	if (dsz(dh) != 0) {
    644 		return invalid;
    645 	};
    646 };
    647 
    648 export type bytestream = struct {
    649 	stream: io::stream,
    650 	d: *decoder,
    651 };
    652 
    653 fn newbytereader(d: *decoder) bytestream = {
    654 	return bytestream {
    655 		stream = &bytestream_vtable,
    656 		d = d,
    657 		...
    658 	};
    659 };
    660 
    661 const bytestream_vtable: io::vtable = io::vtable {
    662 	reader = &bytestream_reader,
    663 	...
    664 };
    665 
    666 fn bytestream_reader(s: *io::stream, buf: []u8) (size | io::EOF | io::error) =
    667 	dataread((s: *bytestream).d, buf);
    668 
    669 // Returns an [[io::reader]] that reads raw data (in its ASN.1 encoded form)
    670 // from a [[decoder]]. Note that this reader will not perform any kind of
    671 // validation.
    672 export fn bytereader(d: *decoder, c: class, tagid: u32) (bytestream | error) = {
    673 	let dh = next(d)?;
    674 	expect_tag(dh, c, tagid)?;
    675 	return newbytereader(d);
    676 };
    677 
    678 // Reads an UTC time. Since the stored date only has a two digit year, 'maxyear'
    679 // is required to define the epoch. For example 'maxyear' = 2046 causes all
    680 // encoded years <= 46 to be after 2000 and all values > 46 will have 1900 as
    681 // the century.
    682 export fn read_utctime(d: *decoder, maxyear: u16) (date::date | error) = {
    683 	assert(maxyear > 100);
    684 
    685 	let dh = next(d)?;
    686 	expect_utag(dh, utag::UTC_TIME)?;
    687 
    688 	let time: [13]u8 = [0...];
    689 	read_nbytes(d, time[..])?;
    690 
    691 	if (time[len(time)-1] != 'Z') {
    692 		return invalid;
    693 	};
    694 
    695 	let year: u16 = (time[0] - 0x30): u16 * 10 + (time[1] - 0x30): u16;
    696 	let cent = maxyear - (maxyear % 100);
    697 	if (year > maxyear % 100) {
    698 		cent -= 100;
    699 	};
    700 
    701 	let v = date::newvirtual();
    702 	v.vloc = chrono::UTC;
    703 	v.year = (year + cent): int;
    704 	v.zoff = 0;
    705 	v.nanosecond = 0;
    706 
    707 	let datestr = strings::fromutf8(time[2..])!;
    708 	if (!(date::parse(&v, "%m%d%H%M%S%Z", datestr) is void)) {
    709 		return invalid;
    710 	};
    711 
    712 	let dt = match (date::realize(v)) {
    713 	case let dt: date::date =>
    714 		yield dt;
    715 	case let e: (date::insufficient | date::invalid) =>
    716 		return invalid;
    717 	};
    718 
    719 	return dt;
    720 };
    721 
    722 // Reads a generalized datetime value.
    723 export fn read_gtime(d: *decoder) (date::date | error) = {
    724 	let dh = next(d)?;
    725 	expect_utag(dh, utag::GENERALIZED_TIME)?;
    726 
    727 	// The date begins with the encoded datetime
    728 	def DATESZ = 14z;
    729 	// followed by optional fractional seconds separated by '.'
    730 	def NANOSZ = 10z;
    731 	def NANOSEPPOS = 14;
    732 	// and ends with the zone info 'Z'
    733 	def ZONESZ = 1z;
    734 
    735 	let time: [DATESZ + NANOSZ + ZONESZ]u8 = [0...];
    736 	let n = read_bytes(d, time[..])?;
    737 
    738 	// zone info and seconds must always be present
    739 	if (time[n-1] != 'Z' || n < DATESZ + ZONESZ) {
    740 		return invalid;
    741 	};
    742 
    743 	// validate fractional seconds
    744 	if (n > DATESZ + ZONESZ) {
    745 		// fractional seconds must not be empty
    746 		if (time[NANOSEPPOS] != '.' || n == DATESZ + ZONESZ + 1) {
    747 			return invalid;
    748 		};
    749 		// fractional seconds must not end with 0 and must be > 0
    750 		if (time[n-2] == '0') return invalid;
    751 	};
    752 
    753 	// right pad fractional seconds to make them valid nanoseconds
    754 	time[n-1..] = ['0'...];
    755 	time[NANOSEPPOS] = '.';
    756 
    757 	match (date::from_str("%Y%m%d%H%M%S.%N", strings::fromutf8(time)!)) {
    758 	case let d: date::date =>
    759 		return d;
    760 	case =>
    761 		return invalid;
    762 	};
    763 };
    764 
    765 // Skips an element and returns the size of the data that has been skipped.
    766 // Returns an error if the skipped data is invalid.
    767 //
    768 // Presently only supports BOOLEAN, INTEGER, NULL, OCTET_STRING, and BITSTRING
    769 // utags, and will abort when attempting to skip anything else.
    770 export fn skip(d: *decoder, tag: utag, max: size) (size | error) = {
    771 	static let buf: [os::BUFSZ]u8 = [0...];
    772 	let s = 0z;
    773 	switch (tag) {
    774 	case utag::BOOLEAN =>
    775 		read_bool(d)?;
    776 		return 1z;
    777 	case utag::INTEGER =>
    778 		let br = bytereader(d, class::UNIVERSAL, utag::INTEGER)?;
    779 		let n = match (io::read(&br, buf)?) {
    780 		case let n: size =>
    781 			yield n;
    782 		case io::EOF =>
    783 			return invalid;
    784 		};
    785 		validate_intprefix(buf[..n])?;
    786 		n += streamskip(&br, max, buf)?;
    787 		return n;
    788 	case utag::NULL =>
    789 		read_null(d)?;
    790 		return 0z;
    791 	case utag::OCTET_STRING =>
    792 		let r = octetstrreader(d)?;
    793 		return streamskip(&r, max, buf)?;
    794 	case utag::BITSTRING =>
    795 		assert(max <= len(buf));
    796 		let buf = buf[..max];
    797 		let p = read_bitstr(d, buf)?;
    798 		bytes::zero(p.0);
    799 		return len(p.0) + 1;
    800 	case =>
    801 		abort("skip for given utag not implemented");
    802 	};
    803 };
    804 
    805 fn streamskip(r: io::handle, max: size, buf: []u8) (size | error) = {
    806 	defer bytes::zero(buf);
    807 	let buf = if (max < len(buf)) buf[..max] else buf[..];
    808 	let s = 0z;
    809 	for (true) {
    810 		match (io::read(r, buf)?) {
    811 		case let n: size =>
    812 			s += n;
    813 		case io::EOF =>
    814 			return s;
    815 		};
    816 
    817 		if (s > max) {
    818 			return badformat;
    819 		};
    820 	};
    821 };