@ziffer-io/verify 0.1.0 → 0.2.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,414 @@
1
+ /**
2
+ * §8.6c HM-1..HM-7 -- a human attester's WebAuthn assertion, verified in the
3
+ * customer's own process.
4
+ *
5
+ * The third writing of `crates/acp-crypto/src/webauthn.rs` and
6
+ * `reference/src/acp_crypto.py`, and the mirrored corpus
7
+ * `fixtures/refused_webauthn.json` is what holds the three to one rule: 18
8
+ * vectors, two ACCEPT and one refusal per HM-4 step, replayed by
9
+ * `webauthn.test.ts` against this file.
10
+ *
11
+ * # What a human leg is NOT (HM-6, CR-8)
12
+ *
13
+ * **CLASSICAL, and this function must never be read as though it were more.**
14
+ * No passkey, platform authenticator or security key in existence produces a
15
+ * post-quantum signature, so a `webauthn` entry is never compared against the
16
+ * bundle's `min_suite` (CR-8) -- the absence of a floor check here IS the
17
+ * clause, not an omission. The post-quantum binding of a human's decision is
18
+ * the receipt AB-1 digests the whole entry into and the §11 anchor over it.
19
+ *
20
+ * # The order is normative and the NAMES are the product (HM-4)
21
+ *
22
+ * HM-4 states six checks in order and says no step is skipped for a smaller one
23
+ * that already failed. What the implementations must agree on is not "refused"
24
+ * but WHICH step refused: `WebauthnOrigin` is a phishing finding and
25
+ * `WebauthnChallenge` is a replay, and one name for both would make them one
26
+ * event in an audit record. That is why this throws a named
27
+ * {@link WebauthnRefusal} rather than returning a boolean.
28
+ *
29
+ * Step (f), the signature counter, is NOT here: comparing it needs the stored
30
+ * value for the credential, which is Consumption Ledger state. What this
31
+ * returns is the counter the assertion carried; `quorum.ts` compares it against
32
+ * what the caller stored, and discloses that this package holds no ledger.
33
+ */
34
+ import { createHash } from 'node:crypto';
35
+ import { p256 } from '@noble/curves/nist.js';
36
+ import { cborAsBytes, cborGetInt, cborGetText, cborIsMap, cborKeys, decodeCanonical } from './cbor.js';
37
+ import { ED25519_PK_LEN, ED25519_SIG_LEN, ed25519IsSmallOrder, verifyEd25519Strict } from './ed25519.js';
38
+ /**
39
+ * HM-1 / CR-8: the two names an entry's `alg` may take.
40
+ *
41
+ * ATTESTATION-ENTRY SUITES ONLY. `suite.ts`'s table is the machine one and
42
+ * these are deliberately absent from it, because CR-8 forbids either as a
43
+ * bundle, receipt or door suite -- a name present in both tables is a name
44
+ * whose meaning depends on where it was read.
45
+ */
46
+ export const WEBAUTHN_ALGS = ['webauthn-es256', 'webauthn-ed25519'];
47
+ /** The one primitive name a human entry's `sig` map carries (HM-3). */
48
+ export const WEBAUTHN_PRIMITIVE = 'webauthn';
49
+ /**
50
+ * HM-3's three fields.
51
+ *
52
+ * **This is not their canonical ORDER**, and the difference looks like one.
53
+ * RFC 8949 §4.2.1 sorts map keys by their ENCODED bytes and a text key's head
54
+ * carries its length, so the wire order is by length first: `signature` (9),
55
+ * `client_data_json` (16), `authenticator_data` (18). The decoder gets that
56
+ * right by construction; a reader who assumed this array's order was the wire
57
+ * order would write a decoder that refuses every real assertion.
58
+ */
59
+ export const ASSERTION_FIELDS = [
60
+ 'authenticator_data',
61
+ 'client_data_json',
62
+ 'signature',
63
+ ];
64
+ /** The registry entry, the COSE_Key or the assertion is not the declared shape. */
65
+ export const MALFORMED = 'Malformed';
66
+ /** PB-9 / HM-5, at enrolment: an off-curve ES256 key or a small-order Ed25519 one. */
67
+ export const REGISTRY_KEY_WEAK = 'RegistryKeyWeak';
68
+ /** HM-4 (a). */
69
+ export const WEBAUTHN_TYPE = 'WebauthnType';
70
+ /** HM-4 (b). */
71
+ export const WEBAUTHN_CHALLENGE = 'WebauthnChallenge';
72
+ /** HM-4 (c). */
73
+ export const WEBAUTHN_ORIGIN = 'WebauthnOrigin';
74
+ /** HM-4 (d). */
75
+ export const WEBAUTHN_AUTHENTICATOR_DATA = 'WebauthnAuthenticatorData';
76
+ /** HM-4 (e). */
77
+ export const WEBAUTHN_SIGNATURE = 'WebauthnSignature';
78
+ /**
79
+ * One HM-4 step refused, named.
80
+ *
81
+ * `name` is the STEP and is what the cross-implementation comparison is on; the
82
+ * clause is HM-4 for all of them, and six failures under one clause id would be
83
+ * one name for six objects. Deliberately NOT a {@link import('./refusal.js').Refusal}:
84
+ * `quorum.ts` maps these to §9.3's vocabulary at its own boundary, and a type
85
+ * that could travel straight out would let an unmapped name reach a caller.
86
+ */
87
+ export class WebauthnRefusal extends Error {
88
+ refusalName;
89
+ detail;
90
+ constructor(refusalName, detail) {
91
+ super(`${refusalName}: ${detail}`);
92
+ this.name = 'WebauthnRefusal';
93
+ this.refusalName = refusalName;
94
+ this.detail = detail;
95
+ }
96
+ }
97
+ // COSE_Key labels (RFC 9052 §7). NAMED, because `1` and `-2` mean nothing at a
98
+ // call site and a transposed pair of integers is a defect no reader would see.
99
+ const COSE_KTY = 1n;
100
+ const COSE_CRV = -1n;
101
+ const COSE_X = -2n;
102
+ const COSE_Y = -3n;
103
+ /**
104
+ * HM-4 (d): the flags byte of `authenticator_data`. UP is "a person touched the
105
+ * authenticator", UV is "the authenticator verified WHO". Both required -- UP
106
+ * alone is possession, and HM-1 says possession is not a person.
107
+ */
108
+ const AD_FLAG_UP = 0x01;
109
+ const AD_FLAG_UV = 0x04;
110
+ /** 32 bytes rpIdHash + 1 flags + 4 counter. */
111
+ const AD_MIN_LEN = 37;
112
+ /**
113
+ * The FIXED shape of a credential's public key, per `alg`.
114
+ *
115
+ * EXACTLY THESE LABELS AND NOTHING ELSE, which is stricter than "whatever the
116
+ * authenticator returned" and is taken deliberately: an optional label gives one
117
+ * key two canonical encodings, hence two `public_key` strings for one
118
+ * credential, and PB-7's distinctness -- which HM-5 takes over exactly that
119
+ * string -- is a comparison over it. Two spellings of one credential enrolled
120
+ * under two names is one holder satisfying k=2 alone.
121
+ *
122
+ * What that costs, disclosed rather than buried: a real authenticator's
123
+ * COSE_Key also carries label 3 (`alg`), so the enrolling service must drop it
124
+ * before writing the registry entry. The registry's own `alg` field is the one
125
+ * definition of which key type HM-4 (e) verifies under, and a second copy inside
126
+ * the key bytes is a second definition CR-5 does not cover. The engine reports
127
+ * this as a spec wording problem; this implementation inherits the reading
128
+ * rather than taking a different one.
129
+ */
130
+ const COSE_SHAPES = {
131
+ 'webauthn-es256': { kty: 2n, crv: 1n, labels: [COSE_KTY, COSE_CRV, COSE_X, COSE_Y] },
132
+ 'webauthn-ed25519': { kty: 1n, crv: 6n, labels: [COSE_KTY, COSE_CRV, COSE_X] },
133
+ };
134
+ function sortedLabelText(labels) {
135
+ return labels
136
+ .map((l) => (typeof l === 'bigint' ? l.toString() : JSON.stringify(l)))
137
+ .sort()
138
+ .join(',');
139
+ }
140
+ /**
141
+ * HM-1 / HM-5: decode a credential's COSE_Key.
142
+ *
143
+ * The decode is CANONICAL AND VALIDATING (AT-8a) because the registry entry is
144
+ * signed policy: a key that decodes from two different byte strings is a
145
+ * credential with two names.
146
+ *
147
+ * WEAKNESS IS REFUSED HERE, at enrolment, not left to the verifier: an ES256
148
+ * point off the curve and an Ed25519 point of small order are both
149
+ * `RegistryKeyWeak` (HM-5, PB-9). The curve-membership question is asked of
150
+ * `p256`'s own construction -- the same one the verifier will use -- rather than
151
+ * of a second copy of the curve equation, which would be a second definition of
152
+ * the curve. {@link ed25519IsSmallOrder} is the predicate `verifyEd25519Strict`
153
+ * already applies: one rule, two callers (ACP-109).
154
+ */
155
+ export function coseKeyParse(alg, raw) {
156
+ const shape = COSE_SHAPES[alg];
157
+ if (shape === undefined) {
158
+ throw new WebauthnRefusal(MALFORMED, `no WebAuthn alg ${JSON.stringify(alg)}`);
159
+ }
160
+ let key;
161
+ try {
162
+ key = decodeCanonical(raw);
163
+ }
164
+ catch (e) {
165
+ const why = e instanceof Error ? e.message : 'not canonical CBOR';
166
+ throw new WebauthnRefusal(MALFORMED, `COSE_Key: ${why}`);
167
+ }
168
+ if (!cborIsMap(key))
169
+ throw new WebauthnRefusal(MALFORMED, 'COSE_Key is not a map');
170
+ // Compared as SETS. The decoder has already refused a duplicate key and a key
171
+ // out of canonical order, so what is left is membership -- and an extra label
172
+ // and a missing one are one refusal, because "this is not the key shape" is
173
+ // one fact.
174
+ const got = sortedLabelText(cborKeys(key));
175
+ const want = sortedLabelText([...shape.labels]);
176
+ if (got !== want) {
177
+ throw new WebauthnRefusal(MALFORMED, `COSE_Key labels {${got}} are not {${want}}`);
178
+ }
179
+ if (cborGetInt(key, COSE_KTY) !== shape.kty || cborGetInt(key, COSE_CRV) !== shape.crv) {
180
+ throw new WebauthnRefusal(MALFORMED, `COSE_Key kty/crv is not ${shape.kty}/${shape.crv} for ${alg}`);
181
+ }
182
+ const coord = (label) => {
183
+ const b = cborAsBytes(cborGetInt(key, label));
184
+ if (b === null)
185
+ throw new WebauthnRefusal(MALFORMED, 'COSE_Key coordinate is not bytes');
186
+ // Fixed width, short encodings forbidden: COSE writes P-256 and Ed25519
187
+ // coordinates at 32 bytes, and a short encoding is a second spelling of one
188
+ // integer.
189
+ if (b.length !== 32) {
190
+ throw new WebauthnRefusal(MALFORMED, 'COSE_Key coordinate is not 32 bytes');
191
+ }
192
+ return b;
193
+ };
194
+ if (alg === 'webauthn-es256') {
195
+ const x = coord(COSE_X);
196
+ const y = coord(COSE_Y);
197
+ // SEC1 uncompressed, which is what `p256` decodes and what the verifier
198
+ // below hands it: one encoding of the point, built once.
199
+ const sec1 = new Uint8Array(65);
200
+ sec1[0] = 0x04;
201
+ sec1.set(x, 1);
202
+ sec1.set(y, 33);
203
+ if (!p256.utils.isValidPublicKey(sec1, false)) {
204
+ throw new WebauthnRefusal(REGISTRY_KEY_WEAK, 'ES256 public key is not a point on secp256r1');
205
+ }
206
+ return { kind: 'es256', sec1 };
207
+ }
208
+ const x = coord(COSE_X);
209
+ if (x.length !== ED25519_PK_LEN || ed25519IsSmallOrder(x)) {
210
+ // PB-9 / ACP-106, one clause over: under a small-order key the Ed25519
211
+ // equation stops mentioning the message and ONE signature verifies every
212
+ // message. Enrolled as a human approver, that identity is a forgery oracle
213
+ // for anyone who has seen the public key.
214
+ throw new WebauthnRefusal(REGISTRY_KEY_WEAK, 'Ed25519 credential key is of small order');
215
+ }
216
+ return { kind: 'ed25519', raw: x };
217
+ }
218
+ /**
219
+ * HM-3: the `webauthn` primitive's value, decoded under a FIXED SHAPE.
220
+ *
221
+ * A general CBOR decoder is the wrong tool and is deliberately not offered.
222
+ * What arrives here is attacker-supplied on the transport path, so the decoder
223
+ * must accept exactly one shape -- three text keys, each a byte string -- and
224
+ * refuse everything else as `Malformed`. A tag, a float, a nested map, an extra
225
+ * key and a missing key are all the same answer, because a decoder that reports
226
+ * them differently is a parser an attacker can interrogate.
227
+ */
228
+ export function decodeAssertion(raw) {
229
+ let obj;
230
+ try {
231
+ obj = decodeCanonical(raw);
232
+ }
233
+ catch (e) {
234
+ const why = e instanceof Error ? e.message : 'not canonical CBOR';
235
+ throw new WebauthnRefusal(MALFORMED, `assertion: ${why}`);
236
+ }
237
+ if (!cborIsMap(obj) || sortedLabelText(cborKeys(obj)) !== sortedLabelText([...ASSERTION_FIELDS])) {
238
+ throw new WebauthnRefusal(MALFORMED, 'assertion is not the HM-3 map');
239
+ }
240
+ const field = (name) => {
241
+ const b = cborAsBytes(cborGetText(obj, name));
242
+ if (b === null) {
243
+ throw new WebauthnRefusal(MALFORMED, `assertion ${name} is not a byte string`);
244
+ }
245
+ return b;
246
+ };
247
+ return {
248
+ authenticatorData: field('authenticator_data'),
249
+ clientDataJson: field('client_data_json'),
250
+ signature: field('signature'),
251
+ };
252
+ }
253
+ /**
254
+ * HM-2: the challenge is base64url WITHOUT padding of the message's bytes.
255
+ *
256
+ * ONE definition, so "the challenge encoding" cannot mean two things. Note what
257
+ * the verifier does with it: it RECOMPUTES this from an id it derived itself and
258
+ * compares (RES-8). It never decodes the challenge the assertion carries and
259
+ * treats the result as an identifier -- a transmitted id is a name for a
260
+ * binding, not evidence of one.
261
+ */
262
+ export function webauthnChallenge(messageIdBytes) {
263
+ const A = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
264
+ let s = '';
265
+ for (let i = 0; i < messageIdBytes.length; i += 3) {
266
+ const b0 = messageIdBytes[i] ?? 0;
267
+ const b1 = messageIdBytes[i + 1];
268
+ const b2 = messageIdBytes[i + 2];
269
+ const v = (b0 << 16) | ((b1 ?? 0) << 8) | (b2 ?? 0);
270
+ s += A[(v >> 18) & 63];
271
+ s += A[(v >> 12) & 63];
272
+ if (b1 !== undefined)
273
+ s += A[(v >> 6) & 63];
274
+ if (b2 !== undefined)
275
+ s += A[v & 63];
276
+ }
277
+ return s;
278
+ }
279
+ function sha256(bytes) {
280
+ return Uint8Array.from(createHash('sha256').update(bytes).digest());
281
+ }
282
+ function bytesEqual(a, b) {
283
+ return a.length === b.length && a.every((x, i) => x === b[i]);
284
+ }
285
+ function strProp(v, key) {
286
+ if (typeof v !== 'object' || v === null || Array.isArray(v))
287
+ return null;
288
+ const raw = Object.prototype.hasOwnProperty.call(v, key)
289
+ ? Object.getOwnPropertyDescriptor(v, key)?.value
290
+ : undefined;
291
+ return typeof raw === 'string' ? raw : null;
292
+ }
293
+ /**
294
+ * HM-4 (a)-(e), IN ORDER, returning the assertion's 32-bit signature counter.
295
+ *
296
+ * `messageIdBytes` is the UTF-8 bytes of a **recomputed** id -- the attestation
297
+ * id at §9.3 step 7b(v). Never an id read off the message being verified
298
+ * (HM-2, RES-8). `rpId` is the entry's SIGNED relying-party id: signed policy,
299
+ * never a value the assertion supplies, which is the whole of HM-4 (c).
300
+ *
301
+ * NO STEP IS SKIPPED FOR AN EARLIER ONE, and the order is normative: an
302
+ * assertion wrong in two ways must produce the same refusal name in every
303
+ * implementation, or the corpus is comparing luck.
304
+ */
305
+ export function verifyWebauthn(alg, coseKey, messageIdBytes, rpId, assertion) {
306
+ const key = coseKeyParse(alg, coseKey);
307
+ const a = decodeAssertion(assertion);
308
+ // (a) A parse failure and a wrong `type` are ONE refusal, because the clause
309
+ // states them as one condition: what it demands is a `webauthn.get`
310
+ // assertion, and bytes that are not JSON are not one. A `webauthn.create`
311
+ // document here is a REGISTRATION ceremony replayed as an approval -- the
312
+ // person consented to enrolling a key, not to the action.
313
+ let client;
314
+ try {
315
+ client = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(a.clientDataJson));
316
+ }
317
+ catch {
318
+ throw new WebauthnRefusal(WEBAUTHN_TYPE, 'HM-4 (a): clientDataJSON is not JSON');
319
+ }
320
+ const type = strProp(client, 'type');
321
+ if (type !== 'webauthn.get') {
322
+ throw new WebauthnRefusal(WEBAUTHN_TYPE, `HM-4 (a): clientDataJSON type is ${JSON.stringify(type)}, not 'webauthn.get'`);
323
+ }
324
+ // (b) HM-4 (b) / HM-2. RECOMPUTED and compared; never read as a value.
325
+ if (strProp(client, 'challenge') !== webauthnChallenge(messageIdBytes)) {
326
+ throw new WebauthnRefusal(WEBAUTHN_CHALLENGE, 'HM-4 (b): the assertion answers a different challenge than the recomputed id');
327
+ }
328
+ // (c) EXACTLY `https://` + the entry's signed `rp_id`, and nothing else -- no
329
+ // port, no path, no scheme substitution. An assertion made for another origin
330
+ // is an assertion made on another SITE, which is the entire phishing case
331
+ // this binding exists to refuse: the person really did touch their key, on a
332
+ // page an attacker served.
333
+ const wantOrigin = `https://${rpId}`;
334
+ const origin = strProp(client, 'origin');
335
+ if (origin !== wantOrigin) {
336
+ throw new WebauthnRefusal(WEBAUTHN_ORIGIN, `HM-4 (c): origin ${JSON.stringify(origin)} is not ${JSON.stringify(wantOrigin)}`);
337
+ }
338
+ // (d) Length, rpIdHash and the two flags are one refusal name because they
339
+ // are one question: is this authenticator data for this relying party,
340
+ // produced with the person present and verified? UV is the half that makes
341
+ // the level AT-10 records mean anything -- without it the entry is possession
342
+ // of a device, and HM-1 says possession is not a person.
343
+ if (a.authenticatorData.length < AD_MIN_LEN) {
344
+ throw new WebauthnRefusal(WEBAUTHN_AUTHENTICATOR_DATA, `HM-4 (d): authenticator_data is ${a.authenticatorData.length} bytes, under the ${AD_MIN_LEN}-byte minimum`);
345
+ }
346
+ if (!bytesEqual(a.authenticatorData.subarray(0, 32), sha256(new TextEncoder().encode(rpId)))) {
347
+ throw new WebauthnRefusal(WEBAUTHN_AUTHENTICATOR_DATA, "HM-4 (d): rpIdHash is not SHA-256 of the entry's rp_id");
348
+ }
349
+ const flags = a.authenticatorData[32] ?? 0;
350
+ if ((flags & AD_FLAG_UP) === 0) {
351
+ throw new WebauthnRefusal(WEBAUTHN_AUTHENTICATOR_DATA, 'HM-4 (d): the user-present flag is clear');
352
+ }
353
+ if ((flags & AD_FLAG_UV) === 0) {
354
+ throw new WebauthnRefusal(WEBAUTHN_AUTHENTICATOR_DATA, 'HM-4 (d): the user-verified flag is clear');
355
+ }
356
+ // (e) The signed message is the authenticator's own bytes followed by the
357
+ // hash of the client data -- NOT the challenge, and not the id. That is what
358
+ // binds (a)-(d) to the signature: tamper with the origin and the client-data
359
+ // hash moves, so an attacker who wants to pass (c) must re-sign, which needs
360
+ // the credential.
361
+ const signed = new Uint8Array(a.authenticatorData.length + 32);
362
+ signed.set(a.authenticatorData, 0);
363
+ signed.set(sha256(a.clientDataJson), a.authenticatorData.length);
364
+ if (key.kind === 'es256') {
365
+ let ok;
366
+ try {
367
+ // DER, as WebAuthn's ES256 is defined and as every authenticator emits.
368
+ // The fixed-width form is a different encoding of the same pair and is
369
+ // NOT accepted: two encodings of one signature is two entry digests for
370
+ // one human decision (AB-1).
371
+ //
372
+ // `lowS: false` is a PIN, not a default left alone. noble refuses a
373
+ // high-S signature unless told otherwise; neither engine side does --
374
+ // RustCrypto's `VerifyingKey::verify` and `cryptography`'s
375
+ // `ECDSA(SHA256())` both accept one -- so leaving noble's default in
376
+ // place would make this verifier refuse an assertion the engine accepts.
377
+ // A guard on one side only converts a closed defect into a divergence
378
+ // (ACP-106's lesson, one primitive over), and malleability buys an
379
+ // attacker nothing here: the receipt commits to the exact entry bytes
380
+ // under AB-1, so a re-encoded signature is a different entry the receipt
381
+ // never authorised.
382
+ ok = p256.verify(a.signature, signed, key.sec1, {
383
+ format: 'der',
384
+ prehash: true,
385
+ lowS: false,
386
+ });
387
+ }
388
+ catch {
389
+ ok = false;
390
+ }
391
+ if (!ok) {
392
+ throw new WebauthnRefusal(WEBAUTHN_SIGNATURE, 'HM-4 (e): ES256 assertion signature is not DER, or does not verify');
393
+ }
394
+ }
395
+ else {
396
+ // ACP-106's strict rule, on the human leg. `coseKeyParse` has already
397
+ // refused a small-order public key; `verifyEd25519Strict` refuses a
398
+ // small-order `R` as well -- the half that lets a signer emit a signature a
399
+ // non-strict verifier accepts over a message it never committed to. The two
400
+ // halves are one rule, and a human leg verified with only half of it would
401
+ // be the ACP-106 defect reopened for the one signature class where the
402
+ // signer is a person.
403
+ if (a.signature.length !== ED25519_SIG_LEN ||
404
+ !verifyEd25519Strict(key.raw, signed, a.signature)) {
405
+ throw new WebauthnRefusal(WEBAUTHN_SIGNATURE, 'HM-4 (e): Ed25519 assertion signature is malformed, non-canonical (ACP-106) or does not verify');
406
+ }
407
+ }
408
+ // HM-4 (f)'s INPUT. Big-endian, bytes 33..37, per the WebAuthn
409
+ // authenticator-data layout. The comparison against the stored value is the
410
+ // caller's -- see the module header on why it is not here.
411
+ const c = a.authenticatorData;
412
+ return (((c[33] ?? 0) << 24) | ((c[34] ?? 0) << 16) | ((c[35] ?? 0) << 8) | (c[36] ?? 0)) >>> 0;
413
+ }
414
+ //# sourceMappingURL=webauthn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webauthn.js","sourceRoot":"","sources":["../src/webauthn.ts"],"names":[],"mappings":"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"}
package/dist/wire.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Wire-value predicates shared by the receipt gate and the §9.3 step 7b quorum.
3
+ *
4
+ * They live here rather than in `verify.ts` for one reason: `quorum.ts` needs
5
+ * every one of them, `verify.ts` imports `quorum.ts`, and a second copy in the
6
+ * quorum would be two definitions of WE-4 in one package -- the encoding-split
7
+ * defect at the source level, in the exact rule whose whole point is that one
8
+ * nonce has one spelling. `verify.ts` re-exports these so the published surface
9
+ * is unchanged.
10
+ */
11
+ /** The one wire nonce size, 128-bit (`Nonce128`, cited by AT-1 and L-17). */
12
+ export declare const NONCE128_BYTES = 16;
13
+ /**
14
+ * `"b64:"` plus base64 of 16 bytes: 4 + 24.
15
+ *
16
+ * Counted, never decoded -- a rule whose behaviour on bad input is "throw" is
17
+ * not a refusal, and the reference learned that the hard way (its first
18
+ * spelling decoded the body, which raises on the URL-safe alphabet).
19
+ */
20
+ export declare const NONCE128_LEN: number;
21
+ /**
22
+ * WE-4: is this the ASCII string `b64:` followed by RFC 4648 §4 base64, WITH
23
+ * padding? Ported from `quorum.rs::is_we4_b64` -- the body is whole
24
+ * four-character groups, the alphabet is §4's (`+` and `/`, never §5's `-` and
25
+ * `_`), and `=` appears only as one or two characters at the very end.
26
+ *
27
+ * Rejected, never normalised: normalising would make this verifier accept two
28
+ * spellings of one value, which is how one nonce comes to hold two ledger slots
29
+ * (ACP-87) and how one attestation comes to hold two ids.
30
+ */
31
+ export declare function isWe4B64(s: string): boolean;
32
+ /** A parsed JSON object, narrowed by a predicate rather than an `as` cast. */
33
+ export declare function isRecord(v: unknown): v is Record<string, unknown>;
34
+ /** A string field of a parsed object, or null -- never a coerced value. */
35
+ export declare function strField(v: Record<string, unknown>, key: string): string | null;
36
+ /**
37
+ * RFC 4648 §4 base64 with padding, decoded. The carrier WE-4 declares.
38
+ *
39
+ * Returns null rather than throwing, and is called only after
40
+ * {@link isWe4B64} has accepted the string: the type check is the refusal, and
41
+ * this is the decode that follows it.
42
+ */
43
+ export declare function b64Decode(s: string): Uint8Array | null;
44
+ //# sourceMappingURL=wire.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../src/wire.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,6EAA6E;AAC7E,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,QAAwC,CAAC;AAElE;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAc3C;AAED,8EAA8E;AAC9E,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEjE;AAED,2EAA2E;AAC3E,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG/E;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAoBtD"}
package/dist/wire.js ADDED
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Wire-value predicates shared by the receipt gate and the §9.3 step 7b quorum.
3
+ *
4
+ * They live here rather than in `verify.ts` for one reason: `quorum.ts` needs
5
+ * every one of them, `verify.ts` imports `quorum.ts`, and a second copy in the
6
+ * quorum would be two definitions of WE-4 in one package -- the encoding-split
7
+ * defect at the source level, in the exact rule whose whole point is that one
8
+ * nonce has one spelling. `verify.ts` re-exports these so the published surface
9
+ * is unchanged.
10
+ */
11
+ /** The one wire nonce size, 128-bit (`Nonce128`, cited by AT-1 and L-17). */
12
+ export const NONCE128_BYTES = 16;
13
+ /**
14
+ * `"b64:"` plus base64 of 16 bytes: 4 + 24.
15
+ *
16
+ * Counted, never decoded -- a rule whose behaviour on bad input is "throw" is
17
+ * not a refusal, and the reference learned that the hard way (its first
18
+ * spelling decoded the body, which raises on the URL-safe alphabet).
19
+ */
20
+ export const NONCE128_LEN = 4 + Math.ceil(NONCE128_BYTES / 3) * 4;
21
+ /**
22
+ * WE-4: is this the ASCII string `b64:` followed by RFC 4648 §4 base64, WITH
23
+ * padding? Ported from `quorum.rs::is_we4_b64` -- the body is whole
24
+ * four-character groups, the alphabet is §4's (`+` and `/`, never §5's `-` and
25
+ * `_`), and `=` appears only as one or two characters at the very end.
26
+ *
27
+ * Rejected, never normalised: normalising would make this verifier accept two
28
+ * spellings of one value, which is how one nonce comes to hold two ledger slots
29
+ * (ACP-87) and how one attestation comes to hold two ids.
30
+ */
31
+ export function isWe4B64(s) {
32
+ if (!s.startsWith('b64:'))
33
+ return false;
34
+ const body = s.slice(4);
35
+ if (body.length % 4 !== 0)
36
+ return false;
37
+ let pad = 0;
38
+ for (let i = body.length - 1; i >= 0 && body[i] === '='; i -= 1)
39
+ pad += 1;
40
+ if (pad > 2)
41
+ return false;
42
+ for (let i = 0; i < body.length - pad; i += 1) {
43
+ const c = body.charCodeAt(i);
44
+ const alnum = (c >= 0x30 && c <= 0x39) || (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a);
45
+ if (!alnum && c !== 0x2b /* + */ && c !== 0x2f /* / */)
46
+ return false;
47
+ }
48
+ return true;
49
+ }
50
+ /** A parsed JSON object, narrowed by a predicate rather than an `as` cast. */
51
+ export function isRecord(v) {
52
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
53
+ }
54
+ /** A string field of a parsed object, or null -- never a coerced value. */
55
+ export function strField(v, key) {
56
+ const raw = v[key];
57
+ return typeof raw === 'string' ? raw : null;
58
+ }
59
+ /**
60
+ * RFC 4648 §4 base64 with padding, decoded. The carrier WE-4 declares.
61
+ *
62
+ * Returns null rather than throwing, and is called only after
63
+ * {@link isWe4B64} has accepted the string: the type check is the refusal, and
64
+ * this is the decode that follows it.
65
+ */
66
+ export function b64Decode(s) {
67
+ const A = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
68
+ if (s.length === 0 || s.length % 4 !== 0)
69
+ return null;
70
+ let pad = 0;
71
+ while (pad < 2 && s[s.length - 1 - pad] === '=')
72
+ pad += 1;
73
+ const out = [];
74
+ let acc = 0;
75
+ let bits = 0;
76
+ for (let i = 0; i < s.length - pad; i += 1) {
77
+ const v = A.indexOf(s[i] ?? '');
78
+ if (v < 0)
79
+ return null;
80
+ acc = (acc << 6) | v;
81
+ bits += 6;
82
+ if (bits >= 8) {
83
+ bits -= 8;
84
+ out.push((acc >> bits) & 0xff);
85
+ acc &= (1 << bits) - 1;
86
+ }
87
+ }
88
+ return Uint8Array.from(out);
89
+ }
90
+ //# sourceMappingURL=wire.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire.js","sourceRoot":"","sources":["../src/wire.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,14 +1,20 @@
1
1
  {
2
2
  "name": "@ziffer-io/verify",
3
- "version": "0.1.0",
4
- "description": "ZIFFER receipt verification: the stateless half of the ACP 9.3 checklist, refusals named by clause.",
3
+ "version": "0.2.0",
4
+ "description": "Verify a ZIFFER decision receipt in your own process, against a key you configured.",
5
+ "keywords": [
6
+ "ziffer",
7
+ "agent",
8
+ "ai-agent",
9
+ "guardrail",
10
+ "approval",
11
+ "receipt"
12
+ ],
5
13
  "author": "code75 SASU",
6
14
  "license": "SEE LICENSE IN LICENSE",
7
- "comment-license": "The SDK is proprietary (code75 SASU). \"license\" is the SPDX escape hatch for exactly this case: there is no SPDX identifier for these terms, so the field points at the file that states them, and LICENSE ships in the tarball beside THIRD-PARTY-NOTICES. Do not put an OSI identifier here -- Apache-2.0 stood in these four files until ACP-214 and was wrong the whole time.",
8
15
  "type": "module",
9
16
  "main": "./dist/index.js",
10
17
  "types": "./dist/index.d.ts",
11
- "comment-exports": "The `./dist/*` entry is not laziness and removing it breaks a caller. `exports` is a gate as well as a map: declaring only `.` makes every other path unreachable, and packages/mcp/src/tools.test.ts imports `@ziffer-io/verify/dist/testkeys.js` ON PURPOSE -- testkeys is deliberately absent from index.ts so it cannot be reached by accident, and the deep import is how the MCP package's tests get a signing key without a second definition of one identity. That import resolves today because there is no exports map; adding a `.`-only one would break it at publish time, which is the class of defect an exports map introduces rather than prevents. So the dist is declared addressable, and the reason is here rather than in a reviewer's memory.",
12
18
  "exports": {
13
19
  ".": {
14
20
  "types": "./dist/index.d.ts",
@@ -34,12 +40,9 @@
34
40
  "publishConfig": {
35
41
  "access": "public"
36
42
  },
37
- "comment-no-provenance": "There is deliberately no \"provenance\": true here, and it was removed rather than never added. npm generates a provenance attestation only from a recognised CI runner, and its documentation states plainly that provenance is NOT SUPPORTED for private repositories (docs.npmjs.com/trusted-publishers, read 2026-09-03) -- ziffer-hq/ziffer is private (`gh api` says so). Setting the flag does not degrade to a warning: npm attempts the attestation and the publish FAILS, so the field would have broken the operator's very first publish from a laptop and every CI publish after it. tools/release-npm.sh asserts the field stays absent, and that assertion is the thing to delete on the day this repository becomes public -- at which point trusted publishing generates provenance on its own, with no flag at all.",
38
43
  "ziffer": {
39
- "enginePin": "fed43d10b427e0a4435a8f04e474334d88a5aa6e"
44
+ "enginePin": "7fd84dbe1a430e6deea4b241ebe1ba641103ffbe"
40
45
  },
41
- "comment-enginePin": "The engine commit whose clause spellings, canon and mirrored corpus this implementation is bound to. A COPY of the rev in Cargo.toml, which is the one authority tools/guard.sh reads; tools/release-npm.sh refuses to release when the two differ, by name. tools/bump-pin.sh does not move this field -- see packages/types/package.json for why and what closing it costs.",
42
- "comment-deps": "Exactly the two signature primitives, at the versions services/approval already pins -- the DR-2 permitted common set minus the wire types, which this package does not consume: it verifies raw parsed JSON, and typing the wire is the client's (@ziffer-io/client) business. Adding anything else needs written justification (ACP-197 runbook section 5).",
43
46
  "dependencies": {
44
47
  "@noble/curves": "2.3.0",
45
48
  "@noble/post-quantum": "0.7.0"
@@ -51,7 +54,7 @@
51
54
  "scripts": {
52
55
  "build": "tsc -b",
53
56
  "typecheck": "tsc -b",
54
- "pretest": "tsc -b && if ! find dist -name '*.test.js' -print -quit | grep -q .; then echo 'ACP-248: no compiled test file under dist/ -- node --test reports 0 tests and exits 0, so this package would go green having run nothing' >&2; exit 1; fi",
57
+ "pretest": "tsc -b && if ! find dist -name '*.test.js' -print -quit | grep -q .; then echo 'no compiled test file under dist/ -- node --test reports 0 tests and exits 0, so this package would go green having run nothing' >&2; exit 1; fi",
55
58
  "test": "tsc -b && node --test \"dist/**/*.test.js\""
56
59
  }
57
60
  }