126 lines · 3.6 KB
/** Minimal CBOR decoder covering what WebAuthn attestation objects and COSE keys use. */
export type CborValue =
  | number
  | bigint
  | string
  | boolean
  | null
  | Uint8Array
  | CborValue[]
  | Map<CborValue, CborValue>;

const MAX_DEPTH = 16;
const MAX_ITEMS = 1024;
const INFO_DIRECT_MAX = 24;
const INFO_UINT8 = 24;
const INFO_UINT16 = 25;
const INFO_UINT32 = 26;
const INFO_UINT64 = 27;
const MAJOR_SIMPLE = 7;
const SIMPLE_FALSE = 20;
const SIMPLE_TRUE = 21;
const SIMPLE_NULL = 22;
const SIMPLE_UNDEFINED = 23;

export function decodeCbor(bytes: Uint8Array): { value: CborValue; length: number } {
  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
  let offset = 0;

  function need(count: number): void {
    if (offset + count > bytes.byteLength) throw new Error("CBOR: truncated input");
  }

  function readArgument(info: number): number {
    if (info < INFO_DIRECT_MAX) return info;
    if (info === INFO_UINT8) {
      need(1);
      return view.getUint8(offset++);
    }
    if (info === INFO_UINT16) {
      need(2);