import { describe, expect, it } from "@suaveplan/testing/runner"; import { UNIVERSAL_TAGS } from "./asn1.types.js"; import { decodeAsn1 } from "./decoder.js"; import { encodeAsn1, makeConstructed, makePrimitive } from "./encoder.js"; describe("encodeAsn1", () => { it("round-trips a primitive INTEGER", { timeout: 5000 }, () => { const bytes = new Uint8Array([0x02, 0x01, 0x05]); const back = encodeAsn1(decodeAsn1(bytes)); expect(Array.from(back)).toEqual(Array.from(bytes)); }); it("round-trips a nested SEQUENCE OF SET Name structure", { timeout: 5000, }, () => { // SEQUENCE { SET { SEQUENCE { OID 2.5.4.3, PrintableString "CN" } } } const oidBody = new Uint8Array([0x55, 0x04, 0x03]); const atv = makeConstructed("universal", UNIVERSAL_TAGS.SEQUENCE, [ makePrimitive( "universal", UNIVERSAL_TAGS.OBJECT_IDENTIFIER, oidBody ), makePrimitive( "universal", UNIVERSAL_TAGS.PRINTABLE_STRING, new Uint8Array([0x43, 0x4e]) ), ]); const rdn = makeConstructed("universal", UNIVERSAL_TAGS.SET, [atv]); const name = makeConstructed("universal", UNIVERSAL_TAGS.SEQUENCE, [ rdn, ]); const enc = encodeAsn1(name); const dec = decodeAsn1(enc); const reenc = encodeAsn1(dec); expect(Array.from(reenc)).toEqual(Array.from(enc)); }); it("encodes a long-form length envelope for >=128 byte body", { timeout: 5000, }, () => { const content = new Uint8Array(300).fill(0x42); const node = makePrimitive( "universal", UNIVERSAL_TAGS.OCTET_STRING, content ); const enc = encodeAsn1(node); // Expect leading 0x04 (OCTET_STRING), then 0x82 (long-form, 2 length bytes), then 0x01, 0x2c (300). expect(enc[0]).toBe(0x04); expect(enc[1]).toBe(0x82); expect(enc[2]).toBe(0x01); expect(enc[3]).toBe(0x2c); }); it("encodes high tag numbers via long-form tag encoding", { timeout: 5000, }, () => { const node = makePrimitive("context", 31, new Uint8Array()); const enc = encodeAsn1(node); expect(enc[0]).toBe(0x9f); expect(enc[1]).toBe(0x1f); }); it("encodes context-class constructed nodes", { timeout: 5000 }, () => { const inner = makePrimitive( "universal", UNIVERSAL_TAGS.INTEGER, new Uint8Array([0x01]) ); const node = makeConstructed("context", 0, [inner]); const enc = encodeAsn1(node); expect(enc[0]).toBe(0xa0); }); it("rejects negative tag numbers", { timeout: 5000 }, () => { const bad = makePrimitive( "universal" as const, -1 as unknown as number, new Uint8Array() ); expect(() => encodeAsn1(bad)).toThrow(); }); it("encodes a long-form tag number that requires multiple sub-bytes", { timeout: 5000, }, () => { // Tag number 200 needs two sub-id bytes (200 = 0x01 0x48 in // base-128), exercising the `last ? 0x00 : 0x80` ternary's // false branch. const bytes = encodeAsn1( makePrimitive("context", 200, new Uint8Array([0x05])) ); const back = decodeAsn1(bytes); expect(back.tag.tagNumber).toBe(200); }); });