UNPKG

@hiero-ledger/cryptography

Version:

Cryptographic utilities and primitives for the Hiero SDK

158 lines (145 loc) 3.65 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.decode = decode; /** * @typedef {object} AsnSeq * @property {AsnType[]} seq */ /** * @typedef {object} AsnInt * @property {number} int */ /** * @typedef {object} AsnBytes * @property {Uint8Array} bytes */ /** * @typedef {object} AsnIdent * @property {string} ident */ /** * @typedef {{}} AsnNull */ /** * @typedef {AsnSeq | AsnInt | AsnBytes | AsnIdent | AsnNull} AsnType */ /** * Note: may throw weird errors on malformed input. Catch and rethrow with, e.g. `BadKeyError`. *@param {Uint8Array} data *@returns {AsnType} */ function decode(data) { return decodeIncremental(data)[0]; } /** * @param {Uint8Array} bytes * @returns {[AsnType, Uint8Array]} */ function decodeIncremental(bytes) { // slice off the initial tag byte, `decodeLength` returns a slice of the remaining data const [len, rem] = decodeLength(bytes.subarray(1)); const data = rem.subarray(0, len); const tail = rem.subarray(len); switch (bytes[0]) { case 2: return [{ int: decodeInt(data) }, tail]; case 4: // must always be primitive form in DER; for OCTET STRING this is literal bytes return [{ bytes: data }, tail]; case 5: // empty return [{}, tail]; case 6: return [{ ident: decodeObjectIdent(data) }, tail]; case 48: return [{ seq: decodeSeq(data) }, tail]; default: throw new Error(`unsupported DER type tag: ${bytes[0]}`); } } /** * @param {Uint8Array} seqBytes * @returns {AsnType[]} */ function decodeSeq(seqBytes) { let data = seqBytes; const seq = []; while (data.length !== 0) { const [decoded, remaining] = decodeIncremental(data); seq.push(decoded); data = remaining; } return seq; } /** * @param {Uint8Array} idBytes * @returns {string} */ function decodeObjectIdent(idBytes) { const id = [ // first octet is 40 * value1 + value2 Math.floor(idBytes[0] / 40), idBytes[0] % 40]; // each following ID component is big-endian base128 where the MSB is set if another byte // follows for the same value let val = 0; for (const byte of idBytes.subarray(1)) { // shift the entire value left by 7 bits val *= 128; if (byte < 128) { // no more octets follow for this value, finish it off val += byte; id.push(val); val = 0; } else { // zero the MSB val += byte & 127; } } return id.join("."); } /** * @param {Uint8Array} lenBytes * @returns {[number, Uint8Array]} */ function decodeLength(lenBytes) { if (lenBytes[0] < 128) { // definite, short form return [lenBytes[0], lenBytes.subarray(1)]; } const numBytes = lenBytes[0] - 128; const intBytes = lenBytes.subarray(1, numBytes + 1); const rem = lenBytes.subarray(numBytes + 1); return [decodeInt(intBytes), rem]; } /** * @param {Uint8Array} intBytes * @returns {number} */ function decodeInt(intBytes) { const len = intBytes.length; if (len === 1) { return intBytes[0]; } let view = new DataView(intBytes.buffer, intBytes.byteOffset, intBytes.byteLength); if (len === 2) return view.getUint16(0, false); if (len === 3) { // prefix a zero byte and we'll treat it as a 32-bit int const data = Uint8Array.of(0, ...intBytes); view = new DataView(data.buffer, data.byteOffset, data.byteLength); } if (len > 4) { // this probably means a bug in the decoding as this would mean a >4GB structure throw new Error(`unsupported DER integer length of ${len} bytes`); } return view.getUint32(0, false); }