hare

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

oid.ha (3553B)


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