@int-loop/core 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,70 @@
1
+ import { z } from "zod";
2
+ import type { LedgerEntry } from "./schema/ledger.ts";
3
+ import type { VerificationFailure } from "./verify.ts";
4
+ export declare const ANCHOR_LEAF_TYPE = "int-loop.anchor-leaf/1";
5
+ export declare const AnchorTimestamp: z.ZodObject<{
6
+ tsa: z.ZodString;
7
+ token: z.ZodString;
8
+ gen_time: z.ZodISODateTime;
9
+ }, z.core.$strict>;
10
+ export type AnchorTimestamp = z.infer<typeof AnchorTimestamp>;
11
+ export declare const AnchorCreatedPayload: z.ZodObject<{
12
+ anchored: z.ZodObject<{
13
+ seq: z.ZodNumber;
14
+ entry_hash: z.ZodString;
15
+ }, z.core.$strict>;
16
+ merkle: z.ZodObject<{
17
+ root: z.ZodString;
18
+ leaf_index: z.ZodNumber;
19
+ tree_size: z.ZodNumber;
20
+ path: z.ZodArray<z.ZodString>;
21
+ }, z.core.$strict>;
22
+ timestamps: z.ZodArray<z.ZodObject<{
23
+ tsa: z.ZodString;
24
+ token: z.ZodString;
25
+ gen_time: z.ZodISODateTime;
26
+ }, z.core.$strict>>;
27
+ }, z.core.$strict>;
28
+ export type AnchorCreatedPayload = z.infer<typeof AnchorCreatedPayload>;
29
+ export type AnchorLeaf = {
30
+ tenant_id: string;
31
+ seq: number;
32
+ entry_hash: string;
33
+ };
34
+ export declare const AnchorLeafSchema: z.ZodObject<{
35
+ tenant_id: z.ZodString;
36
+ seq: z.ZodNumber;
37
+ entry_hash: z.ZodString;
38
+ }, z.core.$strict>;
39
+ export declare function anchorLeafHash(leaf: AnchorLeaf): Promise<Uint8Array<ArrayBuffer>>;
40
+ export declare function merkleRoot(leafHashes: readonly Uint8Array[]): Promise<Uint8Array<ArrayBuffer>>;
41
+ /** The inclusion path for leaf `index`, nearest sibling first. */
42
+ export declare function merklePath(leafHashes: readonly Uint8Array[], index: number): Promise<Uint8Array<ArrayBuffer>[]>;
43
+ /** Recomputes the root from a leaf and its path (RFC 9162 2.1.3.2), or null if the path does not fit the tree. */
44
+ export declare function rootFromPath(leafHash: Uint8Array, index: number, size: number, path: readonly Uint8Array[]): Promise<Uint8Array | null>;
45
+ /**
46
+ * The tree for one anchoring round: one leaf per tenant, in tenant order. Returns the root
47
+ * to stamp, and each tenant's anchor.created payload once the stamps are in.
48
+ */
49
+ export declare function anchorTree(leaves: readonly AnchorLeaf[]): Promise<{
50
+ root: Uint8Array<ArrayBuffer>;
51
+ payload: (tenantId: string, timestamps: AnchorTimestamp[]) => Promise<AnchorCreatedPayload>;
52
+ }>;
53
+ /** How far a TSA's clock may disagree with int-loop's before the verifier objects. */
54
+ export declare const ANCHOR_CLOCK_TOLERANCE_MS: number;
55
+ export type AnchorReport = {
56
+ anchors_checked: number;
57
+ /** The newest anchored entry and the earliest time a TSA vouches it existed. */
58
+ anchored_through: {
59
+ seq: number;
60
+ gen_time: string;
61
+ tsas: string[];
62
+ } | null;
63
+ failures: VerificationFailure[];
64
+ };
65
+ /**
66
+ * Checks every anchor.created entry: the anchored entry is in the ledger with that hash,
67
+ * its leaf leads to the stamped root, and every timestamp token verifies against `roots`
68
+ * for that root, at a time consistent with when the entries were recorded.
69
+ */
70
+ export declare function verifyAnchors(entries: readonly LedgerEntry[], roots: readonly Uint8Array[]): Promise<AnchorReport>;
package/dist/anchor.js ADDED
@@ -0,0 +1,201 @@
1
+ // Anchoring: once an hour, each tenant's latest ledger entry is timestamped by
2
+ // independent RFC 3161 timestamp authorities, so not even int-loop can rewrite
3
+ // history afterwards without it showing.
4
+ //
5
+ // One stamp covers every tenant. The heads are the leaves of a Merkle tree (RFC 9162
6
+ // hashing, domain-separated leaves and nodes); the TSAs stamp the root, and each
7
+ // tenant's anchor.created entry records its own leaf's path to that root. A tenant's
8
+ // evidence pack therefore proves its own chain head existed at the stamped time and
9
+ // reveals nothing about other tenants beyond opaque sibling hashes.
10
+ import { z } from "zod";
11
+ import { canonicalise } from "./jcs.js";
12
+ import { concatBytes, fromBase64Url, fromHex, sha256, toHex, utf8 } from "./hash.js";
13
+ import { Base64Url, Sha256Hex, TenantId, Timestamp } from "./schema/common.js";
14
+ import { verifyTimestampToken } from "./tsp.js";
15
+ export const ANCHOR_LEAF_TYPE = "int-loop.anchor-leaf/1";
16
+ export const AnchorTimestamp = z.strictObject({
17
+ /** Which TSA, e.g. "freetsa". Informational: trust comes from the token's certificate chain. */
18
+ tsa: z.string().regex(/^[a-z][a-z0-9-]{0,31}$/),
19
+ /** The DER TimeStampToken. */
20
+ token: Base64Url,
21
+ /** The time inside the token, repeated for readers; the verifier checks they agree. */
22
+ gen_time: Timestamp,
23
+ });
24
+ export const AnchorCreatedPayload = z.strictObject({
25
+ anchored: z.strictObject({ seq: z.number().int().positive(), entry_hash: Sha256Hex }),
26
+ merkle: z.strictObject({
27
+ root: Sha256Hex,
28
+ leaf_index: z.number().int().nonnegative(),
29
+ tree_size: z.number().int().positive(),
30
+ path: z.array(Sha256Hex).max(64),
31
+ }),
32
+ timestamps: z.array(AnchorTimestamp).min(1).max(8),
33
+ });
34
+ export const AnchorLeafSchema = z.strictObject({ tenant_id: TenantId, seq: z.number().int().positive(), entry_hash: Sha256Hex });
35
+ // ---- Merkle tree (RFC 9162 section 2.1) ----------------------------------------------
36
+ export async function anchorLeafHash(leaf) {
37
+ const body = utf8(canonicalise({ type: ANCHOR_LEAF_TYPE, tenant_id: leaf.tenant_id, seq: leaf.seq, entry_hash: leaf.entry_hash }));
38
+ return sha256(concatBytes(Uint8Array.of(0), body));
39
+ }
40
+ const node = (left, right) => sha256(concatBytes(Uint8Array.of(1), left, right));
41
+ /** The largest power of two smaller than n (n > 1). */
42
+ const split = (n) => 2 ** Math.floor(Math.log2(n - 1));
43
+ export async function merkleRoot(leafHashes) {
44
+ if (leafHashes.length === 0)
45
+ throw new Error("a Merkle tree needs at least one leaf");
46
+ if (leafHashes.length === 1)
47
+ return new Uint8Array(leafHashes[0]);
48
+ const k = split(leafHashes.length);
49
+ return node(await merkleRoot(leafHashes.slice(0, k)), await merkleRoot(leafHashes.slice(k)));
50
+ }
51
+ /** The inclusion path for leaf `index`, nearest sibling first. */
52
+ export async function merklePath(leafHashes, index) {
53
+ if (index < 0 || index >= leafHashes.length)
54
+ throw new Error("leaf index out of range");
55
+ if (leafHashes.length === 1)
56
+ return [];
57
+ const k = split(leafHashes.length);
58
+ return index < k
59
+ ? [...(await merklePath(leafHashes.slice(0, k), index)), await merkleRoot(leafHashes.slice(k))]
60
+ : [...(await merklePath(leafHashes.slice(k), index - k)), await merkleRoot(leafHashes.slice(0, k))];
61
+ }
62
+ /** Recomputes the root from a leaf and its path (RFC 9162 2.1.3.2), or null if the path does not fit the tree. */
63
+ export async function rootFromPath(leafHash, index, size, path) {
64
+ if (index >= size)
65
+ return null;
66
+ let fn = index;
67
+ let sn = size - 1;
68
+ let r = leafHash;
69
+ for (const p of path) {
70
+ if (sn === 0)
71
+ return null;
72
+ if (fn % 2 === 1 || fn === sn) {
73
+ r = await node(p, r);
74
+ while (fn % 2 === 0 && fn !== 0) {
75
+ fn = Math.floor(fn / 2);
76
+ sn = Math.floor(sn / 2);
77
+ }
78
+ }
79
+ else {
80
+ r = await node(r, p);
81
+ }
82
+ fn = Math.floor(fn / 2);
83
+ sn = Math.floor(sn / 2);
84
+ }
85
+ return sn === 0 ? r : null;
86
+ }
87
+ /**
88
+ * The tree for one anchoring round: one leaf per tenant, in tenant order. Returns the root
89
+ * to stamp, and each tenant's anchor.created payload once the stamps are in.
90
+ */
91
+ export async function anchorTree(leaves) {
92
+ const sorted = [...leaves].map((l) => AnchorLeafSchema.parse(l)).sort((a, b) => (a.tenant_id < b.tenant_id ? -1 : a.tenant_id > b.tenant_id ? 1 : 0));
93
+ for (let i = 1; i < sorted.length; i++)
94
+ if (sorted[i].tenant_id === sorted[i - 1].tenant_id)
95
+ throw new Error(`two leaves for ${sorted[i].tenant_id}`);
96
+ const hashes = await Promise.all(sorted.map((l) => anchorLeafHash(l)));
97
+ const root = await merkleRoot(hashes);
98
+ return {
99
+ root,
100
+ payload: async (tenantId, timestamps) => {
101
+ const index = sorted.findIndex((l) => l.tenant_id === tenantId);
102
+ if (index < 0)
103
+ throw new Error(`no leaf for ${tenantId}`);
104
+ const leaf = sorted[index];
105
+ return AnchorCreatedPayload.parse({
106
+ anchored: { seq: leaf.seq, entry_hash: leaf.entry_hash },
107
+ merkle: { root: toHex(root), leaf_index: index, tree_size: sorted.length, path: (await merklePath(hashes, index)).map(toHex) },
108
+ timestamps,
109
+ });
110
+ },
111
+ };
112
+ }
113
+ // ---- Verification -----------------------------------------------------------------------
114
+ /** How far a TSA's clock may disagree with int-loop's before the verifier objects. */
115
+ export const ANCHOR_CLOCK_TOLERANCE_MS = 5 * 60_000;
116
+ /**
117
+ * Checks every anchor.created entry: the anchored entry is in the ledger with that hash,
118
+ * its leaf leads to the stamped root, and every timestamp token verifies against `roots`
119
+ * for that root, at a time consistent with when the entries were recorded.
120
+ */
121
+ export async function verifyAnchors(entries, roots) {
122
+ const failures = [];
123
+ const bySeq = new Map(entries.map((e) => [e.seq, e]));
124
+ let anchors_checked = 0;
125
+ let anchored_through = null;
126
+ for (const entry of entries) {
127
+ if (entry.type !== "anchor.created")
128
+ continue;
129
+ anchors_checked++;
130
+ const fail = (check, message) => failures.push({ seq: entry.seq, check, message });
131
+ try {
132
+ const parsed = AnchorCreatedPayload.safeParse(entry.payload);
133
+ if (!parsed.success) {
134
+ fail("anchor", parsed.error.issues.map((i) => i.message).join("; "));
135
+ continue;
136
+ }
137
+ const p = parsed.data;
138
+ const anchored = bySeq.get(p.anchored.seq);
139
+ if (p.anchored.seq >= entry.seq) {
140
+ fail("anchor", "an anchor can only cover entries recorded before it");
141
+ continue;
142
+ }
143
+ if (!anchored) {
144
+ fail("anchor", `anchored entry ${p.anchored.seq} is not in this ledger`);
145
+ continue;
146
+ }
147
+ if (anchored.entry_hash !== p.anchored.entry_hash) {
148
+ fail("anchor", `anchored hash does not match entry ${p.anchored.seq}`);
149
+ continue;
150
+ }
151
+ const leaf = await anchorLeafHash({ tenant_id: entry.tenant_id, seq: p.anchored.seq, entry_hash: p.anchored.entry_hash });
152
+ const root = await rootFromPath(leaf, p.merkle.leaf_index, p.merkle.tree_size, p.merkle.path.map(fromHex));
153
+ if (root === null || toHex(root) !== p.merkle.root) {
154
+ fail("anchor", "the anchored entry is not in the stamped Merkle tree");
155
+ continue;
156
+ }
157
+ // Labels are the recorder's word; independence is judged by the root each token chains to.
158
+ const tsas = new Set();
159
+ const rootsUsed = new Map();
160
+ const verified = [];
161
+ let earliest = null;
162
+ for (const ts of p.timestamps) {
163
+ const problems = [];
164
+ if (tsas.has(ts.tsa))
165
+ problems.push(`two timestamps from ${ts.tsa}`);
166
+ tsas.add(ts.tsa);
167
+ const result = await verifyTimestampToken(fromBase64Url(ts.token), { digest: fromHex(p.merkle.root), roots });
168
+ if (!result.ok) {
169
+ fail("timestamp", `${ts.tsa}: ${result.reason}`);
170
+ continue;
171
+ }
172
+ const at = Date.parse(result.gen_time);
173
+ if (at !== Date.parse(ts.gen_time))
174
+ problems.push(`recorded time ${ts.gen_time} differs from the token's ${result.gen_time}`);
175
+ if (at < Date.parse(anchored.recorded_at) - ANCHOR_CLOCK_TOLERANCE_MS)
176
+ problems.push(`stamped before entry ${anchored.seq} was recorded`);
177
+ if (at > Date.parse(entry.recorded_at) + ANCHOR_CLOCK_TOLERANCE_MS)
178
+ problems.push("stamped after the anchor itself was recorded");
179
+ const sameRoot = rootsUsed.get(result.root_sha256);
180
+ if (sameRoot !== undefined)
181
+ problems.push(`chains to the same root as ${sameRoot}, so it is not an independent timestamp`);
182
+ rootsUsed.set(result.root_sha256, ts.tsa);
183
+ // Only a timestamp with nothing wrong counts towards what the anchor proves.
184
+ if (problems.length) {
185
+ for (const m of problems)
186
+ fail("timestamp", `${ts.tsa}: ${m}`);
187
+ continue;
188
+ }
189
+ verified.push(ts.tsa);
190
+ earliest = earliest === null ? at : Math.min(earliest, at);
191
+ }
192
+ if (earliest !== null && (anchored_through === null || p.anchored.seq >= anchored_through.seq)) {
193
+ anchored_through = { seq: p.anchored.seq, gen_time: new Date(earliest).toISOString(), tsas: verified };
194
+ }
195
+ }
196
+ catch (err) {
197
+ fail("internal", err instanceof Error ? err.message : String(err));
198
+ }
199
+ }
200
+ return { anchors_checked, anchored_through, failures };
201
+ }
@@ -0,0 +1,12 @@
1
+ import type { Fact } from "./schema/request.ts";
2
+ export type ComprehensionQuestion = {
3
+ question_id: string;
4
+ fact_key: string;
5
+ prompt: string;
6
+ /** Formatted values, in display order. Exactly one is correct. */
7
+ choices: string[];
8
+ answer: string;
9
+ };
10
+ /** Facts a question can be built from: values with plausible wrong answers. */
11
+ export declare function comprehensionFacts(facts: readonly Fact[]): Fact[];
12
+ export declare function comprehensionQuestion(facts: readonly Fact[], seed: string, format: (f: Fact) => string): Promise<ComprehensionQuestion | null>;
@@ -0,0 +1,81 @@
1
+ // L4 comprehension questions. Built deterministically from one structured fact, never
2
+ // from the narrative and never by a model (hard rule 8): "What does the request say
3
+ // for Estimated monthly cost?" with the real value and distractors derived from it.
4
+ //
5
+ // Pure. The seed (request, version, approver) chooses the fact and the order of the
6
+ // choices, so the same inputs always give the same question and anyone holding the
7
+ // facts can rebuild it. Display formatting is passed in, so the choices read exactly
8
+ // as the facts do on the page.
9
+ import { hashCanonical } from "./hash.js";
10
+ const MAX_DISTRACTORS = 3;
11
+ /** Facts a question can be built from: values with plausible wrong answers. */
12
+ export function comprehensionFacts(facts) {
13
+ return facts.filter((f) => f.type === "money" || f.type === "count" || f.type === "date" || (f.type === "enum" && (f.choices ?? []).some((c) => c !== f.value)));
14
+ }
15
+ export async function comprehensionQuestion(facts, seed, format) {
16
+ const eligible = comprehensionFacts(facts);
17
+ if (eligible.length === 0)
18
+ return null;
19
+ const bytes = await hashCanonical({ purpose: "int-loop comprehension", seed });
20
+ // Start at the seeded fact; if formatting leaves it no distinct wrong answers, use the next one.
21
+ for (let k = 0; k < eligible.length; k++) {
22
+ const fact = eligible[(bytes[0] + k) % eligible.length];
23
+ const answer = format(fact);
24
+ const seen = new Set([answer]);
25
+ const distractors = [];
26
+ for (const candidate of variants(fact)) {
27
+ const text = format(candidate);
28
+ if (seen.has(text))
29
+ continue;
30
+ seen.add(text);
31
+ distractors.push(text);
32
+ if (distractors.length === MAX_DISTRACTORS)
33
+ break;
34
+ }
35
+ if (distractors.length > 0)
36
+ return build(fact, answer, distractors, bytes);
37
+ }
38
+ return null;
39
+ }
40
+ function build(fact, answer, distractors, bytes) {
41
+ // Deterministic shuffle (Fisher-Yates driven by the seed's bytes).
42
+ const choices = [answer, ...distractors];
43
+ for (let i = choices.length - 1; i > 0; i--) {
44
+ const j = bytes[1 + i] % (i + 1);
45
+ [choices[i], choices[j]] = [choices[j], choices[i]];
46
+ }
47
+ return {
48
+ question_id: `${fact.key}#${Array.from(bytes.slice(0, 4), (b) => b.toString(16).padStart(2, "0")).join("")}`,
49
+ fact_key: fact.key,
50
+ prompt: `What does the request say for “${fact.label}”?`,
51
+ choices,
52
+ answer,
53
+ };
54
+ }
55
+ /** Wrong values of the same type, nearest-looking first. */
56
+ function variants(f) {
57
+ switch (f.type) {
58
+ case "money": {
59
+ const v = f.value.amount_minor;
60
+ const amounts = v === 0 ? [100, 1_000, 10_000] : [v * 10, Math.round(v / 10), v * 2, Math.round(v / 2), v * 3];
61
+ return amounts.filter((a) => a !== v && a > 0 && Number.isSafeInteger(a)).map((a) => ({ ...f, value: { ...f.value, amount_minor: a } }));
62
+ }
63
+ case "count": {
64
+ const v = f.value;
65
+ const counts = v === 0 ? [1, 10, 100] : [v * 10, Math.floor(v / 10), v * 2, v + 1, v - 1, v * 3];
66
+ return counts.filter((c) => c !== v && c >= 0 && Number.isSafeInteger(c)).map((c) => ({ ...f, value: c }));
67
+ }
68
+ case "date": {
69
+ const dateOnly = /^\d{4}-\d{2}-\d{2}$/.test(f.value);
70
+ const base = Date.parse(dateOnly ? `${f.value}T00:00:00Z` : f.value);
71
+ return [7, -7, 30, -30, 365].map((days) => {
72
+ const iso = new Date(base + days * 86_400_000).toISOString();
73
+ return { ...f, value: dateOnly ? iso.slice(0, 10) : iso.replace(".000Z", "Z") };
74
+ });
75
+ }
76
+ case "enum":
77
+ return (f.choices ?? []).filter((c) => c !== f.value).map((c) => ({ ...f, value: c }));
78
+ default:
79
+ return [];
80
+ }
81
+ }
@@ -0,0 +1,9 @@
1
+ export type Control = {
2
+ id: string;
3
+ name: string;
4
+ implemented_by: string;
5
+ phase: 1 | 2 | 3;
6
+ retired?: true;
7
+ };
8
+ export declare const CONTROLS: readonly Control[];
9
+ export declare const CONTROL_IDS: ReadonlySet<string>;
@@ -0,0 +1,19 @@
1
+ // The control catalogue. IDs are permanent: a retired control keeps its ID and
2
+ // gains `retired`. Packs may only reference controls listed here. Lives in core so
3
+ // evidence reports can be rebuilt offline (DECISIONS.md, 2026-11-10).
4
+ export const CONTROLS = [
5
+ { id: "CTL-OVERRIDE", name: "Every request can be denied, and approvers can attach conditions", implemented_by: "Request validation (exactly one deny), approve_with_conditions", phase: 1 },
6
+ { id: "CTL-MORE-INFO", name: "Approvers can ask for more information before deciding", implemented_by: "more_info flow", phase: 1 },
7
+ { id: "CTL-FACTS-FIRST", name: "Structured facts shown before the AI-written narrative, which is labelled", implemented_by: "Approval page", phase: 1 },
8
+ { id: "CTL-ATTENTION", name: "Time spent and context viewed are recorded; rubber-stamp patterns flagged", implemented_by: "Evidence capture; oversight reports (Phase 2)", phase: 1 },
9
+ { id: "CTL-RATIONALE", name: "Written rationale captured and bound to the decision", implemented_by: "L3", phase: 1 },
10
+ { id: "CTL-COMPREHENSION", name: "Deterministic comprehension question answered", implemented_by: "L4", phase: 2 },
11
+ { id: "CTL-IDENTITY", name: "Decision bound to a passkey held by a named, enrolled person", implemented_by: "L2+ binding", phase: 1 },
12
+ { id: "CTL-QUORUM-2", name: "At least two distinct qualified people decide", implemented_by: "Policy quorum, L4", phase: 1 },
13
+ { id: "CTL-SOD", name: "The agent's owner cannot approve its requests", implemented_by: "Policy engine", phase: 1 },
14
+ { id: "CTL-ATTR-REQUIRED", name: "Only approvers with the required authority and current training can decide", implemented_by: "Directory attributes, stage requires", phase: 1 },
15
+ { id: "CTL-LEDGER", name: "Tamper-evident record anyone can verify offline", implemented_by: "Ledger, int-loop-verify", phase: 1 },
16
+ { id: "CTL-RETENTION", name: "Records kept for at least the strictest active floor", implemented_by: "Retention rule", phase: 1 },
17
+ { id: "CTL-CONTEST", name: "People affected by a decision can ask for a human to review it", implemented_by: "Contest flow", phase: 3 },
18
+ ];
19
+ export const CONTROL_IDS = new Set(CONTROLS.map((c) => c.id));
package/dist/der.d.ts ADDED
@@ -0,0 +1,53 @@
1
+ export declare const TAG: {
2
+ readonly BOOLEAN: 1;
3
+ readonly INTEGER: 2;
4
+ readonly BIT_STRING: 3;
5
+ readonly OCTET_STRING: 4;
6
+ readonly NULL: 5;
7
+ readonly OID: 6;
8
+ readonly UTF8_STRING: 12;
9
+ readonly PRINTABLE_STRING: 19;
10
+ readonly IA5_STRING: 22;
11
+ readonly UTC_TIME: 23;
12
+ readonly GENERALIZED_TIME: 24;
13
+ readonly SEQUENCE: 48;
14
+ readonly SET: 49;
15
+ };
16
+ /** One element. `tag` is the whole identifier octet (class, constructed bit and number). */
17
+ export type Der = {
18
+ tag: number;
19
+ /** The complete encoding, header included: what a signature or hash covers. */
20
+ raw: Uint8Array<ArrayBuffer>;
21
+ /** The contents, without the header. */
22
+ content: Uint8Array<ArrayBuffer>;
23
+ };
24
+ export declare const contextTag: (n: number, constructed?: boolean) => number;
25
+ /** Reads exactly one element filling `bytes`. */
26
+ export declare function readDer(bytes: Uint8Array): Der;
27
+ /** The elements inside a constructed element (a SEQUENCE, SET or explicit tag). */
28
+ export declare function children(el: Der): Der[];
29
+ export declare function expectTag(el: Der | undefined, tag: number, what: string): Der;
30
+ /** The elements of a SEQUENCE (or SET), checking its tag. */
31
+ export declare function seq(el: Der | undefined, what: string, tag?: number): Der[];
32
+ export declare function readOid(el: Der | undefined, what?: string): string;
33
+ /** An INTEGER's two's-complement bytes, checked for minimal encoding. */
34
+ export declare function readIntegerBytes(el: Der | undefined, what?: string): Uint8Array<ArrayBuffer>;
35
+ /** A small non-negative INTEGER (a version number, a status). */
36
+ export declare function readSmallInt(el: Der | undefined, what?: string): number;
37
+ export declare function readBoolean(el: Der | undefined, what?: string): boolean;
38
+ export declare function readOctetString(el: Der | undefined, what?: string): Uint8Array<ArrayBuffer>;
39
+ /** A BIT STRING with no unused bits (keys and signatures always are). */
40
+ export declare function readBitStringBytes(el: Der | undefined, what?: string): Uint8Array<ArrayBuffer>;
41
+ /** UTCTime or GeneralizedTime, to milliseconds since the epoch. Only the Z (UTC) forms DER allows. */
42
+ export declare function readTime(el: Der | undefined, what?: string): number;
43
+ export declare function encode(tag: number, ...content: Uint8Array[]): Uint8Array<ArrayBuffer>;
44
+ export declare const encodeSequence: (...items: Uint8Array[]) => Uint8Array<ArrayBuffer>;
45
+ export declare const encodeNull: () => Uint8Array<ArrayBuffer>;
46
+ export declare const encodeBoolean: (value: boolean) => Uint8Array<ArrayBuffer>;
47
+ export declare const encodeOctetString: (bytes: Uint8Array) => Uint8Array<ArrayBuffer>;
48
+ /** A non-negative INTEGER from big-endian unsigned bytes, minimally encoded. */
49
+ export declare function encodeUnsignedInteger(bytes: Uint8Array): Uint8Array<ArrayBuffer>;
50
+ export declare function encodeSmallInt(value: number): Uint8Array<ArrayBuffer>;
51
+ export declare function encodeOid(oid: string): Uint8Array<ArrayBuffer>;
52
+ /** Decodes the base64 body of every PEM block of the given label (default CERTIFICATE). */
53
+ export declare function fromPem(text: string, label?: string): Uint8Array<ArrayBuffer>[];