Format detection: parse DER without knowing the format first
all() enumerates the registered set), is a member that composes the shared engine and the shared PKIX sub-schemas (AlgorithmIdentifier, Name, Extension), so a structural rule (bounds-checked positional reads, optional / tagged field ordering, SET-OF uniqueness, fail-closed typed errors) is defined once in the engine and no format can reintroduce the class of bug it prevents.
parse is the orchestrator: hand it DER (or PEM) and it detects which format the bytes encode and routes to that member's parser. Each member is also reachable directly (pki.schema.x509.parse, pki.schema.crl.parse), and all() enumerates the registered formats.
pki.schema.all
pki.schema.all() -> string[]
The names of every registered format, in detection order.
Example
pki.schema.all(); // -> ["cms", "tsp", "crmf", "cmp", "csrattrs", "trustanchor", "ocsp-request", "ocsp-response", "pkcs12", "pkcs8", "csr", "attrcert", "attrcert-v1", "crl", "x509"]
References
- spec RFC 5280
pki.schema.parse
pki.schema.parse(input, caps?) -> parsed
Detect which PKI format input (a DER Buffer or a PEM string) encodes and route to that format's parser, returning the same structured object the format's own parse returns. Throws SchemaError("schema/unknown-format") when the bytes match no registered format; the underlying decode / structural errors of the matched format propagate unchanged.
Options
- `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
above it, or an option outside this set, is refused. They bound the detection decode
and the decode of whichever format matches, and a parse that exceeds one is refused
with the `/too-large` code of that format.
Example
async function example() {
var pair = await pki.key.generate("Ed25519");
var der = await pki.x509.sign({ subject: "example.com", subjectPublicKey: await pki.key.export(pair.publicKey),
notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") },
{ key: await pki.key.export(pair.privateKey) });
var parsed = pki.schema.parse(der); // cert -> the pki.schema.x509 shape
}
example();
References
- spec RFC 5280
pki.schema.detectFormat
pki.schema.detectFormat(input) -> string | null
Detect which registered PKI format input (a DER Buffer or PEM string) encodes and return its name, one of pki.schema.all(), without parsing it, or null when the decoded bytes match no registered format. This is the detection half of pki.schema.parse, running the same authoritative FORMATS ordering, exposed for a caller (e.g. pki.inspect.any) that needs the format name instead of the parsed result. Input that does not decode as DER throws the same coercion / decode error parse throws.
Example
async function example() {
var pair = await pki.key.generate("Ed25519");
var der = await pki.x509.sign({ subject: "example.com", subjectPublicKey: await pki.key.export(pair.publicKey),
notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") },
{ key: await pki.key.export(pair.privateKey) });
pki.schema.detectFormat(der); // "x509" | "crl" | "csr" | "cms" | ... | null
}
example();
References
- spec RFC 5280
pki.schema.pem.decodeBundle
pki.schema.pem.decodeBundle(text, opts) -> [{ index, offset, label, der }]
Read every object in a PEM file: a fullchain.pem of a leaf and its intermediates, a ca-certificates.crt of hundreds of anchors, a file holding a key beside its certificate. Each row carries the label that named the object, the DER it decoded to, its position in the file and the offset into the text its boundary started at, so a fault in a file of hundreds names the one object it is about. offset counts into the text this verb read: a BufferSource is read as latin1, so one character is one byte and the offset is the byte offset into the file, while a string a caller decoded itself is counted in that string's own characters.
Explanatory text before, between and after the blocks is text, which is what a bundle written by a real tool carries. A label the toolkit has no parser for is a row like any other: the label is data, not a filter.
This is the verb for a file of several objects. Every other door reads one: a parse door, a pemDecode, and a verb taking a certificate or a message as PEM each refuse a file holding more than one with pem/multiple-blocks, naming how many it holds.
Fail-closed on the file rather than on the object: a boundary opened and never closed, a block closed under a different label, a body carrying RFC 1421 encryption headers, a body outside the base64 alphabet, more objects than opts.maxBlocks, and objects decoding to more than opts.maxDecodedBytes are each refused with their own pem/* code. Either cap may be tightened by a caller and neither may be raised above the toolkit's own.
Options
route check each label's claim against the structure its bytes carry, and add
`format` to every row (`null` when the label names no parser). A label
whose claim the bytes do not support is `pem/label-structure-mismatch`.
maxBlocks how many objects the file may hold. Default `C.LIMITS.PEM_MAX_BLOCKS`.
maxDecodedBytes how many bytes those objects may decode to in total, across the file
rather than per object. Default `C.LIMITS.PEM_MAX_DECODED_BYTES`.
Example
async function example() {
var pair = await pki.key.generate("Ed25519");
var cert = await pki.x509.sign({ subject: "example.com", subjectPublicKey: await pki.key.export(pair.publicKey),
notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") },
{ key: await pki.key.export(pair.privateKey) });
var text = "Some Root CA\n============\n" + pki.schema.x509.pemEncode(cert, "CERTIFICATE");
var rows = pki.schema.pem.decodeBundle(text);
rows.length; // 1, the two prose lines being text
rows[0].label; // "CERTIFICATE"
rows[0].offset; // 26, where its boundary starts
pki.schema.x509.parse(rows[0].der);
}
example();
References
- spec RFC 7468
pki.schema.pem.encodeBundle
pki.schema.pem.encodeBundle(objects) -> string
Write an ordered list of { label, der } objects to one PEM text, each block wrapped at 64 characters and closed under the label it was opened with. What decodeBundle reads back is the list that went in, and writing that list again produces the same text.
A label is held to the uppercase form this toolkit writes, so a label carrying a boundary cannot put a block into the file that the caller never named. The reader is wider than the writer: it takes every label RFC 7468 sec. 3 admits, including lowercase and the empty one.
Example
async function example() {
var pair = await pki.key.generate("Ed25519");
var cert = await pki.x509.sign({ subject: "example.com", subjectPublicKey: await pki.key.export(pair.publicKey),
notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") },
{ key: await pki.key.export(pair.privateKey) });
var text = pki.schema.pem.encodeBundle([{ label: "CERTIFICATE", der: cert }, { label: "CERTIFICATE", der: cert }]);
pki.schema.pem.decodeBundle(text).length; // 2
}
example();
References
- spec RFC 7468