@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sig.d.ts","sourceRoot":"","sources":["../src/sig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,KAAK,KAAK,EACX,MAAM,YAAY,CAAC;AAEpB,4CAA4C;AAC5C,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC,2CAA2C;AAC3C,eAAO,MAAM,eAAe,OAAO,CAAC;AAEpC,sCAAsC;AACtC,wBAAgB,GAAG,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAI7C;AAED,6EAA6E;AAC7E,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,UAAU,CAgB3C;AAED,0DAA0D;AAC1D,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,EAAE,CA4BnE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,UAAU,EACrB,OAAO,EAAE,UAAU,EACnB,SAAS,EAAE,UAAU,GACpB,gBAAgB,CAWlB;AAED,kFAAkF;AAClF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;IAC/B,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,eAAe,EACpB,OAAO,EAAE,UAAU,EACnB,KAAK,EAAE,SAAS,aAAa,EAAE,GAC9B,WAAW,GAAG,IAAI,CAkBpB"}
|
package/dist/sig.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CR-2 signature carrier and the CR-3 conjunctive verification, shared by
|
|
3
|
+
* the receipt gate (`verify.ts`) and the attestation loop (`quorum.ts`).
|
|
4
|
+
*
|
|
5
|
+
* One home, for the reason `quorum.rs` gives when it calls
|
|
6
|
+
* `crate::decide::parse_sig` rather than writing its own: the shape a signature
|
|
7
|
+
* must have is the shape its SUITE requires, and a second parser is a second
|
|
8
|
+
* answer to "which primitives satisfy this suite". The refusal each function
|
|
9
|
+
* raises is the RECEIPT's clause (`9.3-1`); the quorum catches it and re-raises
|
|
10
|
+
* under `9.3-7b-i`, exactly as the engine does, because a bad attester
|
|
11
|
+
* signature and a bad receipt signature are different findings.
|
|
12
|
+
*/
|
|
13
|
+
import { ml_dsa65 } from '@noble/post-quantum/ml-dsa.js';
|
|
14
|
+
import { CLAUSE_SIGNATURE, CLAUSE_UNKNOWN_SUITE } from './clauses.js';
|
|
15
|
+
import { verifyEd25519Strict } from './ed25519.js';
|
|
16
|
+
import { Refusal } from './refusal.js';
|
|
17
|
+
import { parseSuite, suitePrimitives, verifyHybrid, } from './suite.js';
|
|
18
|
+
/** FIPS 204 ML-DSA-65 public key length. */
|
|
19
|
+
export const MLDSA65_PK_LEN = 1952;
|
|
20
|
+
/** FIPS 204 ML-DSA-65 signature length. */
|
|
21
|
+
export const MLDSA65_SIG_LEN = 3309;
|
|
22
|
+
/** Lowercase hex of a byte string. */
|
|
23
|
+
export function hex(bytes) {
|
|
24
|
+
let out = '';
|
|
25
|
+
for (const b of bytes)
|
|
26
|
+
out += b.toString(16).padStart(2, '0');
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
/** Decode a hex signature leg, refusing exactly as `decide.rs`'s `unhex`. */
|
|
30
|
+
export function unhex(s) {
|
|
31
|
+
if (s.length % 2 !== 0) {
|
|
32
|
+
throw new Refusal(CLAUSE_SIGNATURE, 'signature hex has odd length');
|
|
33
|
+
}
|
|
34
|
+
const out = new Uint8Array(s.length / 2);
|
|
35
|
+
for (let i = 0; i < s.length; i += 2) {
|
|
36
|
+
const byte = Number.parseInt(s.slice(i, i + 2), 16);
|
|
37
|
+
// parseInt would tolerate "0x", whitespace and a lone valid first digit;
|
|
38
|
+
// requiring both chars to be hex keeps this as strict as Rust's
|
|
39
|
+
// from_str_radix over a 2-char slice.
|
|
40
|
+
if (Number.isNaN(byte) || !/^[0-9a-fA-F]{2}$/.test(s.slice(i, i + 2))) {
|
|
41
|
+
throw new Refusal(CLAUSE_SIGNATURE, 'signature is not hex');
|
|
42
|
+
}
|
|
43
|
+
out[i / 2] = byte;
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Parse a wire `sig` object into the parts the CR-3 combiner verifies --
|
|
49
|
+
* `decide.rs::parse_sig`, including its rule that the key set must be EXACTLY
|
|
50
|
+
* the suite's. Three attacks live in the difference: a scalar `sig` (format
|
|
51
|
+
* confusion is a downgrade in disguise), a missing primitive (the stripped
|
|
52
|
+
* leg), and an extra undeclared primitive (an accepted code path the attacker
|
|
53
|
+
* chose). An unknown primitive name is refused rather than dropped -- a dropped
|
|
54
|
+
* part would let a suite be satisfied by the parts that remain.
|
|
55
|
+
*/
|
|
56
|
+
export function parseSig(sig, alg) {
|
|
57
|
+
// CR-1 first: an unparseable suite has no primitive set at all, so asking
|
|
58
|
+
// which primitives it requires would be a category error.
|
|
59
|
+
const suite = parseSuite(alg);
|
|
60
|
+
if (suite === null) {
|
|
61
|
+
throw new Refusal(CLAUSE_UNKNOWN_SUITE, 'unknown signature suite');
|
|
62
|
+
}
|
|
63
|
+
if (typeof sig !== 'object' || sig === null || Array.isArray(sig)) {
|
|
64
|
+
throw new Refusal(CLAUSE_SIGNATURE, 'signature is not a per-primitive object');
|
|
65
|
+
}
|
|
66
|
+
const map = { ...sig };
|
|
67
|
+
const required = suitePrimitives(suite);
|
|
68
|
+
const keys = Object.keys(map);
|
|
69
|
+
if (keys.length !== required.length || !required.every((p) => keys.includes(p))) {
|
|
70
|
+
throw new Refusal(CLAUSE_SIGNATURE, 'signature primitives are not exactly those the declared suite requires');
|
|
71
|
+
}
|
|
72
|
+
const parts = [];
|
|
73
|
+
for (const primitive of required) {
|
|
74
|
+
const value = map[primitive];
|
|
75
|
+
if (typeof value !== 'string') {
|
|
76
|
+
throw new Refusal(CLAUSE_SIGNATURE, 'signature value is not a hex string');
|
|
77
|
+
}
|
|
78
|
+
parts.push({ primitive, bytes: unhex(value) });
|
|
79
|
+
}
|
|
80
|
+
return parts;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Verify one ML-DSA-65 signature. Never throws; length checks first, exactly as
|
|
84
|
+
* `primitives.rs` -- a malformed input is `invalid`, never `unsupported`: one is
|
|
85
|
+
* a statement about the signature, the other about this build, and reporting one
|
|
86
|
+
* as the other sends the investigation to the wrong place.
|
|
87
|
+
*/
|
|
88
|
+
export function verifyMlDsa65(publicKey, message, signature) {
|
|
89
|
+
if (publicKey.length !== MLDSA65_PK_LEN || signature.length !== MLDSA65_SIG_LEN) {
|
|
90
|
+
return 'invalid';
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
// Empty FIPS 204 context, matching both engine sides (MLDSA_CTX = &[]):
|
|
94
|
+
// a context the signer did not use is a different message.
|
|
95
|
+
return ml_dsa65.verify(signature, message, publicKey) ? 'valid' : 'invalid';
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return 'invalid';
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* CR-3 over a declared suite: every primitive the suite names must be present
|
|
103
|
+
* and must verify. Returns null on success, the `HybridError` name on refusal.
|
|
104
|
+
*
|
|
105
|
+
* The suite is a PARAMETER, exactly as in Rust: a caller that decides how many
|
|
106
|
+
* primitives a hybrid signature needs can be persuaded to decide "one".
|
|
107
|
+
*/
|
|
108
|
+
export function verifyUnderSuite(suite, key, message, parts) {
|
|
109
|
+
const verdicts = parts.map((part) => {
|
|
110
|
+
switch (part.primitive) {
|
|
111
|
+
case 'classical':
|
|
112
|
+
return [
|
|
113
|
+
part.primitive,
|
|
114
|
+
verifyEd25519Strict(key.classical, message, part.bytes) ? 'valid' : 'invalid',
|
|
115
|
+
];
|
|
116
|
+
case 'pq':
|
|
117
|
+
return [part.primitive, verifyMlDsa65(key.pq, message, part.bytes)];
|
|
118
|
+
case 'pq-slh':
|
|
119
|
+
// Declared, not implemented. Never a pass, and never silently dropped
|
|
120
|
+
// from the set either -- dropping it would let a suite naming it be
|
|
121
|
+
// satisfied by the primitives that remain.
|
|
122
|
+
return [part.primitive, 'unsupported'];
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
return verifyHybrid(suite, verdicts);
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=sig.js.map
|
package/dist/sig.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sig.js","sourceRoot":"","sources":["../src/sig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EACL,UAAU,EACV,eAAe,EACf,YAAY,GAKb,MAAM,YAAY,CAAC;AAEpB,4CAA4C;AAC5C,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AACnC,2CAA2C;AAC3C,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AAEpC,sCAAsC;AACtC,MAAM,UAAU,GAAG,CAAC,KAAiB;IACnC,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,KAAK;QAAE,GAAG,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC9D,OAAO,GAAG,CAAC;AACb,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,KAAK,CAAC,CAAS;IAC7B,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,OAAO,CAAC,gBAAgB,EAAE,8BAA8B,CAAC,CAAC;IACtE,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACpD,yEAAyE;QACzE,gEAAgE;QAChE,sCAAsC;QACtC,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACtE,MAAM,IAAI,OAAO,CAAC,gBAAgB,EAAE,sBAAsB,CAAC,CAAC;QAC9D,CAAC;QACD,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC;IACpB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAQD;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAY,EAAE,GAAW;IAChD,0EAA0E;IAC1E,0DAA0D;IAC1D,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,OAAO,CAAC,oBAAoB,EAAE,yBAAyB,CAAC,CAAC;IACrE,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,OAAO,CAAC,gBAAgB,EAAE,yCAAyC,CAAC,CAAC;IACjF,CAAC;IACD,MAAM,GAAG,GAA4B,EAAE,GAAG,GAAG,EAAE,CAAC;IAChD,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAChF,MAAM,IAAI,OAAO,CACf,gBAAgB,EAChB,wEAAwE,CACzE,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAoB,EAAE,CAAC;IAClC,KAAK,MAAM,SAAS,IAAI,QAAQ,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC;QAC7B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,IAAI,OAAO,CAAC,gBAAgB,EAAE,qCAAqC,CAAC,CAAC;QAC7E,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,SAAqB,EACrB,OAAmB,EACnB,SAAqB;IAErB,IAAI,SAAS,CAAC,MAAM,KAAK,cAAc,IAAI,SAAS,CAAC,MAAM,KAAK,eAAe,EAAE,CAAC;QAChF,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,CAAC;QACH,wEAAwE;QACxE,2DAA2D;QAC3D,OAAO,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAQD;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAAY,EACZ,GAAoB,EACpB,OAAmB,EACnB,KAA+B;IAE/B,MAAM,QAAQ,GAA+C,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QAC9E,QAAQ,IAAI,CAAC,SAAS,EAAE,CAAC;YACvB,KAAK,WAAW;gBACd,OAAO;oBACL,IAAI,CAAC,SAAS;oBACd,mBAAmB,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;iBAC9E,CAAC;YACJ,KAAK,IAAI;gBACP,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YACtE,KAAK,QAAQ;gBACX,sEAAsE;gBACtE,oEAAoE;gBACpE,2CAA2C;gBAC3C,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AACvC,CAAC"}
|
package/dist/testkeys.d.ts
CHANGED
|
@@ -17,4 +17,37 @@ export interface TestHybridKey {
|
|
|
17
17
|
readonly pqSecret: Uint8Array;
|
|
18
18
|
}
|
|
19
19
|
export declare function hybridKeyFromSeed(seedText: string): TestHybridKey;
|
|
20
|
+
/** One enrolled credential and the private half a real one would never expose. */
|
|
21
|
+
export interface TestCredential {
|
|
22
|
+
readonly alg: string;
|
|
23
|
+
readonly rpId: string;
|
|
24
|
+
readonly credentialId: string;
|
|
25
|
+
/** The COSE_Key bytes a registry entry carries (HM-1). */
|
|
26
|
+
readonly coseKey: Uint8Array;
|
|
27
|
+
readonly secret: Uint8Array;
|
|
28
|
+
}
|
|
29
|
+
/** Enrol a test credential, deterministically, from a seed string. */
|
|
30
|
+
export declare function webauthnCredentialFromSeed(seedText: string, alg: string, rpId: string): TestCredential;
|
|
31
|
+
/** What a test may bend, one knob per HM-4 step, so a break is one argument. */
|
|
32
|
+
export interface AssertionOptions {
|
|
33
|
+
/** HM-4 (f)'s counter, big-endian in `authenticator_data`. */
|
|
34
|
+
readonly counter?: number;
|
|
35
|
+
/** HM-4 (a): a `webauthn.create` document is a registration replayed. */
|
|
36
|
+
readonly type?: string;
|
|
37
|
+
/** HM-4 (c): the page the person actually touched their key on. */
|
|
38
|
+
readonly origin?: string;
|
|
39
|
+
/** HM-4 (d): the relying party the authenticator produced data for. */
|
|
40
|
+
readonly rpIdForData?: string;
|
|
41
|
+
/** HM-4 (d): UP and UV, both set by default. */
|
|
42
|
+
readonly flags?: number;
|
|
43
|
+
/** HM-4 (e): sign under this credential instead -- the key-not-enrolled break. */
|
|
44
|
+
readonly signWith?: TestCredential;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* HM-3's `sig.webauthn` value: canonical CBOR of the three fields, `b64:` per
|
|
48
|
+
* WE-4. The challenge is {@link webauthnChallenge} of `messageId`, which the
|
|
49
|
+
* verifier RECOMPUTES -- a test that wrote the challenge in by hand would be
|
|
50
|
+
* asserting against its own copy rather than against HM-2.
|
|
51
|
+
*/
|
|
52
|
+
export declare function signAssertion(cred: TestCredential, messageId: string, opts?: AssertionOptions): string;
|
|
20
53
|
//# sourceMappingURL=testkeys.d.ts.map
|
package/dist/testkeys.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testkeys.d.ts","sourceRoot":"","sources":["../src/testkeys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;IAC/B,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;CAC/B;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAejE"}
|
|
1
|
+
{"version":3,"file":"testkeys.d.ts","sourceRoot":"","sources":["../src/testkeys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;IAC/B,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;CAC/B;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAejE;AAsBD,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;CAC7B;AAUD,sEAAsE;AACtE,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,GACX,cAAc,CA0ChB;AAED,gFAAgF;AAChF,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,yEAAyE;IACzE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,uEAAuE;IACvE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,gDAAgD;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,kFAAkF;IAClF,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC;CACpC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,cAAc,EACpB,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,gBAAqB,GAC1B,MAAM,CAmCR"}
|
package/dist/testkeys.js
CHANGED
|
@@ -25,4 +25,104 @@ export function hybridKeyFromSeed(seedText) {
|
|
|
25
25
|
pqSecret: Uint8Array.from(kp.secretKey),
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
+
// ---------------------------------------------------------------- §8.6c
|
|
29
|
+
//
|
|
30
|
+
// A SOFTWARE AUTHENTICATOR, test-only, mirroring `acp_crypto::webauthn::testing`
|
|
31
|
+
// and `reference/src/acp_webauthn_testing.py`. It exists so `quorum.test.ts` can
|
|
32
|
+
// build a REAL human attestation entry: the mirrored corpus
|
|
33
|
+
// (`fixtures/refused_webauthn.json`) proves the HM-4 steps over the engine's own
|
|
34
|
+
// bytes, and this proves the entry-level composition around them -- HM-3's
|
|
35
|
+
// carrier, the recomputed challenge over an id derived from the object, and the
|
|
36
|
+
// signature counter.
|
|
37
|
+
//
|
|
38
|
+
// The engine keeps its equivalent behind a `testing` feature for one reason and
|
|
39
|
+
// it applies here too: a software authenticator is AS0 under AT-10, it is not a
|
|
40
|
+
// person, and nothing outside a test may reach it. This module is deliberately
|
|
41
|
+
// absent from index.ts.
|
|
42
|
+
import { p256 } from '@noble/curves/nist.js';
|
|
43
|
+
import { cborEncode } from './cbor.js';
|
|
44
|
+
import { webauthnChallenge } from './webauthn.js';
|
|
45
|
+
function b64(bytes) {
|
|
46
|
+
return `b64:${Buffer.from(bytes).toString('base64')}`;
|
|
47
|
+
}
|
|
48
|
+
function coseMap(entries) {
|
|
49
|
+
return cborEncode({ entries });
|
|
50
|
+
}
|
|
51
|
+
/** Enrol a test credential, deterministically, from a seed string. */
|
|
52
|
+
export function webauthnCredentialFromSeed(seedText, alg, rpId) {
|
|
53
|
+
const credentialId = `cred-${seedText}`;
|
|
54
|
+
if (alg === 'webauthn-ed25519') {
|
|
55
|
+
const secret = new Uint8Array(createHash('sha256').update(new TextEncoder().encode(`${seedText}:ed25519`)).digest());
|
|
56
|
+
const pub = Uint8Array.from(ed25519.getPublicKey(secret));
|
|
57
|
+
// {kty: OKP(1), crv: Ed25519(6), x}
|
|
58
|
+
return {
|
|
59
|
+
alg,
|
|
60
|
+
rpId,
|
|
61
|
+
credentialId,
|
|
62
|
+
coseKey: coseMap([
|
|
63
|
+
[1n, 1n],
|
|
64
|
+
[-1n, 6n],
|
|
65
|
+
[-2n, pub],
|
|
66
|
+
]),
|
|
67
|
+
secret,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// ES256. A SHA-256 digest is a valid P-256 scalar with overwhelming
|
|
71
|
+
// probability; the loop is there so "overwhelming" never has to be assumed.
|
|
72
|
+
let secret = new Uint8Array(createHash('sha256').update(new TextEncoder().encode(`${seedText}:es256`)).digest());
|
|
73
|
+
while (!p256.utils.isValidSecretKey(secret)) {
|
|
74
|
+
secret = new Uint8Array(createHash('sha256').update(secret).digest());
|
|
75
|
+
}
|
|
76
|
+
const pub = Uint8Array.from(p256.getPublicKey(secret, false));
|
|
77
|
+
return {
|
|
78
|
+
alg,
|
|
79
|
+
rpId,
|
|
80
|
+
credentialId,
|
|
81
|
+
// {kty: EC2(2), crv: P-256(1), x, y}
|
|
82
|
+
coseKey: coseMap([
|
|
83
|
+
[1n, 2n],
|
|
84
|
+
[-1n, 1n],
|
|
85
|
+
[-2n, pub.slice(1, 33)],
|
|
86
|
+
[-3n, pub.slice(33, 65)],
|
|
87
|
+
]),
|
|
88
|
+
secret,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* HM-3's `sig.webauthn` value: canonical CBOR of the three fields, `b64:` per
|
|
93
|
+
* WE-4. The challenge is {@link webauthnChallenge} of `messageId`, which the
|
|
94
|
+
* verifier RECOMPUTES -- a test that wrote the challenge in by hand would be
|
|
95
|
+
* asserting against its own copy rather than against HM-2.
|
|
96
|
+
*/
|
|
97
|
+
export function signAssertion(cred, messageId, opts = {}) {
|
|
98
|
+
const rpForData = opts.rpIdForData ?? cred.rpId;
|
|
99
|
+
const counter = opts.counter ?? 1;
|
|
100
|
+
const ad = new Uint8Array(37);
|
|
101
|
+
ad.set(new Uint8Array(createHash('sha256').update(new TextEncoder().encode(rpForData)).digest()), 0);
|
|
102
|
+
ad[32] = opts.flags ?? 0x05; // UP | UV
|
|
103
|
+
ad[33] = (counter >>> 24) & 0xff;
|
|
104
|
+
ad[34] = (counter >>> 16) & 0xff;
|
|
105
|
+
ad[35] = (counter >>> 8) & 0xff;
|
|
106
|
+
ad[36] = counter & 0xff;
|
|
107
|
+
const clientData = new TextEncoder().encode(JSON.stringify({
|
|
108
|
+
type: opts.type ?? 'webauthn.get',
|
|
109
|
+
challenge: webauthnChallenge(new TextEncoder().encode(messageId)),
|
|
110
|
+
origin: opts.origin ?? `https://${cred.rpId}`,
|
|
111
|
+
crossOrigin: false,
|
|
112
|
+
}));
|
|
113
|
+
const signed = new Uint8Array(ad.length + 32);
|
|
114
|
+
signed.set(ad, 0);
|
|
115
|
+
signed.set(new Uint8Array(createHash('sha256').update(clientData).digest()), ad.length);
|
|
116
|
+
const signer = opts.signWith ?? cred;
|
|
117
|
+
const signature = signer.alg === 'webauthn-ed25519'
|
|
118
|
+
? Uint8Array.from(ed25519.sign(signed, signer.secret))
|
|
119
|
+
: Uint8Array.from(p256.sign(signed, signer.secret, { format: 'der', prehash: true }));
|
|
120
|
+
return b64(cborEncode({
|
|
121
|
+
entries: [
|
|
122
|
+
['authenticator_data', ad],
|
|
123
|
+
['client_data_json', clientData],
|
|
124
|
+
['signature', signature],
|
|
125
|
+
],
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
28
128
|
//# sourceMappingURL=testkeys.js.map
|
package/dist/testkeys.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testkeys.js","sourceRoot":"","sources":["../src/testkeys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AASzD,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,IAAI,UAAU,CAC7B,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAClF,CAAC;IACF,MAAM,MAAM,GAAG,IAAI,UAAU,CAC3B,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,CACrF,CAAC;IACF,MAAM,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACnC,OAAO;QACL,QAAQ;QACR,SAAS,EAAE,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC1D,EAAE,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC;QACjC,QAAQ,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC;KACxC,CAAC;AACJ,CAAC"}
|
|
1
|
+
{"version":3,"file":"testkeys.js","sourceRoot":"","sources":["../src/testkeys.ts"],"names":[],"mappings":"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"}
|
package/dist/verify.d.ts
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* | 5 | temporal position (9.3-5) and the L-14 window ceiling | yes |
|
|
20
20
|
* | 6 | nonce WE-4 type and L-17 size | yes; the CL-2 single-use CLAIM is ledger state |
|
|
21
21
|
* | 7 | TR-8 / RV-3 recomputation | NO -- needs the signed policy |
|
|
22
|
-
* | 7b | the AT-* quorum, AB-1..AB-4 |
|
|
22
|
+
* | 7b | the AT-* quorum, AB-1..AB-4 | YES, when the anchor carries a `quorum` policy |
|
|
23
23
|
* | 8 | tenant scoping | NO -- needs the bundle's tenant |
|
|
24
24
|
* | 9-10 | capability recheck, delivery identity | NO -- absent in the engine too |
|
|
25
25
|
*
|
|
@@ -28,6 +28,24 @@
|
|
|
28
28
|
* here means "the stateless set found nothing", never "the receipt may be
|
|
29
29
|
* consumed".
|
|
30
30
|
*
|
|
31
|
+
* # Step 7b, and the one thing it still cannot answer (ACP-406 gap 1)
|
|
32
|
+
*
|
|
33
|
+
* Step 7b runs here when {@link TrustAnchor.quorum} is supplied from the
|
|
34
|
+
* caller's own signed bundle -- see `quorum.ts` for the full checklist and its
|
|
35
|
+
* R/B/T table. Two consequences belong in this header rather than only there:
|
|
36
|
+
*
|
|
37
|
+
* - A receipt that CARRIES attestations while the anchor carries no quorum
|
|
38
|
+
* policy is **refused** (`AB-1`), not passed. A verifier that cannot
|
|
39
|
+
* recompute an entry digest has no binding at all, and passing would be the
|
|
40
|
+
* permissive default this package exists to refuse.
|
|
41
|
+
* - **This function still cannot tell that a quorum was REQUIRED.** That is
|
|
42
|
+
* step 7's recomputed floor-only risk, which needs the bundle's grading rules
|
|
43
|
+
* and is not here. A receipt carrying no attestations passes 7b vacuously and
|
|
44
|
+
* {@link Verified.quorum} is `null`, which the caller can read; taking the
|
|
45
|
+
* answer from the receipt's own `risk_level_floor_only` instead would be X1,
|
|
46
|
+
* the defect where a compromised issuer asserting `LOW` suppressed
|
|
47
|
+
* attestation entirely.
|
|
48
|
+
*
|
|
31
49
|
* # R / B / T classification (suite 12) for the inputs this module reads
|
|
32
50
|
*
|
|
33
51
|
* | input | class | why |
|
|
@@ -38,6 +56,9 @@
|
|
|
38
56
|
* | `alg`, `sig`, `decision`, temporal fields, `nonce`, `receipt_version` | B | inside (or selecting) the signed body; each is read only in a position where reading it can at most cause a refusal |
|
|
39
57
|
* | `nowUnixSeconds` | T | the caller's clock. Nothing here can check it; a stale or forward value silently voids the expiry and skew checks (the engine's residual 3, verbatim). Only L-14 survives such a caller, because it compares the receipt's two instants against each other |
|
|
40
58
|
*/
|
|
59
|
+
import { type QuorumOutcome, type QuorumPolicy } from './quorum.js';
|
|
60
|
+
export { isWe4B64, NONCE128_BYTES, NONCE128_LEN } from './wire.js';
|
|
61
|
+
export { MLDSA65_PK_LEN, MLDSA65_SIG_LEN } from './sig.js';
|
|
41
62
|
/** AB-0 (§8.6b): version 3, the digest-bound form, and NO other. A verifier
|
|
42
63
|
* accepting both 2 and 3 lets an attacker present the weaker one -- the CR-4
|
|
43
64
|
* downgrade shape with the format negotiated by the party under verification. */
|
|
@@ -52,15 +73,6 @@ export declare const RECEIPT_MAX_SIGNED_BYTES = 4096;
|
|
|
52
73
|
export declare const CLOCK_SKEW_SECS = 5;
|
|
53
74
|
/** L-14's ceiling on a receipt's validity window, seconds. */
|
|
54
75
|
export declare const MAX_VALIDITY_WINDOW_SECS = 120;
|
|
55
|
-
/** FIPS 204 ML-DSA-65 public key length. */
|
|
56
|
-
export declare const MLDSA65_PK_LEN = 1952;
|
|
57
|
-
/** FIPS 204 ML-DSA-65 signature length. */
|
|
58
|
-
export declare const MLDSA65_SIG_LEN = 3309;
|
|
59
|
-
/** The one wire nonce size, 128-bit (`Nonce128`, cited by AT-1 and L-17). */
|
|
60
|
-
export declare const NONCE128_BYTES = 16;
|
|
61
|
-
/** `"b64:"` plus base64 of 16 bytes: 4 + 24. Counted, never decoded -- a rule
|
|
62
|
-
* whose behaviour on bad input is "throw" is not a refusal. */
|
|
63
|
-
export declare const NONCE128_LEN: number;
|
|
64
76
|
/**
|
|
65
77
|
* The verifier's own trust anchor: the receipt signing keys from the SIGNED
|
|
66
78
|
* bundle (`receipt_identity`, PB-12) and the bundle's suite floor
|
|
@@ -79,6 +91,17 @@ export interface TrustAnchor {
|
|
|
79
91
|
readonly pq: Uint8Array;
|
|
80
92
|
/** CR-4 floor, by wire suite name, from the signed manifest. */
|
|
81
93
|
readonly minSuite: string;
|
|
94
|
+
/**
|
|
95
|
+
* §9.3 step 7b's policy: the attester registry, `quorum_k`, the assurance
|
|
96
|
+
* floor and the bundle basis, all from the SAME signed bundle (ACP-406).
|
|
97
|
+
*
|
|
98
|
+
* Optional because a caller that never receives a floor-HIGH receipt has no
|
|
99
|
+
* registry to hand over -- NOT because 7b is optional. A receipt that carries
|
|
100
|
+
* attestations while this is absent is REFUSED under `AB-1`: a verifier that
|
|
101
|
+
* cannot recompute an entry digest has no binding at all, and passing it
|
|
102
|
+
* would be the permissive-by-omission default this package exists to refuse.
|
|
103
|
+
*/
|
|
104
|
+
readonly quorum?: QuorumPolicy;
|
|
82
105
|
}
|
|
83
106
|
/** What the stateless set concluded, when it concluded anything. */
|
|
84
107
|
export interface Verified {
|
|
@@ -87,6 +110,17 @@ export interface Verified {
|
|
|
87
110
|
readonly proposalHash: string;
|
|
88
111
|
/** The receipt's `expires_at` as step 5 validated it, epoch seconds (B). */
|
|
89
112
|
readonly receiptExpiresAt: number;
|
|
113
|
+
/**
|
|
114
|
+
* What step 7b concluded, or `null` when the receipt carried no attestations
|
|
115
|
+
* and committed to none.
|
|
116
|
+
*
|
|
117
|
+
* `null` means NOTHING WAS VERIFIED, never "a quorum held": this package
|
|
118
|
+
* cannot recompute the floor-only risk, so it cannot know that a quorum was
|
|
119
|
+
* required. A caller that knows the action is floor-HIGH must assert on this
|
|
120
|
+
* field; the module header says why the answer is not taken from the
|
|
121
|
+
* receipt's own risk claim.
|
|
122
|
+
*/
|
|
123
|
+
readonly quorum: QuorumOutcome | null;
|
|
90
124
|
}
|
|
91
125
|
/** Options for {@link verifyReceipt}. */
|
|
92
126
|
export interface VerifyOptions {
|
|
@@ -98,16 +132,6 @@ export interface VerifyOptions {
|
|
|
98
132
|
*/
|
|
99
133
|
readonly nowUnixSeconds?: number;
|
|
100
134
|
}
|
|
101
|
-
/**
|
|
102
|
-
* WE-4: is this the ASCII string `b64:` followed by RFC 4648 §4 base64, WITH
|
|
103
|
-
* padding? Ported from `quorum.rs::is_we4_b64` -- the body is whole
|
|
104
|
-
* four-character groups, the alphabet is §4's (`+` and `/`, never §5's `-` and
|
|
105
|
-
* `_`), and `=` appears only as one or two characters at the very end.
|
|
106
|
-
* Rejected, never normalised: normalising would make this verifier accept two
|
|
107
|
-
* spellings of one nonce, which is how one nonce comes to hold two ledger
|
|
108
|
-
* slots (ACP-87).
|
|
109
|
-
*/
|
|
110
|
-
export declare function isWe4B64(s: string): boolean;
|
|
111
135
|
/**
|
|
112
136
|
* Run the stateless half of §9.3 in specification order over one receipt.
|
|
113
137
|
*
|
package/dist/verify.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AAqBH,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,YAAY,EAClB,MAAM,aAAa,CAAC;AASrB,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3D;;iFAEiF;AACjF,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC;gFACgF;AAChF,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAA4B,CAAC;AAEzE;6EAC6E;AAC7E,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C,iFAAiF;AACjF,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC,8DAA8D;AAC9D,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C;;;;;;;;;;GAUG;AACH,MAAM,WAAW,WAAW;IAC1B,8CAA8C;IAC9C,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;IAC/B,mDAAmD;IACnD,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC;IACxB,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;;;;;;OASG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;CAChC;AAED,oEAAoE;AACpE,MAAM,WAAW,QAAQ;IACvB;2EACuE;IACvE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,4EAA4E;IAC5E,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC;;;;;;;;;OASG;IACH,QAAQ,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;CACvC;AAED,yCAAyC;AACzC,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAsCD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,WAAW,EAAE,OAAO,EACpB,aAAa,EAAE,UAAU,EACzB,QAAQ,EAAE,WAAW,EACrB,IAAI,CAAC,EAAE,aAAa,GACnB,QAAQ,CA2JV"}
|