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 };