hare

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

oid.ha (3566B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use bytes;
      5 use errors;
      6 use fmt;
      7 use io;
      8 use math::{divu};
      9 use memio;
     10 use strings;
     11 
     12 
     13 // An oid database that contains a lookup table of known oids in the DER format.
     14 // A database of oids required by the standard library can be found in
     15 // [[encoding::asn1::stdoid]].
     16 //
     17 // The database can be used with [[oid_from_der]] and [[oid_to_der]] to convert
     18 // an oid between integer and DER encoding. [[read_oid]] and [[write_oid]] can
     19 // be used to decode or encode the oid directly from and to DER.
     20 //
     21 // If the standard oid database is missing entries for the given use case, an
     22 // individual database can be generated using the genoiddb command found in
     23 // cmd/. Take a look at encoding/asn1/stdoid/db.txt for an example database
     24 // file.
     25 export type oiddb = struct {
     26 	lut: []u8,
     27 	index: []size,
     28 	names: []str,
     29 };
     30 
     31 // Numeric id of an oid which is unique within an [[oiddb]].
     32 export type oid = u32;
     33 
     34 // Reads an oid if present in 'db'. Returns [[badformat]] if the oid is unknown.
     35 export fn read_oid(d: *decoder, db: *oiddb) (oid | error) = {
     36 	let raw = read_rawoid(d)?;
     37 
     38 	match (oid_from_der(db, raw)) {
     39 	case let o: oid =>
     40 		return o;
     41 	case =>
     42 		return badformat;
     43 	};
     44 };
     45 
     46 // Reads any [[oid]] and returns the DER encoded form. The returned value is
     47 // borrowed from a static buffer.
     48 export fn read_rawoid(d: *decoder) ([]u8 | error) = {
     49 	def OIDBUFSZ: size = 64; // estimated
     50 	static let oidbuf: [OIDBUFSZ]u8 = [0...];
     51 
     52 	const dh = next(d)?;
     53 	expect_utag(dh, utag::OID)?;
     54 	if (dsz(dh) < 2) {
     55 		return invalid;
     56 	};
     57 	const n = read_bytes(d, oidbuf)?;
     58 	return oidbuf[..n];
     59 };
     60 
     61 // Writes given [[oid]] from the [[oiddb]] 'db'.
     62 export fn write_oid(e: *encoder, db: *oiddb, oid: oid) (void | overflow) = {
     63 	let doid = oid_to_der(db, oid);
     64 	write_fixedprim(e, class::UNIVERSAL, utag::OID, doid)?;
     65 };
     66 
     67 // Looks up DER encoded oid 'raw' in 'db' and returns an [[oid]] if found, or
     68 // void otheriwse.
     69 export fn oid_from_der(db: *oiddb, raw: []u8) (void | oid) = {
     70 	for (let i = 0z; i < len(db.index); i += 1) {
     71 		const off = db.index[i];
     72 		const l = db.lut[off];
     73 		if (bytes::equal(raw, db.lut[off + 1..off + 1 + l])) {
     74 			return i: oid;
     75 		};
     76 	};
     77 };
     78 
     79 // Borrows the DER representation of a known oid from 'db'.
     80 export fn oid_to_der(db: *oiddb, o: oid) []u8 = {
     81 	const off = db.index[o];
     82 	const l = db.lut[off];
     83 	return db.lut[off + 1..off + 1 + l];
     84 };
     85 
     86 // Looks up a str representation of an oid from the database.
     87 export fn stroid(db: *oiddb, o: oid) str = {
     88 	return db.names[o];
     89 };
     90 
     91 // Returns the dot id as string. The caller must free returned value. This
     92 // function may fail if the oid overflows the internal buffer, or an invalid
     93 // value is provided.
     94 export fn strrawoid(der: []u8) (str | io::error) = {
     95 	let s = memio::dynamic();
     96 	let ok = false;
     97 	defer if (!ok) io::close(&s)!;
     98 
     99 	if (len(der) < 1) {
    100 		return errors::invalid;
    101 	};
    102 
    103 	const (a, b) = divu(0, der[0], 40);
    104 	fmt::fprintf(&s, "{}.{}", a, b)?;
    105 
    106 	let j = 2z;
    107 	let el = 0u32;
    108 	let bits: int = size(u32): int * 8;
    109 
    110 	for (let i = 1z; i < len(der); i += 1) {
    111 		el += der[i] & 0x7f;
    112 
    113 		if (der[i] & 0x80 != 0) {
    114 			if (bits - 7 < 0) {
    115 				return errors::overflow;
    116 			};
    117 			el <<= 7;
    118 			bits -= 7;
    119 		} else {
    120 			fmt::fprintf(&s, ".{}", el)?;
    121 			el = 0;
    122 			j += 1;
    123 			bits = size(u32): int * 8;
    124 		};
    125 	};
    126 
    127 	ok = true;
    128 	return memio::string(&s)!;
    129 };
    130 
    131 @test fn strrawoid() void = {
    132 	let der: [_]u8 = [0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01];
    133 	let s = strrawoid(der)!;
    134 	defer free(s);
    135 	assert(s == "1.2.840.113549.1.1.1");
    136 };