99 lines
3.4 KiB
TypeScript
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);
|
|
});
|
|
});
|