hare

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

decoder.ha (19193B)


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