@solidus-network/auth 0.6.0 → 0.6.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/dist/challenge.d.ts +35 -0
- package/dist/challenge.d.ts.map +1 -1
- package/dist/challenge.js +39 -0
- package/dist/challenge.js.map +1 -1
- package/dist/envelope.d.ts +72 -0
- package/dist/envelope.d.ts.map +1 -0
- package/dist/envelope.js +153 -0
- package/dist/envelope.js.map +1 -0
- package/dist/index.d.ts +10 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/pairwise.d.ts +36 -0
- package/dist/pairwise.d.ts.map +1 -0
- package/dist/pairwise.js +115 -0
- package/dist/pairwise.js.map +1 -0
- package/dist/rp.d.ts +34 -0
- package/dist/rp.d.ts.map +1 -0
- package/dist/rp.js +59 -0
- package/dist/rp.js.map +1 -0
- package/dist/verify.d.ts +76 -1
- package/dist/verify.d.ts.map +1 -1
- package/dist/verify.js +137 -0
- package/dist/verify.js.map +1 -1
- package/package.json +1 -1
package/dist/challenge.d.ts
CHANGED
|
@@ -17,4 +17,39 @@ export interface Challenge {
|
|
|
17
17
|
* @param ttlSeconds - How long the challenge is valid (default: 300 = 5 minutes)
|
|
18
18
|
*/
|
|
19
19
|
export declare function createChallenge(did: string, ttlSeconds?: number): Challenge;
|
|
20
|
+
export interface BbsChallenge {
|
|
21
|
+
/** UUID, for idempotency and storage. */
|
|
22
|
+
id: string;
|
|
23
|
+
/**
|
|
24
|
+
* The verifier's AUTHENTICATED origin (e.g. its TLS hostname). Folded
|
|
25
|
+
* into the presentation header, so a proof made for this verifier
|
|
26
|
+
* fails at every other one.
|
|
27
|
+
*/
|
|
28
|
+
domain: string;
|
|
29
|
+
/** The credential type this challenge accepts (pins the header cohort). */
|
|
30
|
+
credentialType: string;
|
|
31
|
+
/** Message indices the presenter must disclose. */
|
|
32
|
+
requiredIndices: number[];
|
|
33
|
+
/** 32-byte random hex — folded into the presentation header. */
|
|
34
|
+
nonce: string;
|
|
35
|
+
/** ISO 8601 */
|
|
36
|
+
issuedAt: string;
|
|
37
|
+
/** ISO 8601 */
|
|
38
|
+
expiresAt: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Create a DID-less selective-disclosure challenge. Carries no DID by
|
|
42
|
+
* construction — the verifier learns only what the disclosed indices say.
|
|
43
|
+
*
|
|
44
|
+
* The CALLER stores the challenge and must enforce single-use (mark it
|
|
45
|
+
* used once a presentation verifies) — the cryptographic ph binding kills
|
|
46
|
+
* cross-challenge replay, storage-side bookkeeping kills same-challenge
|
|
47
|
+
* re-submission.
|
|
48
|
+
*/
|
|
49
|
+
export declare function createBbsChallenge(opts: {
|
|
50
|
+
domain: string;
|
|
51
|
+
credentialType: string;
|
|
52
|
+
requiredIndices: number[];
|
|
53
|
+
ttlSeconds?: number;
|
|
54
|
+
}): BbsChallenge;
|
|
20
55
|
//# sourceMappingURL=challenge.d.ts.map
|
package/dist/challenge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"challenge.d.ts","sourceRoot":"","sources":["../src/challenge.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACxB,wCAAwC;IACxC,EAAE,EAAE,MAAM,CAAA;IACV,wCAAwC;IACxC,GAAG,EAAE,MAAM,CAAA;IACX,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAA;IACb,eAAe;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe;IACf,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,SAAM,GAAG,SAAS,CAUxE"}
|
|
1
|
+
{"version":3,"file":"challenge.d.ts","sourceRoot":"","sources":["../src/challenge.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACxB,wCAAwC;IACxC,EAAE,EAAE,MAAM,CAAA;IACV,wCAAwC;IACxC,GAAG,EAAE,MAAM,CAAA;IACX,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAA;IACb,eAAe;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe;IACf,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,SAAM,GAAG,SAAS,CAUxE;AASD,MAAM,WAAW,YAAY;IAC3B,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAA;IACV;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAA;IACd,2EAA2E;IAC3E,cAAc,EAAE,MAAM,CAAA;IACtB,mDAAmD;IACnD,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAA;IACb,eAAe;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe;IACf,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACvC,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,MAAM,CAAA;IACtB,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,GAAG,YAAY,CA0Bf"}
|
package/dist/challenge.js
CHANGED
|
@@ -16,4 +16,43 @@ export function createChallenge(did, ttlSeconds = 300) {
|
|
|
16
16
|
expiresAt: expiresAt.toISOString(),
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// DID-less BBS challenge (privacy rebuild §5.3) — additive beside the
|
|
21
|
+
// DID-bound challenge above; never a replacement.
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
import { ALWAYS_HIDDEN_KYC_INDICES } from './envelope.js';
|
|
24
|
+
/**
|
|
25
|
+
* Create a DID-less selective-disclosure challenge. Carries no DID by
|
|
26
|
+
* construction — the verifier learns only what the disclosed indices say.
|
|
27
|
+
*
|
|
28
|
+
* The CALLER stores the challenge and must enforce single-use (mark it
|
|
29
|
+
* used once a presentation verifies) — the cryptographic ph binding kills
|
|
30
|
+
* cross-challenge replay, storage-side bookkeeping kills same-challenge
|
|
31
|
+
* re-submission.
|
|
32
|
+
*/
|
|
33
|
+
export function createBbsChallenge(opts) {
|
|
34
|
+
if (!opts.domain)
|
|
35
|
+
throw new Error('domain must be non-empty');
|
|
36
|
+
if (!opts.credentialType)
|
|
37
|
+
throw new Error('credentialType must be non-empty');
|
|
38
|
+
const forbidden = opts.requiredIndices.filter((i) => ALWAYS_HIDDEN_KYC_INDICES.includes(i));
|
|
39
|
+
if (forbidden.length > 0) {
|
|
40
|
+
throw new Error(`requiredIndices ${forbidden.join(',')} are always-hidden fields ` +
|
|
41
|
+
`(subject_did/verification_id/document_hash) and can never be requested`);
|
|
42
|
+
}
|
|
43
|
+
if (opts.requiredIndices.some((i) => !Number.isInteger(i) || i < 0)) {
|
|
44
|
+
throw new Error('requiredIndices must be non-negative integers');
|
|
45
|
+
}
|
|
46
|
+
const now = new Date();
|
|
47
|
+
const ttl = opts.ttlSeconds ?? 300;
|
|
48
|
+
return {
|
|
49
|
+
id: randomUUID(),
|
|
50
|
+
domain: opts.domain,
|
|
51
|
+
credentialType: opts.credentialType,
|
|
52
|
+
requiredIndices: [...opts.requiredIndices],
|
|
53
|
+
nonce: randomBytes(32).toString('hex'),
|
|
54
|
+
issuedAt: now.toISOString(),
|
|
55
|
+
expiresAt: new Date(now.getTime() + ttl * 1_000).toISOString(),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
19
58
|
//# sourceMappingURL=challenge.js.map
|
package/dist/challenge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"challenge.js","sourceRoot":"","sources":["../src/challenge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAerD;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,UAAU,GAAG,GAAG;IAC3D,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAA;IACtB,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,UAAU,GAAG,KAAK,CAAC,CAAA;IAC9D,OAAO;QACL,EAAE,EAAE,UAAU,EAAE;QAChB,GAAG;QACH,KAAK,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;QACtC,QAAQ,EAAE,GAAG,CAAC,WAAW,EAAE;QAC3B,SAAS,EAAE,SAAS,CAAC,WAAW,EAAE;KACnC,CAAA;AACH,CAAC"}
|
|
1
|
+
{"version":3,"file":"challenge.js","sourceRoot":"","sources":["../src/challenge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAerD;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,UAAU,GAAG,GAAG;IAC3D,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAA;IACtB,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,UAAU,GAAG,KAAK,CAAC,CAAA;IAC9D,OAAO;QACL,EAAE,EAAE,UAAU,EAAE;QAChB,GAAG;QACH,KAAK,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;QACtC,QAAQ,EAAE,GAAG,CAAC,WAAW,EAAE;QAC3B,SAAS,EAAE,SAAS,CAAC,WAAW,EAAE;KACnC,CAAA;AACH,CAAC;AAED,8EAA8E;AAC9E,sEAAsE;AACtE,kDAAkD;AAClD,8EAA8E;AAE9E,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAA;AAuBzD;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAKlC;IACC,IAAI,CAAC,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;IAC7D,IAAI,CAAC,IAAI,CAAC,cAAc;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAA;IAC7E,MAAM,SAAS,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CACjD,yBAA+C,CAAC,QAAQ,CAAC,CAAC,CAAC,CAC7D,CAAA;IACD,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,mBAAmB,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,4BAA4B;YAChE,wEAAwE,CAC3E,CAAA;IACH,CAAC;IACD,IAAI,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAA;IAClE,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAA;IACtB,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,IAAI,GAAG,CAAA;IAClC,OAAO;QACL,EAAE,EAAE,UAAU,EAAE;QAChB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,eAAe,EAAE,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC;QAC1C,KAAK,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;QACtC,QAAQ,EAAE,GAAG,CAAC,WAAW,EAAE;QAC3B,SAAS,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,GAAG,GAAG,KAAK,CAAC,CAAC,WAAW,EAAE;KAC/D,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DID-less selective-disclosure presentation envelope (privacy rebuild §5.3).
|
|
3
|
+
*
|
|
4
|
+
* A `BbsSelectiveDisclosurePresentation` is what a wallet sends a relying
|
|
5
|
+
* party: a randomized BBS+ proof over the issuer-signed credential,
|
|
6
|
+
* disclosing exactly the requested message indices. By construction it has
|
|
7
|
+
* NO `holder` field, no subject DID, no per-credential identifier — the
|
|
8
|
+
* only things a verifier learns are "a valid credential from this issuer's
|
|
9
|
+
* (type, epoch) cohort" plus the disclosed claim values.
|
|
10
|
+
*
|
|
11
|
+
* DELIBERATE OMISSION vs the design sketch: the envelope does NOT carry
|
|
12
|
+
* the BBS header. The verifier RECONSTRUCTS the header from its own
|
|
13
|
+
* trusted (issuerDid, credentialType, epoch) — a header taken from the
|
|
14
|
+
* envelope would let a credential of a different cohort (e.g. a lower KYC
|
|
15
|
+
* level) satisfy a check whose policy expects another, and invites
|
|
16
|
+
* third-party RP implementations to skip policy binding entirely.
|
|
17
|
+
*
|
|
18
|
+
* Browser-safe: the wallet builds envelopes client-side — no node imports.
|
|
19
|
+
*/
|
|
20
|
+
/** Canonical KYC message order (frozen — mirrors verify's `buildKycMessageVector`). */
|
|
21
|
+
export declare const KYC_MESSAGE_FIELDS: readonly ["subject_did", "issuer_did", "verification_id", "verified_at", "kyc_level", "country", "document_type", "document_hash"];
|
|
22
|
+
/**
|
|
23
|
+
* Indices that must NEVER be disclosed (§5.2 disposition): the subject
|
|
24
|
+
* DID (the T2 handle), the per-credential verification id (the old
|
|
25
|
+
* header leak), and the per-document hash. Verifiers reject envelopes
|
|
26
|
+
* disclosing any of these even when the proof is cryptographically valid.
|
|
27
|
+
*/
|
|
28
|
+
export declare const ALWAYS_HIDDEN_KYC_INDICES: readonly [0, 2, 7];
|
|
29
|
+
export declare const BBS_PRESENTATION_TYPE = "BbsSelectiveDisclosurePresentation";
|
|
30
|
+
export interface DisclosedMessage {
|
|
31
|
+
/** Zero-based index in the original signed vector. */
|
|
32
|
+
index: number;
|
|
33
|
+
/** Hex-encoded message bytes. */
|
|
34
|
+
message: string;
|
|
35
|
+
}
|
|
36
|
+
export interface BbsSelectiveDisclosurePresentation {
|
|
37
|
+
type: typeof BBS_PRESENTATION_TYPE;
|
|
38
|
+
/** Credential-format version marker. */
|
|
39
|
+
v: 'bbs-v2';
|
|
40
|
+
/** The challenge this presentation answers. */
|
|
41
|
+
challengeId: string;
|
|
42
|
+
/** Issuer key-rotation epoch (cohort label — selects the pubkey). */
|
|
43
|
+
epoch: number;
|
|
44
|
+
/** Hex-encoded randomized BBS+ proof. */
|
|
45
|
+
proof: string;
|
|
46
|
+
totalMessageCount: number;
|
|
47
|
+
disclosed: DisclosedMessage[];
|
|
48
|
+
/** Re-recognition (mechanism B) only — §5.4. Null in one-shot mode. */
|
|
49
|
+
pairwiseDid: string | null;
|
|
50
|
+
/** VP signed by the pairwise key — §5.4. Null in one-shot mode. */
|
|
51
|
+
pairwiseProof: Record<string, unknown> | null;
|
|
52
|
+
}
|
|
53
|
+
export interface BuildBbsPresentationInput {
|
|
54
|
+
challengeId: string;
|
|
55
|
+
epoch: number;
|
|
56
|
+
proofHex: string;
|
|
57
|
+
totalMessageCount: number;
|
|
58
|
+
disclosed: DisclosedMessage[];
|
|
59
|
+
pairwiseDid?: string | null;
|
|
60
|
+
pairwiseProof?: Record<string, unknown> | null;
|
|
61
|
+
}
|
|
62
|
+
/** Build (and validate) a presentation envelope — wallet side. */
|
|
63
|
+
export declare function buildBbsPresentation(input: BuildBbsPresentationInput): BbsSelectiveDisclosurePresentation;
|
|
64
|
+
/** Parse + strictly validate a serialized envelope — verifier side. */
|
|
65
|
+
export declare function parseBbsPresentation(json: string): BbsSelectiveDisclosurePresentation;
|
|
66
|
+
/**
|
|
67
|
+
* Decode disclosed messages to named KYC fields (utf8). Only disclosed
|
|
68
|
+
* indices appear; always-hidden fields can never appear in a valid
|
|
69
|
+
* envelope (the verifier rejects them).
|
|
70
|
+
*/
|
|
71
|
+
export declare function decodeDisclosedKycFields(disclosed: DisclosedMessage[]): Partial<Record<(typeof KYC_MESSAGE_FIELDS)[number], string>>;
|
|
72
|
+
//# sourceMappingURL=envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,uFAAuF;AACvF,eAAO,MAAM,kBAAkB,oIASrB,CAAA;AAEV;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,oBAAqB,CAAA;AAE3D,eAAO,MAAM,qBAAqB,uCAAuC,CAAA;AAEzE,MAAM,WAAW,gBAAgB;IAC/B,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAA;IACb,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,kCAAkC;IACjD,IAAI,EAAE,OAAO,qBAAqB,CAAA;IAClC,wCAAwC;IACxC,CAAC,EAAE,QAAQ,CAAA;IACX,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAA;IACnB,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAA;IACb,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAA;IACb,iBAAiB,EAAE,MAAM,CAAA;IACzB,SAAS,EAAE,gBAAgB,EAAE,CAAA;IAC7B,uEAAuE;IACvE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,mEAAmE;IACnE,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CAC9C;AAQD,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,iBAAiB,EAAE,MAAM,CAAA;IACzB,SAAS,EAAE,gBAAgB,EAAE,CAAA;IAC7B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CAC/C;AAED,kEAAkE;AAClE,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,yBAAyB,GAC/B,kCAAkC,CAcpC;AAED,uEAAuE;AACvE,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,kCAAkC,CAwBrF;AAmDD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,gBAAgB,EAAE,GAC5B,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAa9D"}
|
package/dist/envelope.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DID-less selective-disclosure presentation envelope (privacy rebuild §5.3).
|
|
3
|
+
*
|
|
4
|
+
* A `BbsSelectiveDisclosurePresentation` is what a wallet sends a relying
|
|
5
|
+
* party: a randomized BBS+ proof over the issuer-signed credential,
|
|
6
|
+
* disclosing exactly the requested message indices. By construction it has
|
|
7
|
+
* NO `holder` field, no subject DID, no per-credential identifier — the
|
|
8
|
+
* only things a verifier learns are "a valid credential from this issuer's
|
|
9
|
+
* (type, epoch) cohort" plus the disclosed claim values.
|
|
10
|
+
*
|
|
11
|
+
* DELIBERATE OMISSION vs the design sketch: the envelope does NOT carry
|
|
12
|
+
* the BBS header. The verifier RECONSTRUCTS the header from its own
|
|
13
|
+
* trusted (issuerDid, credentialType, epoch) — a header taken from the
|
|
14
|
+
* envelope would let a credential of a different cohort (e.g. a lower KYC
|
|
15
|
+
* level) satisfy a check whose policy expects another, and invites
|
|
16
|
+
* third-party RP implementations to skip policy binding entirely.
|
|
17
|
+
*
|
|
18
|
+
* Browser-safe: the wallet builds envelopes client-side — no node imports.
|
|
19
|
+
*/
|
|
20
|
+
/** Canonical KYC message order (frozen — mirrors verify's `buildKycMessageVector`). */
|
|
21
|
+
export const KYC_MESSAGE_FIELDS = [
|
|
22
|
+
'subject_did',
|
|
23
|
+
'issuer_did',
|
|
24
|
+
'verification_id',
|
|
25
|
+
'verified_at',
|
|
26
|
+
'kyc_level',
|
|
27
|
+
'country',
|
|
28
|
+
'document_type',
|
|
29
|
+
'document_hash',
|
|
30
|
+
];
|
|
31
|
+
/**
|
|
32
|
+
* Indices that must NEVER be disclosed (§5.2 disposition): the subject
|
|
33
|
+
* DID (the T2 handle), the per-credential verification id (the old
|
|
34
|
+
* header leak), and the per-document hash. Verifiers reject envelopes
|
|
35
|
+
* disclosing any of these even when the proof is cryptographically valid.
|
|
36
|
+
*/
|
|
37
|
+
export const ALWAYS_HIDDEN_KYC_INDICES = [0, 2, 7];
|
|
38
|
+
export const BBS_PRESENTATION_TYPE = 'BbsSelectiveDisclosurePresentation';
|
|
39
|
+
const HEX_RE = /^[0-9a-f]*$/i;
|
|
40
|
+
function isHex(s) {
|
|
41
|
+
return HEX_RE.test(s) && s.length % 2 === 0;
|
|
42
|
+
}
|
|
43
|
+
/** Build (and validate) a presentation envelope — wallet side. */
|
|
44
|
+
export function buildBbsPresentation(input) {
|
|
45
|
+
const envelope = {
|
|
46
|
+
type: BBS_PRESENTATION_TYPE,
|
|
47
|
+
v: 'bbs-v2',
|
|
48
|
+
challengeId: input.challengeId,
|
|
49
|
+
epoch: input.epoch,
|
|
50
|
+
proof: input.proofHex,
|
|
51
|
+
totalMessageCount: input.totalMessageCount,
|
|
52
|
+
disclosed: input.disclosed.map((d) => ({ index: d.index, message: d.message })),
|
|
53
|
+
pairwiseDid: input.pairwiseDid ?? null,
|
|
54
|
+
pairwiseProof: input.pairwiseProof ?? null,
|
|
55
|
+
};
|
|
56
|
+
assertEnvelopeShape(envelope);
|
|
57
|
+
return envelope;
|
|
58
|
+
}
|
|
59
|
+
/** Parse + strictly validate a serialized envelope — verifier side. */
|
|
60
|
+
export function parseBbsPresentation(json) {
|
|
61
|
+
let raw;
|
|
62
|
+
try {
|
|
63
|
+
raw = JSON.parse(json);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
throw new Error('presentation is not valid JSON');
|
|
67
|
+
}
|
|
68
|
+
if (typeof raw !== 'object' || raw === null) {
|
|
69
|
+
throw new Error('presentation must be a JSON object');
|
|
70
|
+
}
|
|
71
|
+
const e = raw;
|
|
72
|
+
const envelope = {
|
|
73
|
+
type: e['type'],
|
|
74
|
+
v: e['v'],
|
|
75
|
+
challengeId: e['challengeId'],
|
|
76
|
+
epoch: e['epoch'],
|
|
77
|
+
proof: e['proof'],
|
|
78
|
+
totalMessageCount: e['totalMessageCount'],
|
|
79
|
+
disclosed: e['disclosed'],
|
|
80
|
+
pairwiseDid: (e['pairwiseDid'] ?? null),
|
|
81
|
+
pairwiseProof: (e['pairwiseProof'] ?? null),
|
|
82
|
+
};
|
|
83
|
+
assertEnvelopeShape(envelope);
|
|
84
|
+
return envelope;
|
|
85
|
+
}
|
|
86
|
+
function assertEnvelopeShape(e) {
|
|
87
|
+
if (e.type !== BBS_PRESENTATION_TYPE) {
|
|
88
|
+
throw new Error(`presentation type must be ${BBS_PRESENTATION_TYPE}`);
|
|
89
|
+
}
|
|
90
|
+
if (e.v !== 'bbs-v2')
|
|
91
|
+
throw new Error('unsupported presentation version');
|
|
92
|
+
if (typeof e.challengeId !== 'string' || e.challengeId.length === 0) {
|
|
93
|
+
throw new Error('challengeId must be a non-empty string');
|
|
94
|
+
}
|
|
95
|
+
if (!Number.isInteger(e.epoch) || e.epoch < 0) {
|
|
96
|
+
throw new Error('epoch must be a non-negative integer');
|
|
97
|
+
}
|
|
98
|
+
if (typeof e.proof !== 'string' || e.proof.length === 0 || !isHex(e.proof)) {
|
|
99
|
+
throw new Error('proof must be non-empty hex');
|
|
100
|
+
}
|
|
101
|
+
if (!Number.isInteger(e.totalMessageCount) ||
|
|
102
|
+
e.totalMessageCount < 1 ||
|
|
103
|
+
e.totalMessageCount > 64) {
|
|
104
|
+
throw new Error('totalMessageCount out of range');
|
|
105
|
+
}
|
|
106
|
+
if (!Array.isArray(e.disclosed))
|
|
107
|
+
throw new Error('disclosed must be an array');
|
|
108
|
+
const seen = new Set();
|
|
109
|
+
for (const d of e.disclosed) {
|
|
110
|
+
if (typeof d !== 'object' ||
|
|
111
|
+
d === null ||
|
|
112
|
+
!Number.isInteger(d.index) ||
|
|
113
|
+
d.index < 0 ||
|
|
114
|
+
d.index >= e.totalMessageCount ||
|
|
115
|
+
typeof d.message !== 'string' ||
|
|
116
|
+
!isHex(d.message)) {
|
|
117
|
+
throw new Error('disclosed entries must be {index within range, hex message}');
|
|
118
|
+
}
|
|
119
|
+
if (seen.has(d.index))
|
|
120
|
+
throw new Error(`duplicate disclosed index ${d.index}`);
|
|
121
|
+
seen.add(d.index);
|
|
122
|
+
}
|
|
123
|
+
if (e.pairwiseDid !== null && (typeof e.pairwiseDid !== 'string' || e.pairwiseDid.length === 0)) {
|
|
124
|
+
throw new Error('pairwiseDid must be null or a non-empty string');
|
|
125
|
+
}
|
|
126
|
+
if (e.pairwiseProof !== null && (typeof e.pairwiseProof !== 'object' || Array.isArray(e.pairwiseProof))) {
|
|
127
|
+
throw new Error('pairwiseProof must be null or an object');
|
|
128
|
+
}
|
|
129
|
+
if ((e.pairwiseDid === null) !== (e.pairwiseProof === null)) {
|
|
130
|
+
throw new Error('pairwiseDid and pairwiseProof must be provided together');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Decode disclosed messages to named KYC fields (utf8). Only disclosed
|
|
135
|
+
* indices appear; always-hidden fields can never appear in a valid
|
|
136
|
+
* envelope (the verifier rejects them).
|
|
137
|
+
*/
|
|
138
|
+
export function decodeDisclosedKycFields(disclosed) {
|
|
139
|
+
const out = {};
|
|
140
|
+
const decoder = new TextDecoder();
|
|
141
|
+
for (const d of disclosed) {
|
|
142
|
+
const field = KYC_MESSAGE_FIELDS[d.index];
|
|
143
|
+
if (!field)
|
|
144
|
+
continue;
|
|
145
|
+
const bytes = new Uint8Array(d.message.length / 2);
|
|
146
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
147
|
+
bytes[i] = parseInt(d.message.slice(i * 2, i * 2 + 2), 16);
|
|
148
|
+
}
|
|
149
|
+
out[field] = decoder.decode(bytes);
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=envelope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.js","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
export { createChallenge } from './challenge.js';
|
|
2
|
-
export type { Challenge } from './challenge.js';
|
|
3
|
-
export { verifyPresentation } from './verify.js';
|
|
4
|
-
export type { VerifiablePresentation, PresentationVerificationResult } from './verify.js';
|
|
1
|
+
export { createChallenge, createBbsChallenge } from './challenge.js';
|
|
2
|
+
export type { Challenge, BbsChallenge } from './challenge.js';
|
|
3
|
+
export { verifyPresentation, verifyBbsPresentation } from './verify.js';
|
|
4
|
+
export type { VerifiablePresentation, PresentationVerificationResult, BbsVerifyOptions, BbsPresentationVerificationResult, } from './verify.js';
|
|
5
|
+
export { buildBbsPresentation, parseBbsPresentation, decodeDisclosedKycFields, KYC_MESSAGE_FIELDS, ALWAYS_HIDDEN_KYC_INDICES, BBS_PRESENTATION_TYPE, } from './envelope.js';
|
|
6
|
+
export type { BbsSelectiveDisclosurePresentation, DisclosedMessage, BuildBbsPresentationInput, } from './envelope.js';
|
|
7
|
+
export { buildPairwiseAttachment, buildPairwiseDid, pairwiseAddressFromPublicKey, asPairwiseAttachment, } from './pairwise.js';
|
|
8
|
+
export type { PairwiseAttachment } from './pairwise.js';
|
|
9
|
+
export { createIssuerKeyResolver } from './rp.js';
|
|
10
|
+
export type { IssuerKeyResolverOptions } from './rp.js';
|
|
5
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACpE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7D,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACvE,YAAY,EACV,sBAAsB,EACtB,8BAA8B,EAC9B,gBAAgB,EAChB,iCAAiC,GAClC,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,EACxB,kBAAkB,EAClB,yBAAyB,EACzB,qBAAqB,GACtB,MAAM,eAAe,CAAA;AACtB,YAAY,EACV,kCAAkC,EAClC,gBAAgB,EAChB,yBAAyB,GAC1B,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,4BAA4B,EAC5B,oBAAoB,GACrB,MAAM,eAAe,CAAA;AACtB,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AACvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAA;AACjD,YAAY,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
export { createChallenge } from './challenge.js';
|
|
2
|
-
export { verifyPresentation } from './verify.js';
|
|
1
|
+
export { createChallenge, createBbsChallenge } from './challenge.js';
|
|
2
|
+
export { verifyPresentation, verifyBbsPresentation } from './verify.js';
|
|
3
|
+
export { buildBbsPresentation, parseBbsPresentation, decodeDisclosedKycFields, KYC_MESSAGE_FIELDS, ALWAYS_HIDDEN_KYC_INDICES, BBS_PRESENTATION_TYPE, } from './envelope.js';
|
|
4
|
+
export { buildPairwiseAttachment, buildPairwiseDid, pairwiseAddressFromPublicKey, asPairwiseAttachment, } from './pairwise.js';
|
|
5
|
+
export { createIssuerKeyResolver } from './rp.js';
|
|
3
6
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAEpE,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAOvE,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,EACxB,kBAAkB,EAClB,yBAAyB,EACzB,qBAAqB,GACtB,MAAM,eAAe,CAAA;AAMtB,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,4BAA4B,EAC5B,oBAAoB,GACrB,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAA"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { VerifiablePresentation } from './verify.js';
|
|
2
|
+
/** The self-certifying attachment carried in the envelope's `pairwiseProof`. */
|
|
3
|
+
export interface PairwiseAttachment {
|
|
4
|
+
/** Hex pairwise public key — the verifier binds this to the pairwise DID. */
|
|
5
|
+
publicKeyHex: string;
|
|
6
|
+
/** Standard W3C VP signed by the pairwise key, holder = the pairwise DID. */
|
|
7
|
+
vp: VerifiablePresentation;
|
|
8
|
+
}
|
|
9
|
+
declare function hexToBytes(hex: string): Uint8Array;
|
|
10
|
+
/** `addr = base58(BLAKE3(pubkey)[0..20])` — mirrors the SDK `addressFromPublicKey`. */
|
|
11
|
+
export declare function pairwiseAddressFromPublicKey(publicKey: Uint8Array): string;
|
|
12
|
+
/** `did:solidus:{network}:{addr}` — the self-certifying pairwise DID (never on-chain). */
|
|
13
|
+
export declare function buildPairwiseDid(network: string, publicKey: Uint8Array): string;
|
|
14
|
+
/**
|
|
15
|
+
* Build the pairwise VP attachment. Takes the ALREADY-DERIVED pairwise key
|
|
16
|
+
* (the wallet derives it via `derivePairwiseKeyFromSeed` under the verifier's
|
|
17
|
+
* authenticated id) plus the challenge nonce it must sign over. The produced
|
|
18
|
+
* VP is byte-compatible with the shipped DID-bound `verifyPresentation`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildPairwiseAttachment(opts: {
|
|
21
|
+
/** 32-byte pairwise private key, hex. */
|
|
22
|
+
pairwisePrivateKeyHex: string;
|
|
23
|
+
/** Verifier's network (goes into the DID). */
|
|
24
|
+
network: string;
|
|
25
|
+
/** The challenge nonce — the VP's `proof.challenge`. */
|
|
26
|
+
nonce: string;
|
|
27
|
+
/** ISO timestamp; defaults to now. */
|
|
28
|
+
created?: string;
|
|
29
|
+
}): Promise<{
|
|
30
|
+
pairwiseDid: string;
|
|
31
|
+
attachment: PairwiseAttachment;
|
|
32
|
+
}>;
|
|
33
|
+
/** Narrow an unknown envelope `pairwiseProof` to a `PairwiseAttachment`. */
|
|
34
|
+
export declare function asPairwiseAttachment(value: unknown): PairwiseAttachment | null;
|
|
35
|
+
export { hexToBytes as _pairwiseHexToBytes };
|
|
36
|
+
//# sourceMappingURL=pairwise.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairwise.d.ts","sourceRoot":"","sources":["../src/pairwise.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAEzD,gFAAgF;AAChF,MAAM,WAAW,kBAAkB;IACjC,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAA;IACpB,6EAA6E;IAC7E,EAAE,EAAE,sBAAsB,CAAA;CAC3B;AAED,iBAAS,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAI3C;AAiBD,uFAAuF;AACvF,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,UAAU,GAAG,MAAM,CAE1E;AAED,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,GAAG,MAAM,CAG/E;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAAC,IAAI,EAAE;IAClD,yCAAyC;IACzC,qBAAqB,EAAE,MAAM,CAAA;IAC7B,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAA;IACf,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAA;IACb,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,GAAG,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,kBAAkB,CAAA;CAAE,CAAC,CAgCnE;AAED,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,kBAAkB,GAAG,IAAI,CAU9E;AAED,OAAO,EAAE,UAAU,IAAI,mBAAmB,EAAE,CAAA"}
|
package/dist/pairwise.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pairwise-DID re-recognition (privacy rebuild §5.4 — mechanism B).
|
|
3
|
+
*
|
|
4
|
+
* "Remember me at THIS verifier" without two verifiers colluding. Beside the
|
|
5
|
+
* one-shot SD proof, the wallet attaches a standard W3C VP signed by a
|
|
6
|
+
* per-verifier pairwise key (derived via `derivePairwiseKeyFromSeed`, §5.1).
|
|
7
|
+
* The pairwise DID is a SELF-CERTIFYING string — `did:solidus:{network}:{addr}`
|
|
8
|
+
* where `addr = base58(BLAKE3(pubkey)[0..20])` — and is NEVER registered
|
|
9
|
+
* on-chain (an on-chain anchor would itself be a correlation handle, BD-7).
|
|
10
|
+
*
|
|
11
|
+
* The verifier recognizes a returning user because the same (seed, verifier)
|
|
12
|
+
* always yields the same pairwise DID; two verifiers get independent HKDF
|
|
13
|
+
* outputs, so they cannot link the same person.
|
|
14
|
+
*
|
|
15
|
+
* Authenticity caveat (§5.4): the WALLET must derive the pairwise key under
|
|
16
|
+
* the verifier's OWN authenticated identity (its TLS origin = the challenge
|
|
17
|
+
* `domain`), never a `verifier_id` copied blindly from an untrusted channel —
|
|
18
|
+
* else verifier A can request B's pseudonym and collude. This module verifies
|
|
19
|
+
* the binding + signature; the wallet owns the "derive under the right id" step.
|
|
20
|
+
*
|
|
21
|
+
* Weakness (surfaced, not buried): mechanism B proves two things SIDE BY SIDE,
|
|
22
|
+
* not jointly — "holds a valid Solidus KYC credential" (the b.1 proof) and
|
|
23
|
+
* "controls this pairwise key" (the VP) — nothing cryptographically binds them
|
|
24
|
+
* to the SAME seed. Assurance tops out at "a returning controller of this key
|
|
25
|
+
* who can also show some valid Solidus credential," fine for cooperative
|
|
26
|
+
* re-recognition. Access-control-grade "provably the same KYC holder" needs
|
|
27
|
+
* mechanism C — swapped in later behind the same verifier_id→handle contract.
|
|
28
|
+
*/
|
|
29
|
+
import * as ed from '@noble/ed25519';
|
|
30
|
+
import { blake3 } from '@noble/hashes/blake3';
|
|
31
|
+
import bs58 from 'bs58';
|
|
32
|
+
function hexToBytes(hex) {
|
|
33
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
34
|
+
for (let i = 0; i < bytes.length; i++)
|
|
35
|
+
bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
36
|
+
return bytes;
|
|
37
|
+
}
|
|
38
|
+
function bytesToHex(bytes) {
|
|
39
|
+
let out = '';
|
|
40
|
+
for (let i = 0; i < bytes.length; i++)
|
|
41
|
+
out += bytes[i].toString(16).padStart(2, '0');
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
function base64url(bytes) {
|
|
45
|
+
// Browser-safe base64url (no node Buffer) so this module can move into the
|
|
46
|
+
// wallet later without change.
|
|
47
|
+
let bin = '';
|
|
48
|
+
for (let i = 0; i < bytes.length; i++)
|
|
49
|
+
bin += String.fromCharCode(bytes[i]);
|
|
50
|
+
const b64 = typeof btoa === 'function' ? btoa(bin) : Buffer.from(bytes).toString('base64');
|
|
51
|
+
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
52
|
+
}
|
|
53
|
+
/** `addr = base58(BLAKE3(pubkey)[0..20])` — mirrors the SDK `addressFromPublicKey`. */
|
|
54
|
+
export function pairwiseAddressFromPublicKey(publicKey) {
|
|
55
|
+
return bs58.encode(blake3(publicKey).slice(0, 20));
|
|
56
|
+
}
|
|
57
|
+
/** `did:solidus:{network}:{addr}` — the self-certifying pairwise DID (never on-chain). */
|
|
58
|
+
export function buildPairwiseDid(network, publicKey) {
|
|
59
|
+
if (!network)
|
|
60
|
+
throw new Error('network must be non-empty');
|
|
61
|
+
return `did:solidus:${network}:${pairwiseAddressFromPublicKey(publicKey)}`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Build the pairwise VP attachment. Takes the ALREADY-DERIVED pairwise key
|
|
65
|
+
* (the wallet derives it via `derivePairwiseKeyFromSeed` under the verifier's
|
|
66
|
+
* authenticated id) plus the challenge nonce it must sign over. The produced
|
|
67
|
+
* VP is byte-compatible with the shipped DID-bound `verifyPresentation`.
|
|
68
|
+
*/
|
|
69
|
+
export async function buildPairwiseAttachment(opts) {
|
|
70
|
+
const priv = hexToBytes(opts.pairwisePrivateKeyHex);
|
|
71
|
+
const pub = await ed.getPublicKeyAsync(priv);
|
|
72
|
+
const pairwiseDid = buildPairwiseDid(opts.network, pub);
|
|
73
|
+
// Presentation (proof added after signing) — key order MUST match what
|
|
74
|
+
// `verifyPresentation` reconstructs (it strips `proof` and stringifies).
|
|
75
|
+
const presentation = {
|
|
76
|
+
'@context': ['https://www.w3.org/2018/credentials/v1'],
|
|
77
|
+
type: ['VerifiablePresentation'],
|
|
78
|
+
holder: pairwiseDid,
|
|
79
|
+
};
|
|
80
|
+
const header = { alg: 'EdDSA', b64: false, crit: ['b64'] };
|
|
81
|
+
const headerB64 = base64url(new TextEncoder().encode(JSON.stringify(header)));
|
|
82
|
+
const payloadB64 = base64url(new TextEncoder().encode(JSON.stringify(presentation)));
|
|
83
|
+
const signingInput = `${headerB64}.${payloadB64}`;
|
|
84
|
+
const signature = await ed.signAsync(new TextEncoder().encode(signingInput), priv);
|
|
85
|
+
const jws = `${headerB64}..${base64url(signature)}`;
|
|
86
|
+
const vp = {
|
|
87
|
+
...presentation,
|
|
88
|
+
proof: {
|
|
89
|
+
type: 'Ed25519Signature2020',
|
|
90
|
+
created: opts.created ?? new Date().toISOString(),
|
|
91
|
+
challenge: opts.nonce,
|
|
92
|
+
proofPurpose: 'authentication',
|
|
93
|
+
verificationMethod: `${pairwiseDid}#key-0`,
|
|
94
|
+
jws,
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
return { pairwiseDid, attachment: { publicKeyHex: bytesToHex(pub), vp } };
|
|
98
|
+
}
|
|
99
|
+
/** Narrow an unknown envelope `pairwiseProof` to a `PairwiseAttachment`. */
|
|
100
|
+
export function asPairwiseAttachment(value) {
|
|
101
|
+
if (typeof value !== 'object' || value === null)
|
|
102
|
+
return null;
|
|
103
|
+
const a = value;
|
|
104
|
+
if (typeof a['publicKeyHex'] !== 'string')
|
|
105
|
+
return null;
|
|
106
|
+
if (typeof a['vp'] !== 'object' || a['vp'] === null)
|
|
107
|
+
return null;
|
|
108
|
+
const vp = a['vp'];
|
|
109
|
+
if (typeof vp['holder'] !== 'string' || typeof vp['proof'] !== 'object' || vp['proof'] === null) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
export { hexToBytes as _pairwiseHexToBytes };
|
|
115
|
+
//# sourceMappingURL=pairwise.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairwise.js","sourceRoot":"","sources":["../src/pairwise.ts"],"names":[],"mappings":"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"}
|
package/dist/rp.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relying-party surface (privacy rebuild §5.7).
|
|
3
|
+
*
|
|
4
|
+
* The verifier primitives — `createBbsChallenge` (issue a DID-less challenge)
|
|
5
|
+
* and `verifyBbsPresentation` (stateless verify) — are the RP SDK. This module
|
|
6
|
+
* adds the batteries a third-party verifier would otherwise hand-roll: an
|
|
7
|
+
* issuer-key resolver that fetches `/.well-known/solidus-bbs-issuer.json`,
|
|
8
|
+
* caches the current + grace epoch pubkeys, and returns exactly the
|
|
9
|
+
* `resolveIssuerKey(epoch)` function `verifyBbsPresentation` expects.
|
|
10
|
+
*
|
|
11
|
+
* FAIL-CLOSED by design: an unknown/lapsed epoch, an unreachable issuer, a
|
|
12
|
+
* non-200, or a malformed doc all resolve to `null` — never a key — so the
|
|
13
|
+
* verify path rejects rather than accepting on error. That is also the
|
|
14
|
+
* bounded-lifetime enforcement point (a lapsed epoch is simply absent from the
|
|
15
|
+
* doc → null → reject; §5.5).
|
|
16
|
+
*
|
|
17
|
+
* Browser-safe (fetch only); works in an RP backend or a browser verifier.
|
|
18
|
+
*/
|
|
19
|
+
export interface IssuerKeyResolverOptions {
|
|
20
|
+
/** The issuer's base URL, e.g. `https://verify.solidus.network`. */
|
|
21
|
+
issuerBaseUrl: string;
|
|
22
|
+
/** Injectable for tests / custom transports. */
|
|
23
|
+
fetchImpl?: typeof fetch;
|
|
24
|
+
/** How long to cache the fetched doc. Default 5 min (matches its Cache-Control). */
|
|
25
|
+
cacheTtlMs?: number;
|
|
26
|
+
/** Clock injection for tests. */
|
|
27
|
+
now?: () => number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Build a caching, fail-closed `resolveIssuerKey(epoch) => Promise<string|null>`
|
|
31
|
+
* suitable to pass straight into `verifyBbsPresentation`'s options.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createIssuerKeyResolver(opts: IssuerKeyResolverOptions): (epoch: number) => Promise<string | null>;
|
|
34
|
+
//# sourceMappingURL=rp.d.ts.map
|
package/dist/rp.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rp.d.ts","sourceRoot":"","sources":["../src/rp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAcH,MAAM,WAAW,wBAAwB;IACvC,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAA;IACrB,gDAAgD;IAChD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,wBAAwB,GAC7B,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmC3C"}
|
package/dist/rp.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relying-party surface (privacy rebuild §5.7).
|
|
3
|
+
*
|
|
4
|
+
* The verifier primitives — `createBbsChallenge` (issue a DID-less challenge)
|
|
5
|
+
* and `verifyBbsPresentation` (stateless verify) — are the RP SDK. This module
|
|
6
|
+
* adds the batteries a third-party verifier would otherwise hand-roll: an
|
|
7
|
+
* issuer-key resolver that fetches `/.well-known/solidus-bbs-issuer.json`,
|
|
8
|
+
* caches the current + grace epoch pubkeys, and returns exactly the
|
|
9
|
+
* `resolveIssuerKey(epoch)` function `verifyBbsPresentation` expects.
|
|
10
|
+
*
|
|
11
|
+
* FAIL-CLOSED by design: an unknown/lapsed epoch, an unreachable issuer, a
|
|
12
|
+
* non-200, or a malformed doc all resolve to `null` — never a key — so the
|
|
13
|
+
* verify path rejects rather than accepting on error. That is also the
|
|
14
|
+
* bounded-lifetime enforcement point (a lapsed epoch is simply absent from the
|
|
15
|
+
* doc → null → reject; §5.5).
|
|
16
|
+
*
|
|
17
|
+
* Browser-safe (fetch only); works in an RP backend or a browser verifier.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Build a caching, fail-closed `resolveIssuerKey(epoch) => Promise<string|null>`
|
|
21
|
+
* suitable to pass straight into `verifyBbsPresentation`'s options.
|
|
22
|
+
*/
|
|
23
|
+
export function createIssuerKeyResolver(opts) {
|
|
24
|
+
const doFetch = opts.fetchImpl ?? globalThis.fetch;
|
|
25
|
+
const now = opts.now ?? (() => Date.now());
|
|
26
|
+
const ttl = opts.cacheTtlMs ?? 300_000;
|
|
27
|
+
const url = `${opts.issuerBaseUrl.replace(/\/$/, '')}/.well-known/solidus-bbs-issuer.json`;
|
|
28
|
+
let cache = null;
|
|
29
|
+
async function loadDoc() {
|
|
30
|
+
if (cache && now() - cache.fetchedAt < ttl)
|
|
31
|
+
return cache.byEpoch;
|
|
32
|
+
const byEpoch = new Map();
|
|
33
|
+
try {
|
|
34
|
+
const res = await doFetch(url, { method: 'GET' });
|
|
35
|
+
if (res.ok) {
|
|
36
|
+
const doc = (await res.json());
|
|
37
|
+
if (doc?.current && typeof doc.current.pubkeyHex === 'string') {
|
|
38
|
+
byEpoch.set(doc.current.epoch, doc.current.pubkeyHex);
|
|
39
|
+
for (const g of doc.grace ?? []) {
|
|
40
|
+
if (typeof g?.pubkeyHex === 'string')
|
|
41
|
+
byEpoch.set(g.epoch, g.pubkeyHex);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Fail-closed: leave byEpoch empty → every resolve returns null → reject.
|
|
48
|
+
}
|
|
49
|
+
// Cache even an empty result briefly so a hard-down issuer doesn't get
|
|
50
|
+
// hammered on every presentation; the short TTL bounds staleness.
|
|
51
|
+
cache = { byEpoch, fetchedAt: now() };
|
|
52
|
+
return byEpoch;
|
|
53
|
+
}
|
|
54
|
+
return async function resolveIssuerKey(epoch) {
|
|
55
|
+
const byEpoch = await loadDoc();
|
|
56
|
+
return byEpoch.get(epoch) ?? null;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=rp.js.map
|
package/dist/rp.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rp.js","sourceRoot":"","sources":["../src/rp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAyBH;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,IAA8B;IAE9B,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,IAAK,UAAU,CAAC,KAAsB,CAAA;IACpE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;IAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,IAAI,OAAO,CAAA;IACtC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,sCAAsC,CAAA;IAE1F,IAAI,KAAK,GAA+D,IAAI,CAAA;IAE5E,KAAK,UAAU,OAAO;QACpB,IAAI,KAAK,IAAI,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,GAAG,GAAG;YAAE,OAAO,KAAK,CAAC,OAAO,CAAA;QAChE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAA;QACzC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;YACjD,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;gBACX,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAsB,CAAA;gBACnD,IAAI,GAAG,EAAE,OAAO,IAAI,OAAO,GAAG,CAAC,OAAO,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;oBAC9D,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;oBACrD,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;wBAChC,IAAI,OAAO,CAAC,EAAE,SAAS,KAAK,QAAQ;4BAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,SAAS,CAAC,CAAA;oBACzE,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,0EAA0E;QAC5E,CAAC;QACD,uEAAuE;QACvE,kEAAkE;QAClE,KAAK,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,CAAA;QACrC,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,OAAO,KAAK,UAAU,gBAAgB,CAAC,KAAa;QAClD,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAA;QAC/B,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAA;IACnC,CAAC,CAAA;AACH,CAAC"}
|
package/dist/verify.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { Challenge } from './challenge.js';
|
|
1
|
+
import type { Challenge, BbsChallenge } from './challenge.js';
|
|
2
|
+
import { type BbsSelectiveDisclosurePresentation } from './envelope.js';
|
|
2
3
|
export interface VerifiablePresentation {
|
|
3
4
|
'@context': string[];
|
|
4
5
|
/** Must include 'VerifiablePresentation' */
|
|
@@ -42,4 +43,78 @@ export interface PresentationVerificationResult {
|
|
|
42
43
|
* @param getPublicKey - Async resolver: given a verificationMethod ID, return the Ed25519 public key (32 bytes)
|
|
43
44
|
*/
|
|
44
45
|
export declare function verifyPresentation(challenge: Challenge, presentation: VerifiablePresentation, getPublicKey: (verificationMethodId: string) => Promise<Uint8Array>): Promise<PresentationVerificationResult>;
|
|
46
|
+
export interface BbsVerifyOptions {
|
|
47
|
+
/** The trusted issuer DID (from the verifier's own policy, never the envelope). */
|
|
48
|
+
issuerDid: string;
|
|
49
|
+
/**
|
|
50
|
+
* Resolve the issuer's BBS pubkey (hex) for an epoch — typically from
|
|
51
|
+
* `/.well-known/solidus-bbs-issuer.json` (current + grace), cached.
|
|
52
|
+
* Return null for unknown/lapsed epochs.
|
|
53
|
+
*/
|
|
54
|
+
resolveIssuerKey: (epoch: number) => Promise<string | null>;
|
|
55
|
+
/**
|
|
56
|
+
* Whether this challenge was already answered (storage-side single-use
|
|
57
|
+
* bookkeeping — the caller tracks it; this library only enforces it).
|
|
58
|
+
*/
|
|
59
|
+
used?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* The verifier's network (e.g. 'testnet'). REQUIRED when the presentation
|
|
62
|
+
* carries a pairwise attachment — the pairwise DID binds to it. A pairwise
|
|
63
|
+
* attachment presented without a configured network fails closed.
|
|
64
|
+
*/
|
|
65
|
+
network?: string;
|
|
66
|
+
/** Clock injection for tests. */
|
|
67
|
+
now?: () => Date;
|
|
68
|
+
}
|
|
69
|
+
export interface BbsPresentationVerificationResult {
|
|
70
|
+
valid: boolean;
|
|
71
|
+
error?: string;
|
|
72
|
+
/**
|
|
73
|
+
* DID-less by construction for the one-shot proof: no holder check of any
|
|
74
|
+
* kind. A presentation proves "a valid credential from the trusted issuer's
|
|
75
|
+
* (type, epoch) cohort, disclosing exactly these claims" — never who.
|
|
76
|
+
*
|
|
77
|
+
* The pairwise checks run ONLY when a re-recognition attachment is present
|
|
78
|
+
* (§5.4). They verify the self-certifying pairwise DID + its VP signature;
|
|
79
|
+
* they still reveal nothing that links the user across verifiers.
|
|
80
|
+
*/
|
|
81
|
+
checks: {
|
|
82
|
+
notExpired: boolean;
|
|
83
|
+
notUsed: boolean;
|
|
84
|
+
challengeIdMatches: boolean;
|
|
85
|
+
noForbiddenDisclosure: boolean;
|
|
86
|
+
requiredDisclosed: boolean;
|
|
87
|
+
epochKeyResolved: boolean;
|
|
88
|
+
proofValid: boolean;
|
|
89
|
+
/** Present only when a pairwise attachment is included. */
|
|
90
|
+
pairwiseBindingValid?: boolean;
|
|
91
|
+
pairwiseSignatureValid?: boolean;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* The verified pairwise DID (re-recognition handle) when the presentation
|
|
95
|
+
* carried a valid pairwise attachment. The verifier stores/matches this
|
|
96
|
+
* keyed by its own verifier id — the seam mechanism C swaps into later
|
|
97
|
+
* without the RP re-integrating.
|
|
98
|
+
*/
|
|
99
|
+
pairwiseDid?: string;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Verify a DID-less selective-disclosure presentation against a challenge.
|
|
103
|
+
*
|
|
104
|
+
* Checks (fail-fast):
|
|
105
|
+
* 1. challenge not expired, not already used
|
|
106
|
+
* 2. envelope answers THIS challenge
|
|
107
|
+
* 3. no always-hidden index is disclosed (subject_did / verification_id /
|
|
108
|
+
* document_hash) — enforced even when the proof itself is valid
|
|
109
|
+
* 4. every required index is disclosed
|
|
110
|
+
* 5. the issuer key for the envelope's epoch resolves (current or grace)
|
|
111
|
+
* 6. the BBS proof verifies under the RECONSTRUCTED header
|
|
112
|
+
* (issuerDid + challenge.credentialType + envelope.epoch — never a
|
|
113
|
+
* header taken from the envelope) and the ph recomputed from the
|
|
114
|
+
* verifier's own domain + stored nonce
|
|
115
|
+
*
|
|
116
|
+
* Uses ONLY the stateless issuer-key check — never a per-credential
|
|
117
|
+
* chain lookup (`verifyCredentialProof` leaks subject_did; BD-5).
|
|
118
|
+
*/
|
|
119
|
+
export declare function verifyBbsPresentation(challenge: BbsChallenge, presentation: BbsSelectiveDisclosurePresentation, opts: BbsVerifyOptions): Promise<BbsPresentationVerificationResult>;
|
|
45
120
|
//# sourceMappingURL=verify.d.ts.map
|
package/dist/verify.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7D,OAAO,EAEL,KAAK,kCAAkC,EACxC,MAAM,eAAe,CAAA;AAGtB,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,4CAA4C;IAC5C,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,oBAAoB,CAAC,EAAE,OAAO,EAAE,GAAG,SAAS,CAAA;IAC5C,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,MAAM,CAAA;QACf,iCAAiC;QACjC,SAAS,EAAE,MAAM,CAAA;QACjB,YAAY,EAAE,MAAM,CAAA;QACpB,0CAA0C;QAC1C,kBAAkB,EAAE,MAAM,CAAA;QAC1B,4DAA4D;QAC5D,GAAG,EAAE,MAAM,CAAA;KACZ,CAAA;CACF;AAED,MAAM,WAAW,8BAA8B;IAC7C,KAAK,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,EAAE;QACN,UAAU,EAAE,OAAO,CAAA;QACnB,aAAa,EAAE,OAAO,CAAA;QACtB,YAAY,EAAE,OAAO,CAAA;QACrB,cAAc,EAAE,OAAO,CAAA;KACxB,CAAA;CACF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,sBAAsB,EACpC,YAAY,EAAE,CAAC,oBAAoB,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,GAClE,OAAO,CAAC,8BAA8B,CAAC,CA8DzC;AAOD,MAAM,WAAW,gBAAgB;IAC/B,mFAAmF;IACnF,SAAS,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IAC3D;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;IACd;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CACjB;AAED,MAAM,WAAW,iCAAiC;IAChD,KAAK,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;;;OAQG;IACH,MAAM,EAAE;QACN,UAAU,EAAE,OAAO,CAAA;QACnB,OAAO,EAAE,OAAO,CAAA;QAChB,kBAAkB,EAAE,OAAO,CAAA;QAC3B,qBAAqB,EAAE,OAAO,CAAA;QAC9B,iBAAiB,EAAE,OAAO,CAAA;QAC1B,gBAAgB,EAAE,OAAO,CAAA;QACzB,UAAU,EAAE,OAAO,CAAA;QACnB,2DAA2D;QAC3D,oBAAoB,CAAC,EAAE,OAAO,CAAA;QAC9B,sBAAsB,CAAC,EAAE,OAAO,CAAA;KACjC,CAAA;IACD;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAUD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,YAAY,EACvB,YAAY,EAAE,kCAAkC,EAChD,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,iCAAiC,CAAC,CA4H5C"}
|
package/dist/verify.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import * as ed from '@noble/ed25519';
|
|
2
|
+
import { BbsProof, BbsPublicKey, buildV2Header, derivePresentationHeader } from '@solidus-network/bbs';
|
|
3
|
+
import { ALWAYS_HIDDEN_KYC_INDICES, } from './envelope.js';
|
|
4
|
+
import { asPairwiseAttachment, buildPairwiseDid } from './pairwise.js';
|
|
2
5
|
/**
|
|
3
6
|
* Verify a W3C Verifiable Presentation against a challenge.
|
|
4
7
|
*
|
|
@@ -60,4 +63,138 @@ export async function verifyPresentation(challenge, presentation, getPublicKey)
|
|
|
60
63
|
}
|
|
61
64
|
return { valid: true, checks };
|
|
62
65
|
}
|
|
66
|
+
function hexToBytes(hex) {
|
|
67
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
68
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
69
|
+
bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
70
|
+
}
|
|
71
|
+
return bytes;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Verify a DID-less selective-disclosure presentation against a challenge.
|
|
75
|
+
*
|
|
76
|
+
* Checks (fail-fast):
|
|
77
|
+
* 1. challenge not expired, not already used
|
|
78
|
+
* 2. envelope answers THIS challenge
|
|
79
|
+
* 3. no always-hidden index is disclosed (subject_did / verification_id /
|
|
80
|
+
* document_hash) — enforced even when the proof itself is valid
|
|
81
|
+
* 4. every required index is disclosed
|
|
82
|
+
* 5. the issuer key for the envelope's epoch resolves (current or grace)
|
|
83
|
+
* 6. the BBS proof verifies under the RECONSTRUCTED header
|
|
84
|
+
* (issuerDid + challenge.credentialType + envelope.epoch — never a
|
|
85
|
+
* header taken from the envelope) and the ph recomputed from the
|
|
86
|
+
* verifier's own domain + stored nonce
|
|
87
|
+
*
|
|
88
|
+
* Uses ONLY the stateless issuer-key check — never a per-credential
|
|
89
|
+
* chain lookup (`verifyCredentialProof` leaks subject_did; BD-5).
|
|
90
|
+
*/
|
|
91
|
+
export async function verifyBbsPresentation(challenge, presentation, opts) {
|
|
92
|
+
const checks = {
|
|
93
|
+
notExpired: false,
|
|
94
|
+
notUsed: false,
|
|
95
|
+
challengeIdMatches: false,
|
|
96
|
+
noForbiddenDisclosure: false,
|
|
97
|
+
requiredDisclosed: false,
|
|
98
|
+
epochKeyResolved: false,
|
|
99
|
+
proofValid: false,
|
|
100
|
+
};
|
|
101
|
+
const now = opts.now?.() ?? new Date();
|
|
102
|
+
checks.notExpired = now < new Date(challenge.expiresAt);
|
|
103
|
+
if (!checks.notExpired) {
|
|
104
|
+
return { valid: false, error: 'Challenge expired', checks };
|
|
105
|
+
}
|
|
106
|
+
checks.notUsed = opts.used !== true;
|
|
107
|
+
if (!checks.notUsed) {
|
|
108
|
+
return { valid: false, error: 'Challenge already used', checks };
|
|
109
|
+
}
|
|
110
|
+
checks.challengeIdMatches = presentation.challengeId === challenge.id;
|
|
111
|
+
if (!checks.challengeIdMatches) {
|
|
112
|
+
return { valid: false, error: 'Presentation answers a different challenge', checks };
|
|
113
|
+
}
|
|
114
|
+
const disclosedIndices = presentation.disclosed.map((d) => d.index);
|
|
115
|
+
checks.noForbiddenDisclosure = !disclosedIndices.some((i) => ALWAYS_HIDDEN_KYC_INDICES.includes(i));
|
|
116
|
+
if (!checks.noForbiddenDisclosure) {
|
|
117
|
+
return {
|
|
118
|
+
valid: false,
|
|
119
|
+
error: 'Presentation discloses an always-hidden field',
|
|
120
|
+
checks,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
checks.requiredDisclosed = challenge.requiredIndices.every((i) => disclosedIndices.includes(i));
|
|
124
|
+
if (!checks.requiredDisclosed) {
|
|
125
|
+
return { valid: false, error: 'A required claim was not disclosed', checks };
|
|
126
|
+
}
|
|
127
|
+
const issuerKeyHex = await opts.resolveIssuerKey(presentation.epoch);
|
|
128
|
+
checks.epochKeyResolved = typeof issuerKeyHex === 'string' && issuerKeyHex.length === 192;
|
|
129
|
+
if (!checks.epochKeyResolved || !issuerKeyHex) {
|
|
130
|
+
return { valid: false, error: 'No issuer key for this epoch (unknown or lapsed)', checks };
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
// Reconstruct — never trust — the header and the presentation header.
|
|
134
|
+
const header = buildV2Header(opts.issuerDid, challenge.credentialType, presentation.epoch);
|
|
135
|
+
const ph = derivePresentationHeader(challenge.domain, hexToBytes(challenge.nonce));
|
|
136
|
+
const sorted = [...presentation.disclosed].sort((a, b) => a.index - b.index);
|
|
137
|
+
checks.proofValid = await BbsProof.fromHex(presentation.proof).verify({
|
|
138
|
+
pk: BbsPublicKey.fromHex(issuerKeyHex),
|
|
139
|
+
header,
|
|
140
|
+
presentationHeader: ph,
|
|
141
|
+
disclosedIndices: sorted.map((d) => d.index),
|
|
142
|
+
disclosedMessages: sorted.map((d) => hexToBytes(d.message)),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
checks.proofValid = false;
|
|
147
|
+
}
|
|
148
|
+
if (!checks.proofValid) {
|
|
149
|
+
return { valid: false, error: 'Proof verification failed', checks };
|
|
150
|
+
}
|
|
151
|
+
// ---- Re-recognition (mechanism B) — only when a pairwise attachment rides
|
|
152
|
+
// the envelope. The one-shot proof above already passed; this adds the
|
|
153
|
+
// "same returning user at this verifier" binding without any on-chain
|
|
154
|
+
// anchor and without leaking a cross-verifier handle.
|
|
155
|
+
if (presentation.pairwiseDid !== null) {
|
|
156
|
+
checks.pairwiseBindingValid = false;
|
|
157
|
+
checks.pairwiseSignatureValid = false;
|
|
158
|
+
if (!opts.network) {
|
|
159
|
+
return { valid: false, error: 'Pairwise attachment requires a configured verifier network', checks };
|
|
160
|
+
}
|
|
161
|
+
const attachment = asPairwiseAttachment(presentation.pairwiseProof);
|
|
162
|
+
if (!attachment) {
|
|
163
|
+
return { valid: false, error: 'Malformed pairwise attachment', checks };
|
|
164
|
+
}
|
|
165
|
+
// 1. Binding: the claimed pairwise DID must be the self-certifying DID of
|
|
166
|
+
// the attached public key (under the verifier's own network), and the
|
|
167
|
+
// VP's holder must equal it. A stolen/forged DID fails here.
|
|
168
|
+
let pubkey;
|
|
169
|
+
try {
|
|
170
|
+
pubkey = hexToBytes(attachment.publicKeyHex);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return { valid: false, error: 'Pairwise public key is not valid hex', checks };
|
|
174
|
+
}
|
|
175
|
+
const recomputedDid = buildPairwiseDid(opts.network, pubkey);
|
|
176
|
+
checks.pairwiseBindingValid =
|
|
177
|
+
recomputedDid === presentation.pairwiseDid && attachment.vp.holder === presentation.pairwiseDid;
|
|
178
|
+
if (!checks.pairwiseBindingValid) {
|
|
179
|
+
return { valid: false, error: 'Pairwise DID does not bind to its public key', checks };
|
|
180
|
+
}
|
|
181
|
+
// 2. Signature: reuse the shipped DID-bound `verifyPresentation` verbatim,
|
|
182
|
+
// resolving the self-certified key. holderMatches + nonceMatches +
|
|
183
|
+
// signatureValid all run inside it.
|
|
184
|
+
const pairwiseChallenge = {
|
|
185
|
+
id: challenge.id,
|
|
186
|
+
did: presentation.pairwiseDid,
|
|
187
|
+
nonce: challenge.nonce,
|
|
188
|
+
issuedAt: challenge.issuedAt,
|
|
189
|
+
expiresAt: challenge.expiresAt,
|
|
190
|
+
};
|
|
191
|
+
const vpResult = await verifyPresentation(pairwiseChallenge, attachment.vp, async () => pubkey);
|
|
192
|
+
checks.pairwiseSignatureValid = vpResult.valid;
|
|
193
|
+
if (!checks.pairwiseSignatureValid) {
|
|
194
|
+
return { valid: false, error: `Pairwise VP invalid: ${vpResult.error ?? 'signature'}`, checks };
|
|
195
|
+
}
|
|
196
|
+
return { valid: true, checks, pairwiseDid: presentation.pairwiseDid };
|
|
197
|
+
}
|
|
198
|
+
return { valid: true, checks };
|
|
199
|
+
}
|
|
63
200
|
//# sourceMappingURL=verify.js.map
|
package/dist/verify.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidus-network/auth",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "DID-based authentication for the Solidus Network protocol — Ed25519 challenge / W3C Verifiable Presentation verification primitives.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|