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