@ziffer-io/verify 0.1.1 → 0.2.1
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.
- package/README.md +8 -0
- package/dist/cbor.d.ts +87 -0
- package/dist/cbor.d.ts.map +1 -0
- package/dist/cbor.js +326 -0
- package/dist/cbor.js.map +1 -0
- package/dist/chain.d.ts +142 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +430 -0
- package/dist/chain.js.map +1 -0
- package/dist/clauses.d.ts +70 -0
- package/dist/clauses.d.ts.map +1 -1
- package/dist/clauses.js +77 -0
- package/dist/clauses.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/quorum.d.ts +245 -0
- package/dist/quorum.d.ts.map +1 -0
- package/dist/quorum.js +566 -0
- package/dist/quorum.js.map +1 -0
- package/dist/sig.d.ts +57 -0
- package/dist/sig.d.ts.map +1 -0
- package/dist/sig.js +127 -0
- package/dist/sig.js.map +1 -0
- package/dist/testkeys.d.ts +33 -0
- package/dist/testkeys.d.ts.map +1 -1
- package/dist/testkeys.js +100 -0
- package/dist/testkeys.js.map +1 -1
- package/dist/verify.d.ts +44 -20
- package/dist/verify.d.ts.map +1 -1
- package/dist/verify.js +55 -146
- package/dist/verify.js.map +1 -1
- package/dist/webauthn.d.ts +148 -0
- package/dist/webauthn.d.ts.map +1 -0
- package/dist/webauthn.js +414 -0
- package/dist/webauthn.js.map +1 -0
- package/dist/wire.d.ts +44 -0
- package/dist/wire.d.ts.map +1 -0
- package/dist/wire.js +90 -0
- package/dist/wire.js.map +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -50,6 +50,14 @@ Every refusal is a thrown `Refusal` whose `clause` names the rule that fired; th
|
|
|
50
50
|
clause, what it means and what to do is at https://ziffer.io/docs/refusals. Narrow with
|
|
51
51
|
`instanceof Refusal`, record the clause, and do not retry it.
|
|
52
52
|
|
|
53
|
+
## Checking an audit chain download
|
|
54
|
+
|
|
55
|
+
The console's *Download the audit chain for this window* file checks here too:
|
|
56
|
+
`verifyChain(fileBytes, parseAnchorKey(anchorKeyBytes))` recomputes every record's hash, checks
|
|
57
|
+
each anchor's signature under the audit anchor key you hold (never the copy inside the file), and
|
|
58
|
+
reports which records no anchor covers yet. A break throws `ChainRefusal`, whose `refusal` and
|
|
59
|
+
`seq` name what broke and where. Integrating the SDK, section 6, has the whole of it.
|
|
60
|
+
|
|
53
61
|
## Documentation
|
|
54
62
|
|
|
55
63
|
- Quickstart: https://ziffer.io/docs/quickstart
|
package/dist/cbor.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AT-8a canonical CBOR (RFC 8949 §4.2) -- the ONE primitive this package did
|
|
3
|
+
* not already have, added for §8.6c and for nothing else.
|
|
4
|
+
*
|
|
5
|
+
* # Why a new module rather than a reused one
|
|
6
|
+
*
|
|
7
|
+
* `canon.ts` is the canonical JSON encoding, and it is the right preimage for
|
|
8
|
+
* every hash this verifier takes: the engine's `attestation_id` and AB-1 entry
|
|
9
|
+
* digest are `sha256:` over `acp_executor.canon` / `receipt.rs::canon`, both
|
|
10
|
+
* JSON, and this package reproduces those bytes. What CBOR is needed for is
|
|
11
|
+
* narrower and unavoidable: HM-3 says a human attester's assertion is canonical
|
|
12
|
+
* CBOR of `{authenticator_data, client_data_json, signature}`, and HM-1 says the
|
|
13
|
+
* credential's `public_key` is a COSE_Key, which is CBOR by RFC 9052. Those two
|
|
14
|
+
* byte strings arrive from the wire and must be DECODED; no JSON encoder can
|
|
15
|
+
* read them. So this is a decoder for two fixed shapes, not a general codec.
|
|
16
|
+
*
|
|
17
|
+
* The engine's twins are `crates/acp-crypto/src/cbor.rs` and
|
|
18
|
+
* `reference/src/acp_crypto.py`'s `_enc` / `_dec` / `decode_canonical`, and the
|
|
19
|
+
* rule this file takes from them verbatim is the last one:
|
|
20
|
+
*
|
|
21
|
+
* # Canonicity is proved by RE-ENCODING, not by a list of sub-rules
|
|
22
|
+
*
|
|
23
|
+
* {@link decodeCanonical} parses, re-encodes what it parsed, and refuses the
|
|
24
|
+
* input unless the two byte strings are identical. That single comparison
|
|
25
|
+
* carries every §4.2 requirement at once -- shortest-form arguments, definite
|
|
26
|
+
* lengths, map keys sorted by their ENCODED bytes, no duplicate keys -- and it
|
|
27
|
+
* cannot drift from the encoder the way a hand-written checklist of those rules
|
|
28
|
+
* can. The decoder still refuses the coarse violations itself (indefinite
|
|
29
|
+
* lengths, non-shortest arguments, reserved additional information) because a
|
|
30
|
+
* decoder that accepted them would have to invent a canonical form to compare
|
|
31
|
+
* against, and inventing one is normalising.
|
|
32
|
+
*
|
|
33
|
+
* Refused, never normalised (AT-8a): the AB-1 entry digest is taken over the
|
|
34
|
+
* bytes that arrived, so a second encoding of one assertion is a second digest
|
|
35
|
+
* for one human decision, and accepting both would put two receipt identities
|
|
36
|
+
* behind one approval -- the Z4 encoding split, reached through the assertion.
|
|
37
|
+
*
|
|
38
|
+
* # Floats and tags are absent on purpose
|
|
39
|
+
*
|
|
40
|
+
* Neither is encodable, so neither is decodable: a float has several
|
|
41
|
+
* representations of one value and no deterministic ordering story (WE-1), and
|
|
42
|
+
* a tag is a type this verifier has no shape for. They refuse as
|
|
43
|
+
* {@link CborError} rather than being skipped, because a decoder that silently
|
|
44
|
+
* ignores what it does not understand is a parser an attacker can steer.
|
|
45
|
+
*/
|
|
46
|
+
/** The input is not canonical CBOR, or is not CBOR at all. */
|
|
47
|
+
export declare class CborError extends Error {
|
|
48
|
+
constructor(message: string);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* What this decoder can produce. Deliberately NOT a general CBOR value type:
|
|
52
|
+
* a map key is an integer (COSE labels, RFC 9052 §7) or text (HM-3's three
|
|
53
|
+
* fields), and nothing here needs more.
|
|
54
|
+
*/
|
|
55
|
+
export type Cbor = boolean | null | bigint | Uint8Array | string | Cbor[] | CborMap;
|
|
56
|
+
/**
|
|
57
|
+
* A CBOR map, kept as ordered entries rather than a JS object.
|
|
58
|
+
*
|
|
59
|
+
* An object would key everything by string and collapse the integer label `1`
|
|
60
|
+
* and the text key `"1"` into one entry -- two distinct CBOR values, and in
|
|
61
|
+
* COSE_Key the integer form is the only legal one. The order is the order the
|
|
62
|
+
* bytes carried, which the re-encode check has already proved canonical.
|
|
63
|
+
*/
|
|
64
|
+
export interface CborMap {
|
|
65
|
+
readonly entries: readonly (readonly [Cbor, Cbor])[];
|
|
66
|
+
}
|
|
67
|
+
/** Whether a decoded value is a map -- the narrowing the callers need. */
|
|
68
|
+
export declare function cborIsMap(v: Cbor): v is CborMap;
|
|
69
|
+
/** A map's keys, in the (canonical) order the bytes carried. */
|
|
70
|
+
export declare function cborKeys(m: CborMap): Cbor[];
|
|
71
|
+
/** The value at an integer label, or undefined. COSE_Key's accessor. */
|
|
72
|
+
export declare function cborGetInt(m: CborMap, label: bigint): Cbor | undefined;
|
|
73
|
+
/** The value at a text key, or undefined. The assertion map's accessor. */
|
|
74
|
+
export declare function cborGetText(m: CborMap, key: string): Cbor | undefined;
|
|
75
|
+
/** A decoded value as bytes, or null if it is not a byte string. */
|
|
76
|
+
export declare function cborAsBytes(v: Cbor | undefined): Uint8Array | null;
|
|
77
|
+
/** The canonical CBOR bytes of a value. Used by {@link decodeCanonical}'s proof. */
|
|
78
|
+
export declare function cborEncode(value: Cbor): Uint8Array;
|
|
79
|
+
/**
|
|
80
|
+
* AT-8a: parse AND prove canonicity. Any input that is not byte-identical to
|
|
81
|
+
* the canonical encoding of what it decodes to is REFUSED, never normalised.
|
|
82
|
+
*
|
|
83
|
+
* The engine's `decode_canonical` in one sentence, and the same sentence is the
|
|
84
|
+
* whole specification of this function.
|
|
85
|
+
*/
|
|
86
|
+
export declare function decodeCanonical(buf: Uint8Array): Cbor;
|
|
87
|
+
//# sourceMappingURL=cbor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cbor.d.ts","sourceRoot":"","sources":["../src/cbor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,8DAA8D;AAC9D,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,MAAM,MAAM,IAAI,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;AAEpF;;;;;;;GAOG;AACH,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;CACtD;AAMD,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,IAAI,OAAO,CAE/C;AAED,gEAAgE;AAChE,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,EAAE,CAE3C;AAED,wEAAwE;AACxE,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAGtE;AAED,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAGrE;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,CAAC,EAAE,IAAI,GAAG,SAAS,GAAG,UAAU,GAAG,IAAI,CAElE;AAsHD,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,KAAK,EAAE,IAAI,GAAG,UAAU,CAIlD;AA0FD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAQrD"}
|
package/dist/cbor.js
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AT-8a canonical CBOR (RFC 8949 §4.2) -- the ONE primitive this package did
|
|
3
|
+
* not already have, added for §8.6c and for nothing else.
|
|
4
|
+
*
|
|
5
|
+
* # Why a new module rather than a reused one
|
|
6
|
+
*
|
|
7
|
+
* `canon.ts` is the canonical JSON encoding, and it is the right preimage for
|
|
8
|
+
* every hash this verifier takes: the engine's `attestation_id` and AB-1 entry
|
|
9
|
+
* digest are `sha256:` over `acp_executor.canon` / `receipt.rs::canon`, both
|
|
10
|
+
* JSON, and this package reproduces those bytes. What CBOR is needed for is
|
|
11
|
+
* narrower and unavoidable: HM-3 says a human attester's assertion is canonical
|
|
12
|
+
* CBOR of `{authenticator_data, client_data_json, signature}`, and HM-1 says the
|
|
13
|
+
* credential's `public_key` is a COSE_Key, which is CBOR by RFC 9052. Those two
|
|
14
|
+
* byte strings arrive from the wire and must be DECODED; no JSON encoder can
|
|
15
|
+
* read them. So this is a decoder for two fixed shapes, not a general codec.
|
|
16
|
+
*
|
|
17
|
+
* The engine's twins are `crates/acp-crypto/src/cbor.rs` and
|
|
18
|
+
* `reference/src/acp_crypto.py`'s `_enc` / `_dec` / `decode_canonical`, and the
|
|
19
|
+
* rule this file takes from them verbatim is the last one:
|
|
20
|
+
*
|
|
21
|
+
* # Canonicity is proved by RE-ENCODING, not by a list of sub-rules
|
|
22
|
+
*
|
|
23
|
+
* {@link decodeCanonical} parses, re-encodes what it parsed, and refuses the
|
|
24
|
+
* input unless the two byte strings are identical. That single comparison
|
|
25
|
+
* carries every §4.2 requirement at once -- shortest-form arguments, definite
|
|
26
|
+
* lengths, map keys sorted by their ENCODED bytes, no duplicate keys -- and it
|
|
27
|
+
* cannot drift from the encoder the way a hand-written checklist of those rules
|
|
28
|
+
* can. The decoder still refuses the coarse violations itself (indefinite
|
|
29
|
+
* lengths, non-shortest arguments, reserved additional information) because a
|
|
30
|
+
* decoder that accepted them would have to invent a canonical form to compare
|
|
31
|
+
* against, and inventing one is normalising.
|
|
32
|
+
*
|
|
33
|
+
* Refused, never normalised (AT-8a): the AB-1 entry digest is taken over the
|
|
34
|
+
* bytes that arrived, so a second encoding of one assertion is a second digest
|
|
35
|
+
* for one human decision, and accepting both would put two receipt identities
|
|
36
|
+
* behind one approval -- the Z4 encoding split, reached through the assertion.
|
|
37
|
+
*
|
|
38
|
+
* # Floats and tags are absent on purpose
|
|
39
|
+
*
|
|
40
|
+
* Neither is encodable, so neither is decodable: a float has several
|
|
41
|
+
* representations of one value and no deterministic ordering story (WE-1), and
|
|
42
|
+
* a tag is a type this verifier has no shape for. They refuse as
|
|
43
|
+
* {@link CborError} rather than being skipped, because a decoder that silently
|
|
44
|
+
* ignores what it does not understand is a parser an attacker can steer.
|
|
45
|
+
*/
|
|
46
|
+
/** The input is not canonical CBOR, or is not CBOR at all. */
|
|
47
|
+
export class CborError extends Error {
|
|
48
|
+
constructor(message) {
|
|
49
|
+
super(message);
|
|
50
|
+
this.name = 'CborError';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function isCborMap(v) {
|
|
54
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v) && !(v instanceof Uint8Array);
|
|
55
|
+
}
|
|
56
|
+
/** Whether a decoded value is a map -- the narrowing the callers need. */
|
|
57
|
+
export function cborIsMap(v) {
|
|
58
|
+
return isCborMap(v);
|
|
59
|
+
}
|
|
60
|
+
/** A map's keys, in the (canonical) order the bytes carried. */
|
|
61
|
+
export function cborKeys(m) {
|
|
62
|
+
return m.entries.map(([k]) => k);
|
|
63
|
+
}
|
|
64
|
+
/** The value at an integer label, or undefined. COSE_Key's accessor. */
|
|
65
|
+
export function cborGetInt(m, label) {
|
|
66
|
+
for (const [k, v] of m.entries)
|
|
67
|
+
if (typeof k === 'bigint' && k === label)
|
|
68
|
+
return v;
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
/** The value at a text key, or undefined. The assertion map's accessor. */
|
|
72
|
+
export function cborGetText(m, key) {
|
|
73
|
+
for (const [k, v] of m.entries)
|
|
74
|
+
if (typeof k === 'string' && k === key)
|
|
75
|
+
return v;
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
/** A decoded value as bytes, or null if it is not a byte string. */
|
|
79
|
+
export function cborAsBytes(v) {
|
|
80
|
+
return v instanceof Uint8Array ? v : null;
|
|
81
|
+
}
|
|
82
|
+
const MAJOR_UINT = 0;
|
|
83
|
+
const MAJOR_NINT = 1;
|
|
84
|
+
const MAJOR_BYTES = 2;
|
|
85
|
+
const MAJOR_TEXT = 3;
|
|
86
|
+
const MAJOR_ARRAY = 4;
|
|
87
|
+
const MAJOR_MAP = 5;
|
|
88
|
+
const MAJOR_SIMPLE = 7;
|
|
89
|
+
/**
|
|
90
|
+
* RFC 8949 §4.2.1: the argument MUST use the shortest form that holds it.
|
|
91
|
+
*
|
|
92
|
+
* `bigint` throughout rather than `number`, because a CBOR argument is a full
|
|
93
|
+
* 64-bit unsigned integer and `number` loses digits past 2^53-1 -- the same
|
|
94
|
+
* defect `canon.ts` refuses on the JSON side, and here it would mean re-encoding
|
|
95
|
+
* a length to different bytes than arrived and calling the input non-canonical
|
|
96
|
+
* for the decoder's own reason.
|
|
97
|
+
*/
|
|
98
|
+
function encodeHead(major, value, out) {
|
|
99
|
+
const head = major << 5;
|
|
100
|
+
if (value < 24n) {
|
|
101
|
+
out.push(head | Number(value));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
let width;
|
|
105
|
+
if (value < 0x100n) {
|
|
106
|
+
out.push(head | 24);
|
|
107
|
+
width = 1;
|
|
108
|
+
}
|
|
109
|
+
else if (value < 0x10000n) {
|
|
110
|
+
out.push(head | 25);
|
|
111
|
+
width = 2;
|
|
112
|
+
}
|
|
113
|
+
else if (value < 0x100000000n) {
|
|
114
|
+
out.push(head | 26);
|
|
115
|
+
width = 4;
|
|
116
|
+
}
|
|
117
|
+
else if (value < 0x10000000000000000n) {
|
|
118
|
+
out.push(head | 27);
|
|
119
|
+
width = 8;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
throw new CborError('integer exceeds the 64-bit CBOR argument');
|
|
123
|
+
}
|
|
124
|
+
for (let i = width - 1; i >= 0; i -= 1) {
|
|
125
|
+
out.push(Number((value >> BigInt(i * 8)) & 0xffn));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function encodeInto(value, out) {
|
|
129
|
+
if (value === true) {
|
|
130
|
+
out.push(0xf5);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (value === false) {
|
|
134
|
+
out.push(0xf4);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (value === null) {
|
|
138
|
+
out.push(0xf6);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (typeof value === 'bigint') {
|
|
142
|
+
if (value >= 0n)
|
|
143
|
+
encodeHead(MAJOR_UINT, value, out);
|
|
144
|
+
else
|
|
145
|
+
encodeHead(MAJOR_NINT, -value - 1n, out);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (value instanceof Uint8Array) {
|
|
149
|
+
encodeHead(MAJOR_BYTES, BigInt(value.length), out);
|
|
150
|
+
for (const b of value)
|
|
151
|
+
out.push(b);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (typeof value === 'string') {
|
|
155
|
+
const bytes = new TextEncoder().encode(value);
|
|
156
|
+
encodeHead(MAJOR_TEXT, BigInt(bytes.length), out);
|
|
157
|
+
for (const b of bytes)
|
|
158
|
+
out.push(b);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (Array.isArray(value)) {
|
|
162
|
+
encodeHead(MAJOR_ARRAY, BigInt(value.length), out);
|
|
163
|
+
for (const item of value)
|
|
164
|
+
encodeInto(item, out);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (isCborMap(value)) {
|
|
168
|
+
// §4.2.1: keys sorted by their ENCODED bytes, bytewise lexicographic.
|
|
169
|
+
// Sorting by the decoded value would put the integer label -2 and the text
|
|
170
|
+
// key "x" in an order that depends on the comparison chosen rather than on
|
|
171
|
+
// the bytes, and the encoded order is the one the rule names.
|
|
172
|
+
const keyed = value.entries.map(([k, v]) => {
|
|
173
|
+
const kb = [];
|
|
174
|
+
encodeInto(k, kb);
|
|
175
|
+
return { kb, v };
|
|
176
|
+
});
|
|
177
|
+
keyed.sort((a, b) => compareBytes(a.kb, b.kb));
|
|
178
|
+
for (let i = 1; i < keyed.length; i += 1) {
|
|
179
|
+
const prev = keyed[i - 1];
|
|
180
|
+
const cur = keyed[i];
|
|
181
|
+
if (prev === undefined || cur === undefined)
|
|
182
|
+
continue; // unreachable; index in range
|
|
183
|
+
if (compareBytes(prev.kb, cur.kb) === 0)
|
|
184
|
+
throw new CborError('duplicate map key');
|
|
185
|
+
}
|
|
186
|
+
encodeHead(MAJOR_MAP, BigInt(keyed.length), out);
|
|
187
|
+
for (const { kb, v } of keyed) {
|
|
188
|
+
for (const b of kb)
|
|
189
|
+
out.push(b);
|
|
190
|
+
encodeInto(v, out);
|
|
191
|
+
}
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
throw new CborError('value is not canonically encodable');
|
|
195
|
+
}
|
|
196
|
+
function compareBytes(a, b) {
|
|
197
|
+
const n = Math.min(a.length, b.length);
|
|
198
|
+
for (let i = 0; i < n; i += 1) {
|
|
199
|
+
const x = a[i];
|
|
200
|
+
const y = b[i];
|
|
201
|
+
if (x === undefined || y === undefined)
|
|
202
|
+
continue; // unreachable; index in range
|
|
203
|
+
if (x !== y)
|
|
204
|
+
return x - y;
|
|
205
|
+
}
|
|
206
|
+
return a.length - b.length;
|
|
207
|
+
}
|
|
208
|
+
/** The canonical CBOR bytes of a value. Used by {@link decodeCanonical}'s proof. */
|
|
209
|
+
export function cborEncode(value) {
|
|
210
|
+
const out = [];
|
|
211
|
+
encodeInto(value, out);
|
|
212
|
+
return Uint8Array.from(out);
|
|
213
|
+
}
|
|
214
|
+
function decodeAt(buf, start) {
|
|
215
|
+
if (start >= buf.length)
|
|
216
|
+
throw new CborError('truncated');
|
|
217
|
+
const ib = buf[start];
|
|
218
|
+
if (ib === undefined)
|
|
219
|
+
throw new CborError('truncated'); // unreachable; bound checked
|
|
220
|
+
const major = ib >> 5;
|
|
221
|
+
const ai = ib & 0x1f;
|
|
222
|
+
let i = start + 1;
|
|
223
|
+
let value;
|
|
224
|
+
if (ai < 24) {
|
|
225
|
+
value = BigInt(ai);
|
|
226
|
+
}
|
|
227
|
+
else if (ai <= 27) {
|
|
228
|
+
const width = 1 << (ai - 24);
|
|
229
|
+
if (i + width > buf.length)
|
|
230
|
+
throw new CborError('truncated argument');
|
|
231
|
+
value = 0n;
|
|
232
|
+
for (let k = 0; k < width; k += 1) {
|
|
233
|
+
const b = buf[i + k];
|
|
234
|
+
if (b === undefined)
|
|
235
|
+
throw new CborError('truncated argument'); // unreachable
|
|
236
|
+
value = (value << 8n) | BigInt(b);
|
|
237
|
+
}
|
|
238
|
+
i += width;
|
|
239
|
+
// Shortest form (§4.2.1). Refused here as well as by the re-encode proof,
|
|
240
|
+
// because the value that re-encodes shorter would otherwise be decoded
|
|
241
|
+
// first and any shape check above would run on attacker-chosen bytes.
|
|
242
|
+
const floor = ai === 24 ? 24n : 1n << BigInt((width / 2) * 8);
|
|
243
|
+
if (value < floor)
|
|
244
|
+
throw new CborError('non-shortest argument');
|
|
245
|
+
}
|
|
246
|
+
else if (ai === 31) {
|
|
247
|
+
throw new CborError('indefinite length forbidden in canonical CBOR');
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
throw new CborError(`reserved additional information ${ai}`);
|
|
251
|
+
}
|
|
252
|
+
switch (major) {
|
|
253
|
+
case MAJOR_UINT:
|
|
254
|
+
return { value, next: i };
|
|
255
|
+
case MAJOR_NINT:
|
|
256
|
+
return { value: -value - 1n, next: i };
|
|
257
|
+
case MAJOR_BYTES:
|
|
258
|
+
case MAJOR_TEXT: {
|
|
259
|
+
const len = Number(value);
|
|
260
|
+
if (!Number.isSafeInteger(len) || i + len > buf.length) {
|
|
261
|
+
throw new CborError('truncated string');
|
|
262
|
+
}
|
|
263
|
+
const slice = buf.subarray(i, i + len);
|
|
264
|
+
if (major === MAJOR_BYTES)
|
|
265
|
+
return { value: Uint8Array.from(slice), next: i + len };
|
|
266
|
+
// fatal: true -- invalid UTF-8 is a refusal, not a run of U+FFFD. A
|
|
267
|
+
// replacement character would make two distinct byte strings decode to
|
|
268
|
+
// one text value, which the re-encode proof would then report as
|
|
269
|
+
// non-canonical for the wrong reason.
|
|
270
|
+
return { value: new TextDecoder('utf-8', { fatal: true }).decode(slice), next: i + len };
|
|
271
|
+
}
|
|
272
|
+
case MAJOR_ARRAY: {
|
|
273
|
+
const n = Number(value);
|
|
274
|
+
if (!Number.isSafeInteger(n) || n > buf.length - i)
|
|
275
|
+
throw new CborError('truncated array');
|
|
276
|
+
const items = [];
|
|
277
|
+
for (let k = 0; k < n; k += 1) {
|
|
278
|
+
const d = decodeAt(buf, i);
|
|
279
|
+
items.push(d.value);
|
|
280
|
+
i = d.next;
|
|
281
|
+
}
|
|
282
|
+
return { value: items, next: i };
|
|
283
|
+
}
|
|
284
|
+
case MAJOR_MAP: {
|
|
285
|
+
const n = Number(value);
|
|
286
|
+
if (!Number.isSafeInteger(n) || n > buf.length - i)
|
|
287
|
+
throw new CborError('truncated map');
|
|
288
|
+
const entries = [];
|
|
289
|
+
for (let k = 0; k < n; k += 1) {
|
|
290
|
+
const key = decodeAt(buf, i);
|
|
291
|
+
const val = decodeAt(buf, key.next);
|
|
292
|
+
entries.push([key.value, val.value]);
|
|
293
|
+
i = val.next;
|
|
294
|
+
}
|
|
295
|
+
return { value: { entries }, next: i };
|
|
296
|
+
}
|
|
297
|
+
case MAJOR_SIMPLE:
|
|
298
|
+
if (value === 20n)
|
|
299
|
+
return { value: false, next: i };
|
|
300
|
+
if (value === 21n)
|
|
301
|
+
return { value: true, next: i };
|
|
302
|
+
if (value === 22n)
|
|
303
|
+
return { value: null, next: i };
|
|
304
|
+
throw new CborError('float or unsupported simple value');
|
|
305
|
+
default:
|
|
306
|
+
throw new CborError(`unsupported major type ${major}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* AT-8a: parse AND prove canonicity. Any input that is not byte-identical to
|
|
311
|
+
* the canonical encoding of what it decodes to is REFUSED, never normalised.
|
|
312
|
+
*
|
|
313
|
+
* The engine's `decode_canonical` in one sentence, and the same sentence is the
|
|
314
|
+
* whole specification of this function.
|
|
315
|
+
*/
|
|
316
|
+
export function decodeCanonical(buf) {
|
|
317
|
+
const { value, next } = decodeAt(buf, 0);
|
|
318
|
+
if (next !== buf.length)
|
|
319
|
+
throw new CborError('trailing bytes');
|
|
320
|
+
const re = cborEncode(value);
|
|
321
|
+
if (re.length !== buf.length || !re.every((b, i) => b === buf[i])) {
|
|
322
|
+
throw new CborError('input is not the canonical encoding of its value');
|
|
323
|
+
}
|
|
324
|
+
return value;
|
|
325
|
+
}
|
|
326
|
+
//# sourceMappingURL=cbor.js.map
|
package/dist/cbor.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cbor.js","sourceRoot":"","sources":["../src/cbor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,8DAA8D;AAC9D,MAAM,OAAO,SAAU,SAAQ,KAAK;IAClC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC1B,CAAC;CACF;AAqBD,SAAS,SAAS,CAAC,CAAO;IACxB,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,UAAU,CAAC,CAAC;AAChG,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,SAAS,CAAC,CAAO;IAC/B,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC;AACtB,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,QAAQ,CAAC,CAAU;IACjC,OAAO,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;AACnC,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,UAAU,CAAC,CAAU,EAAE,KAAa;IAClD,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO;QAAE,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,KAAK;YAAE,OAAO,CAAC,CAAC;IACnF,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,WAAW,CAAC,CAAU,EAAE,GAAW;IACjD,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO;QAAE,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,CAAC,CAAC;IACjF,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,WAAW,CAAC,CAAmB;IAC7C,OAAO,CAAC,YAAY,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,CAAC;AACrB,MAAM,UAAU,GAAG,CAAC,CAAC;AACrB,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,UAAU,GAAG,CAAC,CAAC;AACrB,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,SAAS,GAAG,CAAC,CAAC;AACpB,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvB;;;;;;;;GAQG;AACH,SAAS,UAAU,CAAC,KAAa,EAAE,KAAa,EAAE,GAAa;IAC7D,MAAM,IAAI,GAAG,KAAK,IAAI,CAAC,CAAC;IACxB,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;QAChB,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/B,OAAO;IACT,CAAC;IACD,IAAI,KAAa,CAAC;IAClB,IAAI,KAAK,GAAG,MAAM,EAAE,CAAC;QACnB,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;QACpB,KAAK,GAAG,CAAC,CAAC;IACZ,CAAC;SAAM,IAAI,KAAK,GAAG,QAAQ,EAAE,CAAC;QAC5B,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;QACpB,KAAK,GAAG,CAAC,CAAC;IACZ,CAAC;SAAM,IAAI,KAAK,GAAG,YAAY,EAAE,CAAC;QAChC,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;QACpB,KAAK,GAAG,CAAC,CAAC;IACZ,CAAC;SAAM,IAAI,KAAK,GAAG,oBAAoB,EAAE,CAAC;QACxC,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;QACpB,KAAK,GAAG,CAAC,CAAC;IACZ,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,SAAS,CAAC,0CAA0C,CAAC,CAAC;IAClE,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;IACrD,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,KAAW,EAAE,GAAa;IAC5C,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,OAAO;IACT,CAAC;IACD,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;QACpB,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,OAAO;IACT,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,KAAK,IAAI,EAAE;YAAE,UAAU,CAAC,UAAU,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;;YAC/C,UAAU,CAAC,UAAU,EAAE,CAAC,KAAK,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;QAC9C,OAAO;IACT,CAAC;IACD,IAAI,KAAK,YAAY,UAAU,EAAE,CAAC;QAChC,UAAU,CAAC,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;QACnD,KAAK,MAAM,CAAC,IAAI,KAAK;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACnC,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC9C,UAAU,CAAC,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;QAClD,KAAK,MAAM,CAAC,IAAI,KAAK;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACnC,OAAO;IACT,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,UAAU,CAAC,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;QACnD,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAChD,OAAO;IACT,CAAC;IACD,IAAI,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,sEAAsE;QACtE,2EAA2E;QAC3E,2EAA2E;QAC3E,8DAA8D;QAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;YACzC,MAAM,EAAE,GAAa,EAAE,CAAC;YACxB,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAClB,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;QACnB,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC1B,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACrB,IAAI,IAAI,KAAK,SAAS,IAAI,GAAG,KAAK,SAAS;gBAAE,SAAS,CAAC,8BAA8B;YACrF,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC;gBAAE,MAAM,IAAI,SAAS,CAAC,mBAAmB,CAAC,CAAC;QACpF,CAAC;QACD,UAAU,CAAC,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;QACjD,KAAK,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,KAAK,EAAE,CAAC;YAC9B,KAAK,MAAM,CAAC,IAAI,EAAE;gBAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChC,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACrB,CAAC;QACD,OAAO;IACT,CAAC;IACD,MAAM,IAAI,SAAS,CAAC,oCAAoC,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,YAAY,CAAC,CAAoB,EAAE,CAAoB;IAC9D,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IACvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACf,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACf,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,SAAS;YAAE,SAAS,CAAC,8BAA8B;QAChF,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;AAC7B,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,UAAU,CAAC,KAAW;IACpC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACvB,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC9B,CAAC;AAOD,SAAS,QAAQ,CAAC,GAAe,EAAE,KAAa;IAC9C,IAAI,KAAK,IAAI,GAAG,CAAC,MAAM;QAAE,MAAM,IAAI,SAAS,CAAC,WAAW,CAAC,CAAC;IAC1D,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;IACtB,IAAI,EAAE,KAAK,SAAS;QAAE,MAAM,IAAI,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,6BAA6B;IACrF,MAAM,KAAK,GAAG,EAAE,IAAI,CAAC,CAAC;IACtB,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACrB,IAAI,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;IAClB,IAAI,KAAa,CAAC;IAClB,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;QACZ,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;IACrB,CAAC;SAAM,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QAC7B,IAAI,CAAC,GAAG,KAAK,GAAG,GAAG,CAAC,MAAM;YAAE,MAAM,IAAI,SAAS,CAAC,oBAAoB,CAAC,CAAC;QACtE,KAAK,GAAG,EAAE,CAAC;QACX,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAClC,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACrB,IAAI,CAAC,KAAK,SAAS;gBAAE,MAAM,IAAI,SAAS,CAAC,oBAAoB,CAAC,CAAC,CAAC,cAAc;YAC9E,KAAK,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;QACD,CAAC,IAAI,KAAK,CAAC;QACX,0EAA0E;QAC1E,uEAAuE;QACvE,sEAAsE;QACtE,MAAM,KAAK,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,MAAM,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9D,IAAI,KAAK,GAAG,KAAK;YAAE,MAAM,IAAI,SAAS,CAAC,uBAAuB,CAAC,CAAC;IAClE,CAAC;SAAM,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrB,MAAM,IAAI,SAAS,CAAC,+CAA+C,CAAC,CAAC;IACvE,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,SAAS,CAAC,mCAAmC,EAAE,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,UAAU;YACb,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAC5B,KAAK,UAAU;YACb,OAAO,EAAE,KAAK,EAAE,CAAC,KAAK,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACzC,KAAK,WAAW,CAAC;QACjB,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC1B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;gBACvD,MAAM,IAAI,SAAS,CAAC,kBAAkB,CAAC,CAAC;YAC1C,CAAC;YACD,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC;YACvC,IAAI,KAAK,KAAK,WAAW;gBAAE,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC;YACnF,oEAAoE;YACpE,uEAAuE;YACvE,iEAAiE;YACjE,sCAAsC;YACtC,OAAO,EAAE,KAAK,EAAE,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC;QAC3F,CAAC;QACD,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YACxB,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC;gBAAE,MAAM,IAAI,SAAS,CAAC,iBAAiB,CAAC,CAAC;YAC3F,MAAM,KAAK,GAAW,EAAE,CAAC;YACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9B,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;gBAC3B,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;gBACpB,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACb,CAAC;YACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACnC,CAAC;QACD,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YACxB,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC;gBAAE,MAAM,IAAI,SAAS,CAAC,eAAe,CAAC,CAAC;YACzF,MAAM,OAAO,GAA8B,EAAE,CAAC;YAC9C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9B,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;gBAC7B,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;gBACpC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;gBACrC,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC;YACf,CAAC;YACD,OAAO,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACzC,CAAC;QACD,KAAK,YAAY;YACf,IAAI,KAAK,KAAK,GAAG;gBAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YACpD,IAAI,KAAK,KAAK,GAAG;gBAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YACnD,IAAI,KAAK,KAAK,GAAG;gBAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YACnD,MAAM,IAAI,SAAS,CAAC,mCAAmC,CAAC,CAAC;QAC3D;YACE,MAAM,IAAI,SAAS,CAAC,0BAA0B,KAAK,EAAE,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,GAAe;IAC7C,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACzC,IAAI,IAAI,KAAK,GAAG,CAAC,MAAM;QAAE,MAAM,IAAI,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAC/D,MAAM,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,EAAE,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,SAAS,CAAC,kDAAkD,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/chain.d.ts
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The audit chain export verifier (ACP-428) -- the TypeScript reader of the
|
|
3
|
+
* file the console's "Download the audit chain for this window" link gives.
|
|
4
|
+
*
|
|
5
|
+
* The link said the file was verifiable offline and nothing a customer held
|
|
6
|
+
* verified it. The Python SDK's `verify_chain` and this function are that
|
|
7
|
+
* verifier, and they are held to ONE verdict per file by the shared corpus in
|
|
8
|
+
* `fixtures/chain-export/` -- the same refusal name at the same record, the
|
|
9
|
+
* same recomputed last hash, the same report of what the anchors cover. The
|
|
10
|
+
* check order below is the Python reader's, step for step, because on a file
|
|
11
|
+
* with two defects the order decides which one is named.
|
|
12
|
+
*
|
|
13
|
+
* # What is recomputed, and what is only reported
|
|
14
|
+
*
|
|
15
|
+
* Every `chain_hash` is RECOMPUTED -- `sha256:` over the canonical bytes of the
|
|
16
|
+
* record (the first record of the chain) or of `{prev, record}` (every later
|
|
17
|
+
* one), the audit service's own link -- and the file's value is compared,
|
|
18
|
+
* never used. Every `previous_hash` after the first row is compared against
|
|
19
|
+
* the hash just recomputed. The first row's `previous_hash`, in a file that
|
|
20
|
+
* starts after the beginning of the chain, is the one link this file cannot
|
|
21
|
+
* recompute; it is reported as `startsAfter`, and it is checked only when an
|
|
22
|
+
* anchor names exactly that position.
|
|
23
|
+
*
|
|
24
|
+
* # The anchor key never comes from the file
|
|
25
|
+
*
|
|
26
|
+
* Every anchor entry carries an `anchor_identity` beside it. Reading the key
|
|
27
|
+
* from there would accept any file whose author signed their own anchors and
|
|
28
|
+
* wrote their own key next to them -- the party under verification supplying
|
|
29
|
+
* the value it is verified with. So the key is a parameter, from a file the
|
|
30
|
+
* customer holds, and with no key every anchor is reported `unchecked`: never
|
|
31
|
+
* passed, and never dropped from the report.
|
|
32
|
+
*
|
|
33
|
+
* # Numbers: the file's TEXT is read, not only its parsed values
|
|
34
|
+
*
|
|
35
|
+
* `JSON.parse` reads `1.0` as the integer 1. The record the writer hashed said
|
|
36
|
+
* `1.0`, so hashing `1` would compare bytes nobody wrote and name the wrong
|
|
37
|
+
* defect. The parser's reviver sees each number's source text (Node 22 and
|
|
38
|
+
* later), and a number written with a fraction or an exponent becomes a value
|
|
39
|
+
* the canonical encoder refuses -- `RecordNotCanonical` inside a record,
|
|
40
|
+
* `ExportMalformed` in the file's own fields, exactly where the Python reader
|
|
41
|
+
* says so. On a runtime whose parser does not hand the reviver the source
|
|
42
|
+
* text, that one case would be named `ChainHashMismatch` instead; the file is
|
|
43
|
+
* refused either way.
|
|
44
|
+
*/
|
|
45
|
+
/** The one suite an audit anchor is signed under here, and the only one an anchor key may name. */
|
|
46
|
+
export declare const ANCHOR_SUITE = "hybrid-ed25519-mldsa65";
|
|
47
|
+
/** A refusal of an export, by name, at the first record or anchor that broke. */
|
|
48
|
+
export declare class ChainRefusal extends Error {
|
|
49
|
+
/** The refusal name, identical to the Python reader's `ChainRefused.name`. */
|
|
50
|
+
readonly refusal: string;
|
|
51
|
+
readonly seq: number | null;
|
|
52
|
+
readonly anchor: number | null;
|
|
53
|
+
constructor(refusal: string, detail: string, where?: {
|
|
54
|
+
seq?: number;
|
|
55
|
+
anchor?: number;
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/** An anchor key file that cannot be used, by name. A defect in the caller's own file, not a verdict. */
|
|
59
|
+
export declare class AnchorKeyRefusal extends Error {
|
|
60
|
+
readonly refusal: string;
|
|
61
|
+
constructor(refusal: string, detail: string);
|
|
62
|
+
}
|
|
63
|
+
/** The audit anchor identity's public halves, from the file the customer holds. */
|
|
64
|
+
export interface AnchorKey {
|
|
65
|
+
readonly alg: typeof ANCHOR_SUITE;
|
|
66
|
+
readonly classical: Uint8Array;
|
|
67
|
+
readonly pq: Uint8Array;
|
|
68
|
+
readonly fingerprint: string;
|
|
69
|
+
}
|
|
70
|
+
export interface AnchorResult {
|
|
71
|
+
readonly index: number;
|
|
72
|
+
readonly status: 'verified' | 'unchecked';
|
|
73
|
+
readonly placement: 'in_file' | 'window_floor' | 'before_file' | 'after_file' | 'no_head';
|
|
74
|
+
readonly headSeq: number | null;
|
|
75
|
+
readonly periodEnd: number;
|
|
76
|
+
}
|
|
77
|
+
export interface ChainVerified {
|
|
78
|
+
readonly tenant: string;
|
|
79
|
+
readonly firstSeq: number;
|
|
80
|
+
readonly lastSeq: number;
|
|
81
|
+
readonly records: number;
|
|
82
|
+
/** RECOMPUTED, never the file's. */
|
|
83
|
+
readonly lastChainHash: string;
|
|
84
|
+
readonly startsAfter: string | null;
|
|
85
|
+
readonly anchorsChecked: boolean;
|
|
86
|
+
readonly anchors: readonly AnchorResult[];
|
|
87
|
+
readonly anchoredThrough: number | null;
|
|
88
|
+
readonly unanchoredFrom: number | null;
|
|
89
|
+
readonly unanchoredCount: number;
|
|
90
|
+
}
|
|
91
|
+
/** The verdict as the shared corpus writes it -- snake_case, the Python reader's keys. */
|
|
92
|
+
export type ChainVerdict = {
|
|
93
|
+
readonly verdict: 'verified';
|
|
94
|
+
readonly tenant: string;
|
|
95
|
+
readonly first_seq: number;
|
|
96
|
+
readonly last_seq: number;
|
|
97
|
+
readonly records: number;
|
|
98
|
+
readonly last_chain_hash: string;
|
|
99
|
+
readonly starts_after: string | null;
|
|
100
|
+
readonly anchors_checked: boolean;
|
|
101
|
+
readonly anchors: readonly {
|
|
102
|
+
readonly index: number;
|
|
103
|
+
readonly status: string;
|
|
104
|
+
readonly placement: string;
|
|
105
|
+
readonly head_seq: number | null;
|
|
106
|
+
readonly period_end: number;
|
|
107
|
+
}[];
|
|
108
|
+
readonly anchored_through: number | null;
|
|
109
|
+
readonly unanchored_from: number | null;
|
|
110
|
+
readonly unanchored_count: number;
|
|
111
|
+
} | {
|
|
112
|
+
readonly verdict: 'refused';
|
|
113
|
+
readonly refusal: string;
|
|
114
|
+
readonly seq: number | null;
|
|
115
|
+
readonly anchor: number | null;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Read the audit anchor identity's public document -- `{alg, classical, pq,
|
|
119
|
+
* fingerprint}`, the halves in base64 -- checking every redundant field: the
|
|
120
|
+
* fingerprint is recomputed over both halves and compared, and a small-order
|
|
121
|
+
* Ed25519 half is refused, because under one a single signature verifies every
|
|
122
|
+
* message.
|
|
123
|
+
*/
|
|
124
|
+
export declare function parseAnchorKey(file: string | Uint8Array): AnchorKey;
|
|
125
|
+
/**
|
|
126
|
+
* Verify an audit chain export, from the file's bytes or text.
|
|
127
|
+
*
|
|
128
|
+
* Throws {@link ChainRefusal} at the first break: `ExportMalformed`,
|
|
129
|
+
* `ExportEmpty`, `ChainSeqGap`, `RecordOutOfPlace`, `ChainLinkBroken`,
|
|
130
|
+
* `RecordNotCanonical`, `ChainHashMismatch`, `AnchorMalformed`,
|
|
131
|
+
* `AnchorSuiteNotEnrolled`, `AnchorSignatureInvalid`, `AnchorHeadMismatch`.
|
|
132
|
+
*
|
|
133
|
+
* NOT checked, on purpose, as in the Python reader: whether each record's
|
|
134
|
+
* fields are the set its kind of event carries, the transparency-log entry and
|
|
135
|
+
* timestamp stored beside each anchor, and whether the file holds EVERY record
|
|
136
|
+
* of its window -- a file cut short at the end verifies, and `unanchoredFrom`
|
|
137
|
+
* says how much of it nothing yet commits to.
|
|
138
|
+
*/
|
|
139
|
+
export declare function verifyChain(file: string | Uint8Array, anchorKey?: AnchorKey): ChainVerified;
|
|
140
|
+
/** {@link verifyChain}'s answer, or its refusal, as one corpus-shaped object. */
|
|
141
|
+
export declare function chainVerdict(file: string | Uint8Array, anchorKey?: AnchorKey): ChainVerdict;
|
|
142
|
+
//# sourceMappingURL=chain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chain.d.ts","sourceRoot":"","sources":["../src/chain.ts"],"names":[],"mappings":"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"}
|