@ixo/credentials-kit 0.0.0-stage → 0.1.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.
- package/README.md +469 -2
- package/dist/chunk-2SF56NEJ.js +1053 -0
- package/dist/chunk-2SF56NEJ.js.map +1 -0
- package/dist/chunk-46VRPQPD.js +176 -0
- package/dist/chunk-46VRPQPD.js.map +1 -0
- package/dist/chunk-AQ3IINHC.js +138 -0
- package/dist/chunk-AQ3IINHC.js.map +1 -0
- package/dist/chunk-FSKCDHZ7.js +613 -0
- package/dist/chunk-FSKCDHZ7.js.map +1 -0
- package/dist/chunk-I6GBYFIJ.js +89 -0
- package/dist/chunk-I6GBYFIJ.js.map +1 -0
- package/dist/chunk-JBUX6V2Q.js +210 -0
- package/dist/chunk-JBUX6V2Q.js.map +1 -0
- package/dist/chunk-Q3MZNMCK.js +183 -0
- package/dist/chunk-Q3MZNMCK.js.map +1 -0
- package/dist/chunk-RKKRO4ZK.js +133 -0
- package/dist/chunk-RKKRO4ZK.js.map +1 -0
- package/dist/chunk-STMMFNVW.js +391 -0
- package/dist/chunk-STMMFNVW.js.map +1 -0
- package/dist/chunk-VXKONZK4.js +189 -0
- package/dist/chunk-VXKONZK4.js.map +1 -0
- package/dist/chunk-WIEGZFUO.js +337 -0
- package/dist/chunk-WIEGZFUO.js.map +1 -0
- package/dist/chunk-WND2LZ4O.js +66 -0
- package/dist/chunk-WND2LZ4O.js.map +1 -0
- package/dist/chunk-XREZY53L.js +260 -0
- package/dist/chunk-XREZY53L.js.map +1 -0
- package/dist/chunk-ZZY4WGQ6.js +13 -0
- package/dist/chunk-ZZY4WGQ6.js.map +1 -0
- package/dist/did.d.ts +182 -0
- package/dist/did.js +6 -0
- package/dist/did.js.map +1 -0
- package/dist/display.d.ts +117 -0
- package/dist/display.js +5 -0
- package/dist/display.js.map +1 -0
- package/dist/errors-BKQmNpXX.d.ts +11 -0
- package/dist/holder.d.ts +85 -0
- package/dist/holder.js +8 -0
- package/dist/holder.js.map +1 -0
- package/dist/http-BhuJ_-RZ.d.ts +25 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/jose.d.ts +134 -0
- package/dist/jose.js +4 -0
- package/dist/jose.js.map +1 -0
- package/dist/oid4vci.d.ts +428 -0
- package/dist/oid4vci.js +11 -0
- package/dist/oid4vci.js.map +1 -0
- package/dist/oid4vp.d.ts +416 -0
- package/dist/oid4vp.js +11 -0
- package/dist/oid4vp.js.map +1 -0
- package/dist/profile.d.ts +218 -0
- package/dist/profile.js +4 -0
- package/dist/profile.js.map +1 -0
- package/dist/sdjwt.d.ts +133 -0
- package/dist/sdjwt.js +5 -0
- package/dist/sdjwt.js.map +1 -0
- package/dist/sender.d.ts +8 -0
- package/dist/sender.js +7 -0
- package/dist/sender.js.map +1 -0
- package/dist/status.d.ts +108 -0
- package/dist/status.js +7 -0
- package/dist/status.js.map +1 -0
- package/dist/util-DOTEZgTt.d.ts +3 -0
- package/dist/verify.d.ts +144 -0
- package/dist/verify.js +9 -0
- package/dist/verify.js.map +1 -0
- package/dist/webhook.d.ts +62 -0
- package/dist/webhook.js +7 -0
- package/dist/webhook.js.map +1 -0
- package/package.json +168 -4
package/dist/sdjwt.d.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { CredentialsKitError } from './index.js';
|
|
2
|
+
import { JsonObject } from './index.js';
|
|
3
|
+
|
|
4
|
+
/** Largest SD-JWT (with KB-JWT) the kit parses: 32 KiB. */
|
|
5
|
+
declare const MAX_SD_JWT_LENGTH: number;
|
|
6
|
+
/** Most disclosures one SD-JWT may carry. */
|
|
7
|
+
declare const MAX_DISCLOSURES = 256;
|
|
8
|
+
interface Disclosure {
|
|
9
|
+
/** The disclosure exactly as it appears in the SD-JWT (base64url). */
|
|
10
|
+
raw: string;
|
|
11
|
+
salt: string;
|
|
12
|
+
/** The claim name; null for an array-element disclosure (`[salt, value]`). */
|
|
13
|
+
name: string | null;
|
|
14
|
+
value: unknown;
|
|
15
|
+
/** base64url(SHA-256(raw)), the value found in `_sd`. */
|
|
16
|
+
digest: string;
|
|
17
|
+
}
|
|
18
|
+
interface ParsedSdJwt {
|
|
19
|
+
/** The issuer-signed JWT. */
|
|
20
|
+
jwt: string;
|
|
21
|
+
header: JsonObject;
|
|
22
|
+
payload: JsonObject;
|
|
23
|
+
disclosures: Disclosure[];
|
|
24
|
+
/** The Key Binding JWT, when the input was an SD-JWT+KB. */
|
|
25
|
+
kbJwt?: string;
|
|
26
|
+
}
|
|
27
|
+
type SdJwtErrorCode = 'malformed' | 'bad_disclosure' | 'unknown_claim';
|
|
28
|
+
/**
|
|
29
|
+
* Claim names refused anywhere in a credential (plaintext or disclosed): they would change an
|
|
30
|
+
* object's prototype or shadow its constructor in JavaScript consumers (SPEC §13.3).
|
|
31
|
+
*/
|
|
32
|
+
declare const REFUSED_CLAIM_NAMES: ReadonlySet<string>;
|
|
33
|
+
/** Deepest nesting `processDisclosures` walks. */
|
|
34
|
+
declare const MAX_CLAIM_DEPTH = 32;
|
|
35
|
+
declare class SdJwtError extends CredentialsKitError {
|
|
36
|
+
readonly code: SdJwtErrorCode;
|
|
37
|
+
constructor(code: SdJwtErrorCode);
|
|
38
|
+
}
|
|
39
|
+
/** base64url(SHA-256(US-ASCII bytes of `s`)), the SD-JWT digest (sha-256 only). */
|
|
40
|
+
declare function sdDigest(s: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* Parse an SD-JWT or SD-JWT+KB. Throws `SdJwtError` (`malformed`, `bad_disclosure`). Does not
|
|
43
|
+
* verify anything cryptographic and does not check that disclosures are referenced.
|
|
44
|
+
*/
|
|
45
|
+
declare function parseSdJwt(str: string): ParsedSdJwt;
|
|
46
|
+
/**
|
|
47
|
+
* `<jwt>~<d1>~…~<dn>~` plus the KB-JWT when given. With no KB-JWT the result ends with `~`
|
|
48
|
+
* (RFC 9901 §4).
|
|
49
|
+
*/
|
|
50
|
+
declare function serializeSdJwt(jwt: string, disclosureRaws: readonly string[], kbJwt?: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* The raw disclosures for the TOP-LEVEL claim `names`: object-property disclosures whose digest
|
|
53
|
+
* is listed in the payload's own `_sd`, in the order the SD-JWT carries them. A claim name is an
|
|
54
|
+
* exact path of one segment: a disclosure of the same name nested inside another claim is never
|
|
55
|
+
* selected (it would release a value nobody asked for), and the nested disclosures inside a
|
|
56
|
+
* selected value are not released either. Throws `unknown_claim` when a name has no top-level
|
|
57
|
+
* disclosure, so a caller never silently presents less (or something else) than it meant to.
|
|
58
|
+
*/
|
|
59
|
+
declare function selectDisclosures(parsed: ParsedSdJwt, names: readonly string[]): string[];
|
|
60
|
+
/**
|
|
61
|
+
* The KB-JWT `sd_hash` over `<jwt>~<d1>~…~<dn>~` (must end with `~`, as presented: the
|
|
62
|
+
* issuer-signed JWT and the chosen disclosures, without the KB-JWT).
|
|
63
|
+
*/
|
|
64
|
+
declare function sdHash(jwtAndDisclosures: string): string;
|
|
65
|
+
interface ProcessedSdJwt {
|
|
66
|
+
/**
|
|
67
|
+
* The payload with every presented disclosure put in place, recursively, and every `_sd`,
|
|
68
|
+
* `_sd_alg` and unmatched `{"...": digest}` array element removed (RFC 9901 §7.1 step 3).
|
|
69
|
+
*/
|
|
70
|
+
claims: JsonObject;
|
|
71
|
+
/** Names of the top-level claims that came from a disclosure, in payload order. */
|
|
72
|
+
disclosed: string[];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* RFC 9901 §7.1 steps 3–5 over an (already signature-checked) payload: every `_sd` digest list
|
|
76
|
+
* in every object and every `{"...": digest}` element of every array, recursively into
|
|
77
|
+
* disclosed values. Throws `SdJwtError('bad_disclosure')` when a digest appears more than once,
|
|
78
|
+
* a disclosure matches no digest (or is presented twice), an object-property disclosure is
|
|
79
|
+
* found in an array (or the reverse), a disclosed name is `_sd`, `...` or `_sd_alg`, or a
|
|
80
|
+
* disclosed name already exists at its level. Throws `SdJwtError('malformed')` for a malformed
|
|
81
|
+
* `_sd` or `...` structure, a claim named `__proto__` / `constructor` / `prototype`, `_sd_alg`
|
|
82
|
+
* below the top level, or nesting deeper than `MAX_CLAIM_DEPTH`. `sha-256` digests only (the
|
|
83
|
+
* caller checks `_sd_alg`).
|
|
84
|
+
*/
|
|
85
|
+
declare function processDisclosures(payload: JsonObject, disclosures: readonly Disclosure[]): ProcessedSdJwt;
|
|
86
|
+
/**
|
|
87
|
+
* The `ixo.credential` payload the portal / jambo / Self web app store
|
|
88
|
+
* (`SdJwtCredentialRecord`): the raw SD-JWT is authoritative; `claims` is a display cache with
|
|
89
|
+
* each claim's disclosure rule.
|
|
90
|
+
*/
|
|
91
|
+
interface SdJwtCredentialRecord {
|
|
92
|
+
recordType: 'SdJwtCredentialRecord';
|
|
93
|
+
format: 'dc+sd-jwt';
|
|
94
|
+
credential: string;
|
|
95
|
+
issuerDid: string;
|
|
96
|
+
issuerName?: string;
|
|
97
|
+
holderDid: string;
|
|
98
|
+
vct: string;
|
|
99
|
+
issuedAt: string;
|
|
100
|
+
expiresAt?: string;
|
|
101
|
+
claims: Record<string, {
|
|
102
|
+
value: unknown;
|
|
103
|
+
sd: 'never' | 'allowed' | 'always';
|
|
104
|
+
}>;
|
|
105
|
+
holderBinding: boolean;
|
|
106
|
+
}
|
|
107
|
+
interface CredentialRecordOptions {
|
|
108
|
+
/** Display name of the issuer (Self stores `'IXO Identity'`). Omitted when not given. */
|
|
109
|
+
issuerName?: string;
|
|
110
|
+
/** When given, the credential's `sub` must equal it (it was issued to this holder). */
|
|
111
|
+
holderDid?: string;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The Vault record of an issued SD-JWT VC, byte-identical (same members, same order) to Self's
|
|
115
|
+
* `toCredentialRecord` for the same flat credential, so the Hub and the browser de-duplicate by
|
|
116
|
+
* CID. Every field comes from the SD-JWT itself; the same credential always yields the same
|
|
117
|
+
* record. `claims` holds the TOP-LEVEL claims only: plaintext ones (`sd: 'never'`), then the
|
|
118
|
+
* top-level disclosures in the order the SD-JWT carries them (`sd: 'allowed'`). A nested
|
|
119
|
+
* disclosure stays inside its parent's value (RFC 9901 §7.1 processing), so it can never stand
|
|
120
|
+
* in for, or overwrite, a top-level claim of the same name. Throws `SdJwtError('malformed')`
|
|
121
|
+
* when `iss`, `sub`, `vct` or `iat` is missing, when `holderDid` does not match, or when a claim
|
|
122
|
+
* is named `__proto__`, `constructor` or `prototype`, and `SdJwtError('bad_disclosure')` (or
|
|
123
|
+
* `malformed`) when the disclosures break RFC 9901 §7.1 (unreferenced, repeated, overwriting).
|
|
124
|
+
*
|
|
125
|
+
* This only PARSES: it does not check the issuer's signature, `cnf` or validity. A wallet must
|
|
126
|
+
* call `verifyIssuedCredential` (kit `verify`) first and store only what passed (RFC 9901 §7.2).
|
|
127
|
+
*/
|
|
128
|
+
declare function credentialRecordFromSdJwt(sdjwt: string, opts?: CredentialRecordOptions): SdJwtCredentialRecord;
|
|
129
|
+
/** `"sha256:" + hex(sha256(JSON.stringify(canonicalize(record))))`, the Vault's dedup key. */
|
|
130
|
+
declare function credentialCid(record: unknown): string;
|
|
131
|
+
|
|
132
|
+
export { MAX_CLAIM_DEPTH, MAX_DISCLOSURES, MAX_SD_JWT_LENGTH, REFUSED_CLAIM_NAMES, SdJwtError, credentialCid, credentialRecordFromSdJwt, parseSdJwt, processDisclosures, sdDigest, sdHash, selectDisclosures, serializeSdJwt };
|
|
133
|
+
export type { CredentialRecordOptions, Disclosure, ParsedSdJwt, ProcessedSdJwt, SdJwtCredentialRecord, SdJwtErrorCode };
|
package/dist/sdjwt.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { MAX_CLAIM_DEPTH, MAX_DISCLOSURES, MAX_SD_JWT_LENGTH, REFUSED_CLAIM_NAMES, SdJwtError, credentialCid, credentialRecordFromSdJwt, parseSdJwt, processDisclosures, sdDigest, sdHash, selectDisclosures, serializeSdJwt } from './chunk-XREZY53L.js';
|
|
2
|
+
import './chunk-VXKONZK4.js';
|
|
3
|
+
import './chunk-AQ3IINHC.js';
|
|
4
|
+
//# sourceMappingURL=sdjwt.js.map
|
|
5
|
+
//# sourceMappingURL=sdjwt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"sdjwt.js"}
|
package/dist/sender.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sender key of `did`: the DID itself when it is a did:ixo person or entity DID, or a did:web
|
|
3
|
+
* in canonical form. Null for any other did:ixo or did:web spelling (refuse it) and for anything
|
|
4
|
+
* that is not a did:ixo or did:web.
|
|
5
|
+
*/
|
|
6
|
+
declare function senderKeyOf(did: string): string | null;
|
|
7
|
+
|
|
8
|
+
export { senderKeyOf };
|
package/dist/sender.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"sender.js"}
|
package/dist/status.d.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { DidDocument } from './did.js';
|
|
2
|
+
import { CredentialsKitError } from './index.js';
|
|
3
|
+
import { Signer } from './jose.js';
|
|
4
|
+
import './http-BhuJ_-RZ.js';
|
|
5
|
+
import './util-DOTEZgTt.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `@ixo/credentials-kit/status` — IETF Token Status List (draft-ietf-oauth-status-list), JWT
|
|
9
|
+
* form, as Self issues it today (`ixo-self/worker/src/service/credential/status-list.ts`):
|
|
10
|
+
*
|
|
11
|
+
* header { alg: 'EdDSA', typ: 'statuslist+jwt', kid: <issuer assertionMethod id> }
|
|
12
|
+
* payload { sub: <the list's public URI>, iss: <issuer DID>, iat, exp, ttl,
|
|
13
|
+
* status_list: { bits: 1, lst: base64url(ZLIB-DEFLATE(bytes)) } }
|
|
14
|
+
* entry i byte i >> 3, bit i & 7 (least significant bit first); 0 = VALID, 1 = INVALID.
|
|
15
|
+
*
|
|
16
|
+
* DEFLATE/INFLATE use `CompressionStream` / `DecompressionStream('deflate')` (the ZLIB format,
|
|
17
|
+
* RFC 1950), available in workerd, Node 22 and browsers. Inflation is capped.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
declare const STATUS_LIST_TYP = "statuslist+jwt";
|
|
21
|
+
declare const STATUS_LIST_MEDIA_TYPE = "application/statuslist+jwt";
|
|
22
|
+
/** Largest status list a verifier inflates (1 MiB = 8,388,608 one-bit entries). */
|
|
23
|
+
declare const MAX_STATUS_LIST_BYTES: number;
|
|
24
|
+
/** Largest status list token accepted (64 KiB, the Credentials worker's limit). */
|
|
25
|
+
declare const MAX_STATUS_LIST_TOKEN_LENGTH: number;
|
|
26
|
+
declare const STATUS_VALID = 0;
|
|
27
|
+
declare const STATUS_INVALID = 1;
|
|
28
|
+
declare const STATUS_SUSPENDED = 2;
|
|
29
|
+
type StatusListErrorCode = 'malformed' | 'wrong_type' | 'wrong_subject' | 'wrong_issuer' | 'issuer_key_unpublished' | 'bad_signature' | 'expired' | 'not_yet_valid' | 'rolled_back' | 'too_large' | 'out_of_range';
|
|
30
|
+
declare class StatusListError extends CredentialsKitError {
|
|
31
|
+
readonly code: StatusListErrorCode;
|
|
32
|
+
constructor(code: StatusListErrorCode);
|
|
33
|
+
}
|
|
34
|
+
/** A new, all-VALID list of `bytes` bytes (`bytes * 8` entries). */
|
|
35
|
+
declare function newStatusBits(bytes: number): Uint8Array;
|
|
36
|
+
/** Set entry `idx` to INVALID (1), in place. Idempotent. */
|
|
37
|
+
declare function setStatusBit(bits: Uint8Array, idx: number): void;
|
|
38
|
+
/** The 1-bit entry at `idx` (0 VALID, 1 INVALID). */
|
|
39
|
+
declare function getStatusBit(bits: Uint8Array, idx: number): 0 | 1;
|
|
40
|
+
/**
|
|
41
|
+
* The status value at `idx` of a list with `bitsPerEntry` bits per entry (1, 2, 4 or 8;
|
|
42
|
+
* default 1). Throws `out_of_range` for an index past the list.
|
|
43
|
+
*/
|
|
44
|
+
declare function statusAt(bits: Uint8Array, idx: number, bitsPerEntry?: 1 | 2 | 4 | 8): number;
|
|
45
|
+
/**
|
|
46
|
+
* A uniformly random index in `[0, size)` whose bit is 0 in `used`, a bitmap of indices
|
|
47
|
+
* already handed out (same LSB-first layout; pass the issuer's allocation bitmap, not the
|
|
48
|
+
* revocation bits, because a VALID entry is also 0 there). Random indices give herd privacy
|
|
49
|
+
* (TSL §12.5). Throws `out_of_range` when every index is taken.
|
|
50
|
+
*/
|
|
51
|
+
declare function randomFreeIndex(used: Uint8Array, size: number): number;
|
|
52
|
+
/** ZLIB (RFC 1950) DEFLATE, as the `lst` member requires. */
|
|
53
|
+
declare function deflateStatusList(bytes: Uint8Array): Promise<Uint8Array>;
|
|
54
|
+
/** ZLIB INFLATE, at most `max` output bytes (default `MAX_STATUS_LIST_BYTES`). */
|
|
55
|
+
declare function inflateStatusList(bytes: Uint8Array, max?: number): Promise<Uint8Array>;
|
|
56
|
+
interface CreateStatusListTokenInput {
|
|
57
|
+
/** The list bytes (1 bit per entry). */
|
|
58
|
+
bits: Uint8Array;
|
|
59
|
+
/** The list's public URI, exactly as credentials carry it in `status.status_list.uri`. */
|
|
60
|
+
uri: string;
|
|
61
|
+
/** The issuer DID. */
|
|
62
|
+
iss: string;
|
|
63
|
+
/** Signs with the issuer's assertionMethod key; `signer.kid` becomes the header `kid`. */
|
|
64
|
+
signer: Signer;
|
|
65
|
+
/** Seconds a verifier may cache the token. */
|
|
66
|
+
ttl: number;
|
|
67
|
+
/** Expiry, unix seconds. */
|
|
68
|
+
exp: number;
|
|
69
|
+
/** Issued at, unix seconds; default now. */
|
|
70
|
+
iat?: number;
|
|
71
|
+
}
|
|
72
|
+
/** A signed `statuslist+jwt` (bits 1). */
|
|
73
|
+
declare function createStatusListToken(input: CreateStatusListTokenInput): Promise<string>;
|
|
74
|
+
interface VerifyStatusListTokenOptions {
|
|
75
|
+
/** The URI the credential points to; the token's `sub` must equal it exactly. */
|
|
76
|
+
uri: string;
|
|
77
|
+
/** The issuer's resolved DID document; the token must be signed by its assertionMethod key. */
|
|
78
|
+
issuerDoc: DidDocument;
|
|
79
|
+
/** ms since the epoch; default `Date.now()`. */
|
|
80
|
+
now?: number;
|
|
81
|
+
/**
|
|
82
|
+
* Refuse a token issued before this (unix seconds), e.g. the `iat` of the newest token already
|
|
83
|
+
* accepted for this URI (`rolled_back`).
|
|
84
|
+
*/
|
|
85
|
+
minIat?: number;
|
|
86
|
+
}
|
|
87
|
+
interface VerifiedStatusList {
|
|
88
|
+
/** The inflated list bytes. */
|
|
89
|
+
bits: Uint8Array;
|
|
90
|
+
/** Bits per entry (1, 2, 4 or 8); pass to `statusAt`. */
|
|
91
|
+
bitsPerEntry: 1 | 2 | 4 | 8;
|
|
92
|
+
ttl: number | null;
|
|
93
|
+
exp: number | null;
|
|
94
|
+
iat: number;
|
|
95
|
+
/** The header `kid` that verified. */
|
|
96
|
+
kid: string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Verify a `statuslist+jwt` for `uri` issued by `issuerDoc.id`: typ, Ed25519 signature (`alg`
|
|
100
|
+
* `EdDSA` or `Ed25519`) by the issuer's assertionMethod key named by `kid`, `sub` = `uri`, `iss`
|
|
101
|
+
* (when present) = the issuer, `iat` present (and ≥ `minIat`), `exp` not past and `nbf` not in
|
|
102
|
+
* the future (2 min skew), `bits` ∈ {1,2,4,8}, `lst` inflates within the cap. Throws
|
|
103
|
+
* `StatusListError`.
|
|
104
|
+
*/
|
|
105
|
+
declare function verifyStatusListToken(token: string, opts: VerifyStatusListTokenOptions): Promise<VerifiedStatusList>;
|
|
106
|
+
|
|
107
|
+
export { MAX_STATUS_LIST_BYTES, MAX_STATUS_LIST_TOKEN_LENGTH, STATUS_INVALID, STATUS_LIST_MEDIA_TYPE, STATUS_LIST_TYP, STATUS_SUSPENDED, STATUS_VALID, StatusListError, createStatusListToken, deflateStatusList, getStatusBit, inflateStatusList, newStatusBits, randomFreeIndex, setStatusBit, statusAt, verifyStatusListToken };
|
|
108
|
+
export type { CreateStatusListTokenInput, StatusListErrorCode, VerifiedStatusList, VerifyStatusListTokenOptions };
|
package/dist/status.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { MAX_STATUS_LIST_BYTES, MAX_STATUS_LIST_TOKEN_LENGTH, STATUS_INVALID, STATUS_LIST_MEDIA_TYPE, STATUS_LIST_TYP, STATUS_SUSPENDED, STATUS_VALID, StatusListError, createStatusListToken, deflateStatusList, getStatusBit, inflateStatusList, newStatusBits, randomFreeIndex, setStatusBit, statusAt, verifyStatusListToken } from './chunk-JBUX6V2Q.js';
|
|
2
|
+
import './chunk-WIEGZFUO.js';
|
|
3
|
+
import './chunk-Q3MZNMCK.js';
|
|
4
|
+
import './chunk-VXKONZK4.js';
|
|
5
|
+
import './chunk-AQ3IINHC.js';
|
|
6
|
+
//# sourceMappingURL=status.js.map
|
|
7
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"status.js"}
|
package/dist/verify.d.ts
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { DidResolver, KeyValueCache } from './did.js';
|
|
2
|
+
import { CredentialsKitError } from './index.js';
|
|
3
|
+
import { FetchLike } from './did.js';
|
|
4
|
+
import { JsonObject } from './index.js';
|
|
5
|
+
import { Ed25519PublicJwk } from './jose.js';
|
|
6
|
+
|
|
7
|
+
type VerifyFailureCode = 'malformed' | 'untrusted_issuer' | 'wrong_type' | 'issuer_key_unpublished' | 'bad_signature' | 'expired' | 'not_yet_valid' | 'bad_disclosure' | 'wrong_subject' | 'key_binding_required' | 'bad_key_binding' | 'revoked' | 'no_status';
|
|
8
|
+
declare const VERIFY_FAILURE_DETAILS: Record<VerifyFailureCode, string>;
|
|
9
|
+
/** Checks a KB-JWT nonce: the exact value, or a callback (e.g. a single-use nonce store). */
|
|
10
|
+
type NonceCheck = string | ((nonce: string) => boolean | Promise<boolean>);
|
|
11
|
+
interface VerifyPolicy {
|
|
12
|
+
/** Accepted issuer DIDs, or a predicate. */
|
|
13
|
+
issuers: readonly string[] | ((did: string) => boolean);
|
|
14
|
+
/** Accepted `vct` values. */
|
|
15
|
+
vcts: readonly string[];
|
|
16
|
+
/**
|
|
17
|
+
* The KB-JWT `aud`: the verifier's full OpenID4VP client_id (or the audience your own protocol
|
|
18
|
+
* fixed). REQUIRED when key binding is required.
|
|
19
|
+
*/
|
|
20
|
+
audience?: string;
|
|
21
|
+
/**
|
|
22
|
+
* The nonce the KB-JWT must carry: the one you issued for THIS check, or a callback that
|
|
23
|
+
* accepts each issued nonce once. REQUIRED when key binding is required.
|
|
24
|
+
*/
|
|
25
|
+
nonce?: NonceCheck;
|
|
26
|
+
/** Default true. */
|
|
27
|
+
requireKeyBinding?: boolean;
|
|
28
|
+
/** Default true. */
|
|
29
|
+
requireStatus?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* The credential's `sub` must equal this (`wrong_subject`). Also what makes a failure
|
|
32
|
+
* `holderAuthenticated`: without it no answer is bound to a person you expected, so
|
|
33
|
+
* `holderAuthenticated` is always false (see `keyBindingValid`).
|
|
34
|
+
*/
|
|
35
|
+
expectedSubject?: string;
|
|
36
|
+
/** ms since the epoch; default `Date.now()`. */
|
|
37
|
+
now?: number;
|
|
38
|
+
}
|
|
39
|
+
interface VerifyDeps {
|
|
40
|
+
resolver: DidResolver;
|
|
41
|
+
fetch: FetchLike;
|
|
42
|
+
/**
|
|
43
|
+
* Verified status list tokens (pass a KV binding or `createMemoryCache()`). Without one every
|
|
44
|
+
* verification fetches the list.
|
|
45
|
+
*/
|
|
46
|
+
statusCache?: KeyValueCache;
|
|
47
|
+
/** Fetch a status list on a loopback host over http (local development only; needs `allowLocal`). */
|
|
48
|
+
allowHttp?: boolean;
|
|
49
|
+
/** Fetch a status list on a loopback / private / local-name host (local development only). */
|
|
50
|
+
allowLocal?: boolean;
|
|
51
|
+
}
|
|
52
|
+
interface VerifiedPresentation {
|
|
53
|
+
ok: true;
|
|
54
|
+
issuer: string;
|
|
55
|
+
subject: string;
|
|
56
|
+
vct: string;
|
|
57
|
+
/**
|
|
58
|
+
* The processed claims: plaintext claims (registered claims removed) plus the disclosed ones,
|
|
59
|
+
* with nested disclosures in place. Never contains `_sd`, `_sd_alg` or `...`.
|
|
60
|
+
*/
|
|
61
|
+
claims: JsonObject;
|
|
62
|
+
/** Names of the top-level claims that came from a disclosure. */
|
|
63
|
+
disclosed: string[];
|
|
64
|
+
/** The credential's `exp` (unix seconds). */
|
|
65
|
+
expiresAt: number | null;
|
|
66
|
+
/** Lower-case hex SHA-256 of the issuer-signed JWT: one value per credential, whatever is disclosed. */
|
|
67
|
+
credentialSha256: string;
|
|
68
|
+
/** The credential's `cnf.jwk`, when it has one. */
|
|
69
|
+
holderKey: Ed25519PublicJwk | null;
|
|
70
|
+
}
|
|
71
|
+
interface FailedPresentation {
|
|
72
|
+
ok: false;
|
|
73
|
+
code: VerifyFailureCode;
|
|
74
|
+
/** A fixed sentence for the code (no credential content). */
|
|
75
|
+
detail: string;
|
|
76
|
+
/**
|
|
77
|
+
* True only when the failure came AFTER the answer was bound to the person you expected: a
|
|
78
|
+
* trusted issuer's valid signature, the key binding for this nonce and audience, AND
|
|
79
|
+
* `policy.expectedSubject` given and equal to `sub`. The person really answered, and their
|
|
80
|
+
* answer does not meet the policy (wrong type, expired, revoked, …). False for everything a
|
|
81
|
+
* third party could have sent, always false without key binding, and always false without
|
|
82
|
+
* `expectedSubject` (a stranger's own credential would pass every other step).
|
|
83
|
+
*/
|
|
84
|
+
holderAuthenticated: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* True when the failure came after the KB-JWT verified for this nonce and audience with the
|
|
87
|
+
* presented credential's OWN `cnf` key (trusted issuer, valid signature). It proves that the
|
|
88
|
+
* holder of THAT credential answered, not that it is the person you meant: never close a
|
|
89
|
+
* check on it alone. Always false without key binding.
|
|
90
|
+
*/
|
|
91
|
+
keyBindingValid: boolean;
|
|
92
|
+
}
|
|
93
|
+
type VerifyResult = VerifiedPresentation | FailedPresentation;
|
|
94
|
+
type UnavailableSource = 'resolver' | 'status_list';
|
|
95
|
+
/** A dependency could not answer; nothing can be concluded about the credential (answer 503). */
|
|
96
|
+
declare class VerificationUnavailable extends CredentialsKitError {
|
|
97
|
+
readonly code: 'verification_unavailable';
|
|
98
|
+
readonly source: UnavailableSource;
|
|
99
|
+
/** A constant cause code (`unreachable`, `http_status`, `too_large`, a `StatusListError` code, …). */
|
|
100
|
+
readonly reason: string;
|
|
101
|
+
constructor(source: UnavailableSource, reason: string);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Verify an SD-JWT VC presentation (`<jwt>~<disclosures>~<kb-jwt>`). Returns
|
|
105
|
+
* `{ ok: true, … }` or `{ ok: false, code, detail, holderAuthenticated, keyBindingValid }`
|
|
106
|
+
* (`holderAuthenticated` needs `policy.expectedSubject`); throws
|
|
107
|
+
* `VerificationUnavailable` when a dependency cannot answer, and `TypeError` when key binding is
|
|
108
|
+
* required but `policy.audience` or `policy.nonce` is missing.
|
|
109
|
+
*/
|
|
110
|
+
declare function verifyPresentation(presentation: string, policy: VerifyPolicy, deps: VerifyDeps): Promise<VerifyResult>;
|
|
111
|
+
interface VerifyIssuedCredentialOptions {
|
|
112
|
+
/**
|
|
113
|
+
* The issuer DID the credential must name in `iss` (for example the DID that deposited the
|
|
114
|
+
* offer). REQUIRED. `false` accepts any issuer whose own DID document holds the signing key:
|
|
115
|
+
* UNSAFE, since the credential is then authentic but anyone may have issued it (a foreign
|
|
116
|
+
* issuer can mint your types); judge `result.issuer` yourself before storing or showing it.
|
|
117
|
+
*/
|
|
118
|
+
issuer: string | false;
|
|
119
|
+
/** The holder DID: the credential's `sub` must equal it. */
|
|
120
|
+
subject: string;
|
|
121
|
+
/** The key the wallet proved possession of: the credential's `cnf.jwk` must be exactly it. */
|
|
122
|
+
holderKey: Ed25519PublicJwk | Uint8Array;
|
|
123
|
+
/** The `vct` of the offered credential configuration (or the accepted list). */
|
|
124
|
+
vct: string | readonly string[];
|
|
125
|
+
resolver: DidResolver;
|
|
126
|
+
/** ms since the epoch; default `Date.now()`. */
|
|
127
|
+
now?: number;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Check a credential a wallet just received from an issuer, before storing it (RFC 9901 §7.2,
|
|
131
|
+
* SPEC §13.3): no KB-JWT, header and shape as `verifyPresentation`, `iss` = `opts.issuer` when
|
|
132
|
+
* given (`untrusted_issuer`), the issuer's signature by its `assertionMethod` key named by `kid`,
|
|
133
|
+
* every disclosure matching exactly one digest (recursive, `bad_disclosure`), `sub` =
|
|
134
|
+
* `opts.subject` (`wrong_subject`), `cnf.jwk` = `opts.holderKey` (`bad_key_binding`), `vct` as
|
|
135
|
+
* offered (`wrong_type`), `exp`/`nbf` (`expired`, `not_yet_valid`). The status list is not read
|
|
136
|
+
* (a just-issued credential); verifiers check it at presentation. Returns the same result shape
|
|
137
|
+
* as `verifyPresentation` (`holderAuthenticated` and `keyBindingValid` are always false); throws
|
|
138
|
+
* `VerificationUnavailable` when the issuer's DID document cannot be read and `TypeError` when
|
|
139
|
+
* `issuer` is neither a DID nor `false`.
|
|
140
|
+
*/
|
|
141
|
+
declare function verifyIssuedCredential(credential: string, opts: VerifyIssuedCredentialOptions): Promise<VerifyResult>;
|
|
142
|
+
|
|
143
|
+
export { VERIFY_FAILURE_DETAILS, VerificationUnavailable, verifyIssuedCredential, verifyPresentation };
|
|
144
|
+
export type { FailedPresentation, NonceCheck, UnavailableSource, VerifiedPresentation, VerifyDeps, VerifyFailureCode, VerifyIssuedCredentialOptions, VerifyPolicy, VerifyResult };
|
package/dist/verify.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { VERIFY_FAILURE_DETAILS, VerificationUnavailable, verifyIssuedCredential, verifyPresentation } from './chunk-STMMFNVW.js';
|
|
2
|
+
import './chunk-XREZY53L.js';
|
|
3
|
+
import './chunk-JBUX6V2Q.js';
|
|
4
|
+
import './chunk-WIEGZFUO.js';
|
|
5
|
+
import './chunk-Q3MZNMCK.js';
|
|
6
|
+
import './chunk-VXKONZK4.js';
|
|
7
|
+
import './chunk-AQ3IINHC.js';
|
|
8
|
+
//# sourceMappingURL=verify.js.map
|
|
9
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"verify.js"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { DidResolver } from './did.js';
|
|
2
|
+
import { Signer } from './jose.js';
|
|
3
|
+
import './errors-BKQmNpXX.js';
|
|
4
|
+
import './http-BhuJ_-RZ.js';
|
|
5
|
+
import './util-DOTEZgTt.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `@ixo/credentials-kit/webhook` — signed webhooks (Credentials worker → Hub, SPEC §5.4).
|
|
9
|
+
*
|
|
10
|
+
* Header value (`x-ixo-signature`): `t=<unix seconds>,kid=<DID URL>,sig=<base64url>` where `sig`
|
|
11
|
+
* is Ed25519 over the UTF-8 bytes of `"<t>.<body>"` and `body` is the exact request body string.
|
|
12
|
+
* The timestamp is inside the signed bytes. The verifier resolves `expectedDid`, takes the key
|
|
13
|
+
* `kid` names under `assertionMethod`, and refuses a timestamp older than `toleranceSeconds`
|
|
14
|
+
* (default and maximum 300: SPEC §13.3) or more than 60 seconds in the future. A signed webhook
|
|
15
|
+
* still verifies again within that window: receivers MUST de-duplicate on the event `id`
|
|
16
|
+
* (`evt_…`) for at least the window (the Hub keeps every id it took). Senders sign at every
|
|
17
|
+
* delivery attempt (a fresh `t`).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
interface SignWebhookInput {
|
|
21
|
+
/** The exact request body that will be sent. */
|
|
22
|
+
body: string;
|
|
23
|
+
signer: Signer;
|
|
24
|
+
/** ms since the epoch; default `Date.now()`. */
|
|
25
|
+
now?: number;
|
|
26
|
+
}
|
|
27
|
+
/** The `x-ixo-signature` header value for `body`. */
|
|
28
|
+
declare function signWebhook(input: SignWebhookInput): Promise<string>;
|
|
29
|
+
type WebhookFailure = 'malformed' | 'stale' | 'wrong_did' | 'unknown_key' | 'bad_signature';
|
|
30
|
+
type WebhookVerification = {
|
|
31
|
+
ok: true;
|
|
32
|
+
kid: string;
|
|
33
|
+
timestamp: number;
|
|
34
|
+
} | {
|
|
35
|
+
ok: false;
|
|
36
|
+
code: WebhookFailure;
|
|
37
|
+
};
|
|
38
|
+
interface VerifyWebhookInput {
|
|
39
|
+
body: string;
|
|
40
|
+
/** The `x-ixo-signature` header value (null/undefined when absent). */
|
|
41
|
+
header: string | null | undefined;
|
|
42
|
+
resolver: DidResolver;
|
|
43
|
+
/** The only DID allowed to sign (e.g. the Credentials worker's did:web). */
|
|
44
|
+
expectedDid: string;
|
|
45
|
+
/** How old a signature may be, in seconds: default and maximum 300 (RangeError above). */
|
|
46
|
+
toleranceSeconds?: number;
|
|
47
|
+
/** ms since the epoch; default `Date.now()`. */
|
|
48
|
+
now?: number;
|
|
49
|
+
}
|
|
50
|
+
/** Most the signed timestamp may be ahead of the receiver's clock. */
|
|
51
|
+
declare const WEBHOOK_MAX_FUTURE_SECONDS = 60;
|
|
52
|
+
/** Largest (and default) `toleranceSeconds`. */
|
|
53
|
+
declare const WEBHOOK_MAX_AGE_SECONDS = 300;
|
|
54
|
+
/**
|
|
55
|
+
* Verify a webhook. Returns `{ ok: false, code }` for every refusal; throws only
|
|
56
|
+
* `ResolverUnavailable` (the signer's DID document could not be read — answer 503, the
|
|
57
|
+
* sender retries).
|
|
58
|
+
*/
|
|
59
|
+
declare function verifyWebhook(input: VerifyWebhookInput): Promise<WebhookVerification>;
|
|
60
|
+
|
|
61
|
+
export { WEBHOOK_MAX_AGE_SECONDS, WEBHOOK_MAX_FUTURE_SECONDS, signWebhook, verifyWebhook };
|
|
62
|
+
export type { SignWebhookInput, VerifyWebhookInput, WebhookFailure, WebhookVerification };
|
package/dist/webhook.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { WEBHOOK_MAX_AGE_SECONDS, WEBHOOK_MAX_FUTURE_SECONDS, signWebhook, verifyWebhook } from './chunk-WND2LZ4O.js';
|
|
2
|
+
import './chunk-WIEGZFUO.js';
|
|
3
|
+
import './chunk-Q3MZNMCK.js';
|
|
4
|
+
import './chunk-VXKONZK4.js';
|
|
5
|
+
import './chunk-AQ3IINHC.js';
|
|
6
|
+
//# sourceMappingURL=webhook.js.map
|
|
7
|
+
//# sourceMappingURL=webhook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"webhook.js"}
|