Files
suaveplan/packages/security-stack/security/x509/src/asn1/encoder.test.ts
2026-05-27 23:47:53 +01:00

99 lines
3.4 KiB
TypeScript

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