@dwk/vc 0.1.0-beta.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/LICENSE +15 -0
- package/README.md +143 -0
- package/dist/config.d.ts +97 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +62 -0
- package/dist/config.js.map +1 -0
- package/dist/credential.d.ts +70 -0
- package/dist/credential.d.ts.map +1 -0
- package/dist/credential.js +139 -0
- package/dist/credential.js.map +1 -0
- package/dist/data-integrity.d.ts +102 -0
- package/dist/data-integrity.d.ts.map +1 -0
- package/dist/data-integrity.js +253 -0
- package/dist/data-integrity.js.map +1 -0
- package/dist/datetime.d.ts +26 -0
- package/dist/datetime.d.ts.map +1 -0
- package/dist/datetime.js +54 -0
- package/dist/datetime.js.map +1 -0
- package/dist/did-web.d.ts +93 -0
- package/dist/did-web.d.ts.map +1 -0
- package/dist/did-web.js +206 -0
- package/dist/did-web.js.map +1 -0
- package/dist/handler.d.ts +37 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +362 -0
- package/dist/handler.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/jcs.d.ts +31 -0
- package/dist/jcs.d.ts.map +1 -0
- package/dist/jcs.js +67 -0
- package/dist/jcs.js.map +1 -0
- package/dist/log.d.ts +34 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +32 -0
- package/dist/log.js.map +1 -0
- package/dist/multibase.d.ts +57 -0
- package/dist/multibase.d.ts.map +1 -0
- package/dist/multibase.js +165 -0
- package/dist/multibase.js.map +1 -0
- package/dist/status-list.d.ts +116 -0
- package/dist/status-list.d.ts.map +1 -0
- package/dist/status-list.js +241 -0
- package/dist/status-list.js.map +1 -0
- package/package.json +48 -0
- package/src/config.ts +158 -0
- package/src/credential.ts +188 -0
- package/src/data-integrity.ts +425 -0
- package/src/datetime.ts +57 -0
- package/src/did-web.ts +273 -0
- package/src/handler.ts +477 -0
- package/src/index.ts +133 -0
- package/src/jcs.ts +83 -0
- package/src/log.ts +35 -0
- package/src/multibase.ts +189 -0
- package/src/status-list.ts +356 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@dwk/vc` — `did:web` identity plus Verifiable Credential issuance and
|
|
3
|
+
* verification.
|
|
4
|
+
*
|
|
5
|
+
* Endpoint package (+ lib): exports a factory returning a `fetch`-compatible
|
|
6
|
+
* handler, mountable under any prefix so it composes with the other `@dwk`
|
|
7
|
+
* packages in one Worker. Decentralized identity is rooted at the user's own
|
|
8
|
+
* domain — the same WebID / IndieAuth identity root, expressed as a DID — and
|
|
9
|
+
* credential proofs reuse the project's asymmetric, alg-allow-listed crypto
|
|
10
|
+
* posture (`@dwk/dpop`, `@dwk/http-signatures`).
|
|
11
|
+
*
|
|
12
|
+
* The split: the **`did:web` DID document is a static file** ({@link buildDidDocument}
|
|
13
|
+
* produces `/.well-known/did.json` — no Worker needed to resolve it), while the
|
|
14
|
+
* Worker covers the dynamic parts — VC **issuance** (signing with the domain's
|
|
15
|
+
* key), VC **verification**, and **status / revocation** whose bit-flips need a
|
|
16
|
+
* strongly-consistent store. Proofs use the **JCS** Data Integrity cryptosuites
|
|
17
|
+
* (`eddsa-jcs-2022`, `ecdsa-jcs-2019`), so the package canonicalizes with
|
|
18
|
+
* {@link canonicalize} (RFC 8785) rather than shipping a JSON-LD/RDF
|
|
19
|
+
* canonicalizer — staying within the Worker script-size budget.
|
|
20
|
+
*
|
|
21
|
+
* Credential construction and proof logic take **plain-data inputs** and
|
|
22
|
+
* unit-test without a Workers runtime; only the issuance/status endpoints and
|
|
23
|
+
* their D1-backed status store touch the runtime. Signing keys and issuer
|
|
24
|
+
* identity arrive via config and a secret binding — never read from the global
|
|
25
|
+
* environment (composition contract).
|
|
26
|
+
*
|
|
27
|
+
* @see spec/packages/vc.md
|
|
28
|
+
* @packageDocumentation
|
|
29
|
+
*/
|
|
30
|
+
export { createVc } from "./handler";
|
|
31
|
+
export type { VcEnv, VcHandler } from "./handler";
|
|
32
|
+
export { resolveConfig, type VcConfig, type ResolvedVcConfig, type StatusConfig, type VcOperation, type AuthorizeOperation, type DidResolver, } from "./config";
|
|
33
|
+
export { importSigner, addProof, verifyProof, isSupportedCryptosuite, type Cryptosuite, type Signer, type JsonObject, type SecuredDocument, type AddProofOptions, type VerifyProofOptions, type VerifyProofResult, type VerificationMethod, type VerificationMethodResolver, } from "./data-integrity";
|
|
34
|
+
export { buildCredential, validateCredential, checkValidityPeriod, issuerId, VC_CONTEXT_V2, VERIFIABLE_CREDENTIAL_TYPE, type UnsignedCredential, type BuildCredentialOptions, type Issuer, } from "./credential";
|
|
35
|
+
export { didWebToUrl, urlToDidWeb, buildDidDocument, findVerificationMethod, createDidWebResolver, DID_CONTEXT_V1, MULTIKEY_CONTEXT_V1, JWK_CONTEXT_V1, type BuildDidDocumentOptions, type VerificationMethodInput, type VerificationRelationships, type DidWebResolverOptions, type FetchLike, } from "./did-web";
|
|
36
|
+
export { buildStatusListCredential, buildStatusEntry, buildEncodedList, encodeBitstring, decodeBitstring, getBit, setBit, findStatusEntry, statusEntryIndex, createVcStatusStore, DEFAULT_STATUS_LIST_LENGTH, BITSTRING_STATUS_LIST_CREDENTIAL_TYPE, BITSTRING_STATUS_LIST_ENTRY_TYPE, BITSTRING_STATUS_LIST_SUBJECT_TYPE, type StatusPurpose, type StatusPurposeValue, type StatusListCredentialOptions, type VcStatusStore, type VcStatusStoreEnv, } from "./status-list";
|
|
37
|
+
export { isValidXsdDateTimeStamp, toXsdDateTime } from "./datetime";
|
|
38
|
+
export { canonicalize, canonicalizeToBytes, type JcsValue } from "./jcs";
|
|
39
|
+
export { base58btcEncode, base58btcDecode, base64urlEncode, base64urlDecode, encodeMultibaseBase58btc, encodeMultibaseBase64url, decodeMultibase, encodeEd25519Multikey, decodeMultikey, type DecodedMultikey, } from "./multibase";
|
|
40
|
+
export { VcLogEvent } from "./log";
|
|
41
|
+
export type { Logger, Metrics } from "@dwk/log";
|
|
42
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAElD,OAAO,EACL,aAAa,EACb,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,WAAW,GACjB,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,sBAAsB,EACtB,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,0BAA0B,GAChC,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,QAAQ,EACR,aAAa,EACb,0BAA0B,EAC1B,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,MAAM,GACZ,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAC1B,KAAK,SAAS,GACf,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,yBAAyB,EACzB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,MAAM,EACN,MAAM,EACN,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,0BAA0B,EAC1B,qCAAqC,EACrC,gCAAgC,EAChC,kCAAkC,EAClC,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,2BAA2B,EAChC,KAAK,aAAa,EAClB,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGpE,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,KAAK,QAAQ,EAAE,MAAM,OAAO,CAAC;AACzE,OAAO,EACL,eAAe,EACf,eAAe,EACf,eAAe,EACf,eAAe,EACf,wBAAwB,EACxB,wBAAwB,EACxB,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@dwk/vc` — `did:web` identity plus Verifiable Credential issuance and
|
|
3
|
+
* verification.
|
|
4
|
+
*
|
|
5
|
+
* Endpoint package (+ lib): exports a factory returning a `fetch`-compatible
|
|
6
|
+
* handler, mountable under any prefix so it composes with the other `@dwk`
|
|
7
|
+
* packages in one Worker. Decentralized identity is rooted at the user's own
|
|
8
|
+
* domain — the same WebID / IndieAuth identity root, expressed as a DID — and
|
|
9
|
+
* credential proofs reuse the project's asymmetric, alg-allow-listed crypto
|
|
10
|
+
* posture (`@dwk/dpop`, `@dwk/http-signatures`).
|
|
11
|
+
*
|
|
12
|
+
* The split: the **`did:web` DID document is a static file** ({@link buildDidDocument}
|
|
13
|
+
* produces `/.well-known/did.json` — no Worker needed to resolve it), while the
|
|
14
|
+
* Worker covers the dynamic parts — VC **issuance** (signing with the domain's
|
|
15
|
+
* key), VC **verification**, and **status / revocation** whose bit-flips need a
|
|
16
|
+
* strongly-consistent store. Proofs use the **JCS** Data Integrity cryptosuites
|
|
17
|
+
* (`eddsa-jcs-2022`, `ecdsa-jcs-2019`), so the package canonicalizes with
|
|
18
|
+
* {@link canonicalize} (RFC 8785) rather than shipping a JSON-LD/RDF
|
|
19
|
+
* canonicalizer — staying within the Worker script-size budget.
|
|
20
|
+
*
|
|
21
|
+
* Credential construction and proof logic take **plain-data inputs** and
|
|
22
|
+
* unit-test without a Workers runtime; only the issuance/status endpoints and
|
|
23
|
+
* their D1-backed status store touch the runtime. Signing keys and issuer
|
|
24
|
+
* identity arrive via config and a secret binding — never read from the global
|
|
25
|
+
* environment (composition contract).
|
|
26
|
+
*
|
|
27
|
+
* @see spec/packages/vc.md
|
|
28
|
+
* @packageDocumentation
|
|
29
|
+
*/
|
|
30
|
+
export { createVc } from "./handler";
|
|
31
|
+
export { resolveConfig, } from "./config";
|
|
32
|
+
// Data Integrity (cryptosuites + proof pipeline)
|
|
33
|
+
export { importSigner, addProof, verifyProof, isSupportedCryptosuite, } from "./data-integrity";
|
|
34
|
+
// Credential data model (VCDM 2.0)
|
|
35
|
+
export { buildCredential, validateCredential, checkValidityPeriod, issuerId, VC_CONTEXT_V2, VERIFIABLE_CREDENTIAL_TYPE, } from "./credential";
|
|
36
|
+
// did:web
|
|
37
|
+
export { didWebToUrl, urlToDidWeb, buildDidDocument, findVerificationMethod, createDidWebResolver, DID_CONTEXT_V1, MULTIKEY_CONTEXT_V1, JWK_CONTEXT_V1, } from "./did-web";
|
|
38
|
+
// Bitstring Status List
|
|
39
|
+
export { buildStatusListCredential, buildStatusEntry, buildEncodedList, encodeBitstring, decodeBitstring, getBit, setBit, findStatusEntry, statusEntryIndex, createVcStatusStore, DEFAULT_STATUS_LIST_LENGTH, BITSTRING_STATUS_LIST_CREDENTIAL_TYPE, BITSTRING_STATUS_LIST_ENTRY_TYPE, BITSTRING_STATUS_LIST_SUBJECT_TYPE, } from "./status-list";
|
|
40
|
+
// XSD dateTimeStamp validation for VC temporal fields
|
|
41
|
+
export { isValidXsdDateTimeStamp, toXsdDateTime } from "./datetime";
|
|
42
|
+
// JCS canonicalization and multibase/Multikey codecs
|
|
43
|
+
export { canonicalize, canonicalizeToBytes } from "./jcs";
|
|
44
|
+
export { base58btcEncode, base58btcDecode, base64urlEncode, base64urlDecode, encodeMultibaseBase58btc, encodeMultibaseBase64url, decodeMultibase, encodeEd25519Multikey, decodeMultikey, } from "./multibase";
|
|
45
|
+
export { VcLogEvent } from "./log";
|
|
46
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAGrC,OAAO,EACL,aAAa,GAOd,MAAM,UAAU,CAAC;AAElB,iDAAiD;AACjD,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,sBAAsB,GAUvB,MAAM,kBAAkB,CAAC;AAE1B,mCAAmC;AACnC,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,QAAQ,EACR,aAAa,EACb,0BAA0B,GAI3B,MAAM,cAAc,CAAC;AAEtB,UAAU;AACV,OAAO,EACL,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,mBAAmB,EACnB,cAAc,GAMf,MAAM,WAAW,CAAC;AAEnB,wBAAwB;AACxB,OAAO,EACL,yBAAyB,EACzB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,MAAM,EACN,MAAM,EACN,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,0BAA0B,EAC1B,qCAAqC,EACrC,gCAAgC,EAChC,kCAAkC,GAMnC,MAAM,eAAe,CAAC;AAEvB,sDAAsD;AACtD,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEpE,qDAAqD;AACrD,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAiB,MAAM,OAAO,CAAC;AACzE,OAAO,EACL,eAAe,EACf,eAAe,EACf,eAAe,EACf,eAAe,EACf,wBAAwB,EACxB,wBAAwB,EACxB,eAAe,EACf,qBAAqB,EACrB,cAAc,GAEf,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC"}
|
package/dist/jcs.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Canonicalization Scheme (RFC 8785).
|
|
3
|
+
*
|
|
4
|
+
* The `*-jcs-*` Data Integrity cryptosuites canonicalize a credential and its
|
|
5
|
+
* proof configuration with JCS — not RDF Dataset Canonicalization — so the proof
|
|
6
|
+
* pipeline needs no JSON-LD expansion or URDNA2015 implementation. That keeps
|
|
7
|
+
* the package within the Worker script-size budget (no `jsonld.js`/Comunica) and
|
|
8
|
+
* makes proof construction a pure, plain-data transform that unit-tests without a
|
|
9
|
+
* Workers runtime.
|
|
10
|
+
*
|
|
11
|
+
* JCS ordering and escaping line up with the ECMAScript primitives this uses:
|
|
12
|
+
* object members are sorted by the UTF-16 code units of their keys (the default
|
|
13
|
+
* `Array.prototype.sort` order), and string escaping matches `JSON.stringify`.
|
|
14
|
+
* Number serialization uses the ECMAScript `Number`-to-string algorithm
|
|
15
|
+
* (`String(n)`); credentials carry small integers and simple decimals, well
|
|
16
|
+
* within where that agrees with RFC 8785.
|
|
17
|
+
*
|
|
18
|
+
* @see https://www.rfc-editor.org/rfc/rfc8785
|
|
19
|
+
*/
|
|
20
|
+
/** A JSON value accepted by {@link canonicalize}. */
|
|
21
|
+
export type JcsValue = null | boolean | number | string | JcsValue[] | {
|
|
22
|
+
[key: string]: JcsValue | undefined;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Canonicalize a JSON value to its RFC 8785 string form. Throws on values JSON
|
|
26
|
+
* cannot represent (non-finite numbers, `undefined` at the top level, functions).
|
|
27
|
+
*/
|
|
28
|
+
export declare function canonicalize(value: JcsValue): string;
|
|
29
|
+
/** Canonicalize a value and return its UTF-8 bytes (the hash input). */
|
|
30
|
+
export declare function canonicalizeToBytes(value: JcsValue): Uint8Array;
|
|
31
|
+
//# sourceMappingURL=jcs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jcs.d.ts","sourceRoot":"","sources":["../src/jcs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,qDAAqD;AACrD,MAAM,MAAM,QAAQ,GAChB,IAAI,GACJ,OAAO,GACP,MAAM,GACN,MAAM,GACN,QAAQ,EAAE,GACV;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;CAAE,CAAC;AAyC5C;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAKpD;AAED,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,QAAQ,GAAG,UAAU,CAE/D"}
|
package/dist/jcs.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Canonicalization Scheme (RFC 8785).
|
|
3
|
+
*
|
|
4
|
+
* The `*-jcs-*` Data Integrity cryptosuites canonicalize a credential and its
|
|
5
|
+
* proof configuration with JCS — not RDF Dataset Canonicalization — so the proof
|
|
6
|
+
* pipeline needs no JSON-LD expansion or URDNA2015 implementation. That keeps
|
|
7
|
+
* the package within the Worker script-size budget (no `jsonld.js`/Comunica) and
|
|
8
|
+
* makes proof construction a pure, plain-data transform that unit-tests without a
|
|
9
|
+
* Workers runtime.
|
|
10
|
+
*
|
|
11
|
+
* JCS ordering and escaping line up with the ECMAScript primitives this uses:
|
|
12
|
+
* object members are sorted by the UTF-16 code units of their keys (the default
|
|
13
|
+
* `Array.prototype.sort` order), and string escaping matches `JSON.stringify`.
|
|
14
|
+
* Number serialization uses the ECMAScript `Number`-to-string algorithm
|
|
15
|
+
* (`String(n)`); credentials carry small integers and simple decimals, well
|
|
16
|
+
* within where that agrees with RFC 8785.
|
|
17
|
+
*
|
|
18
|
+
* @see https://www.rfc-editor.org/rfc/rfc8785
|
|
19
|
+
*/
|
|
20
|
+
function serializeNumber(value) {
|
|
21
|
+
if (!Number.isFinite(value)) {
|
|
22
|
+
throw new Error("@dwk/vc: cannot canonicalize a non-finite number");
|
|
23
|
+
}
|
|
24
|
+
// String(-0) === "0", matching JCS's requirement that -0 serialize as "0".
|
|
25
|
+
return String(value);
|
|
26
|
+
}
|
|
27
|
+
function serialize(value) {
|
|
28
|
+
if (value === null)
|
|
29
|
+
return "null";
|
|
30
|
+
const type = typeof value;
|
|
31
|
+
if (type === "boolean")
|
|
32
|
+
return value ? "true" : "false";
|
|
33
|
+
if (type === "number")
|
|
34
|
+
return serializeNumber(value);
|
|
35
|
+
if (type === "string")
|
|
36
|
+
return JSON.stringify(value);
|
|
37
|
+
if (Array.isArray(value)) {
|
|
38
|
+
// Mirror JSON.stringify: an `undefined` element serializes as `null`.
|
|
39
|
+
const items = value.map((item) => item === undefined ? "null" : serialize(item));
|
|
40
|
+
return `[${items.join(",")}]`;
|
|
41
|
+
}
|
|
42
|
+
if (type === "object") {
|
|
43
|
+
const obj = value;
|
|
44
|
+
// Drop members whose value is `undefined`, then sort by UTF-16 code unit.
|
|
45
|
+
const keys = Object.keys(obj)
|
|
46
|
+
.filter((key) => obj[key] !== undefined)
|
|
47
|
+
.sort();
|
|
48
|
+
const members = keys.map((key) => `${JSON.stringify(key)}:${serialize(obj[key])}`);
|
|
49
|
+
return `{${members.join(",")}}`;
|
|
50
|
+
}
|
|
51
|
+
throw new Error(`@dwk/vc: cannot canonicalize value of type ${type}`);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Canonicalize a JSON value to its RFC 8785 string form. Throws on values JSON
|
|
55
|
+
* cannot represent (non-finite numbers, `undefined` at the top level, functions).
|
|
56
|
+
*/
|
|
57
|
+
export function canonicalize(value) {
|
|
58
|
+
if (value === undefined) {
|
|
59
|
+
throw new Error("@dwk/vc: cannot canonicalize `undefined`");
|
|
60
|
+
}
|
|
61
|
+
return serialize(value);
|
|
62
|
+
}
|
|
63
|
+
/** Canonicalize a value and return its UTF-8 bytes (the hash input). */
|
|
64
|
+
export function canonicalizeToBytes(value) {
|
|
65
|
+
return new TextEncoder().encode(canonicalize(value));
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=jcs.js.map
|
package/dist/jcs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jcs.js","sourceRoot":"","sources":["../src/jcs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAWH,SAAS,eAAe,CAAC,KAAa;IACpC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IACtE,CAAC;IACD,2EAA2E;IAC3E,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,SAAS,CAAC,KAAe;IAChC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAElC,MAAM,IAAI,GAAG,OAAO,KAAK,CAAC;IAC1B,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IACxD,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,eAAe,CAAC,KAAe,CAAC,CAAC;IAC/D,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAEpD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,sEAAsE;QACtE,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAC/B,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAC9C,CAAC;QACF,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAChC,CAAC;IAED,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,KAAgD,CAAC;QAC7D,0EAA0E;QAC1E,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;aAC1B,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC;aACvC,IAAI,EAAE,CAAC;QACV,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CACtB,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,EAAE,CAC1D,CAAC;QACF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAClC,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,8CAA8C,IAAI,EAAE,CAAC,CAAC;AACxE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,KAAe;IAC1C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,mBAAmB,CAAC,KAAe;IACjD,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;AACvD,CAAC"}
|
package/dist/log.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@dwk/vc` — structured observability event taxonomy.
|
|
3
|
+
*
|
|
4
|
+
* Credential issuance and status changes are the security-relevant moments here:
|
|
5
|
+
* an unauthorized issue attempt, a verification that fails its proof, or a
|
|
6
|
+
* revocation are exactly what an operator wants a signal for. Logging and metrics
|
|
7
|
+
* are opt-in via an injected {@link Logger} and {@link Metrics} (see `@dwk/log`)
|
|
8
|
+
* and **share this one vocabulary**: the same dotted event name is passed to the
|
|
9
|
+
* logger and the metrics sink so a log line and its counter line up.
|
|
10
|
+
*
|
|
11
|
+
* Fields follow the redaction policy: never the credential subject, claims, the
|
|
12
|
+
* signing key, or a `proofValue` — only stable reason codes, a credential `type`
|
|
13
|
+
* count or list, the issuer host, and the status purpose/outcome.
|
|
14
|
+
*
|
|
15
|
+
* @packageDocumentation
|
|
16
|
+
*/
|
|
17
|
+
/** Stable event names emitted by `@dwk/vc`. */
|
|
18
|
+
export declare const VcLogEvent: {
|
|
19
|
+
/** A credential was issued (signed). Fields: `cryptosuite`. */
|
|
20
|
+
readonly Issued: "vc.issued";
|
|
21
|
+
/** A credential was verified. Fields: `verified` (boolean). */
|
|
22
|
+
readonly Verified: "vc.verified";
|
|
23
|
+
/** A credential's status was changed. Fields: `statusPurpose`, `value`. */
|
|
24
|
+
readonly StatusChanged: "vc.status.changed";
|
|
25
|
+
/**
|
|
26
|
+
* A request was rejected before completing. Field: `reason`
|
|
27
|
+
* (`method_not_allowed`, `unauthorized`, `malformed_request`,
|
|
28
|
+
* `unsupported`, `status_disabled`, `not_found`).
|
|
29
|
+
*/
|
|
30
|
+
readonly Rejected: "vc.rejected";
|
|
31
|
+
};
|
|
32
|
+
/** Union of the event-name string literals in {@link VcLogEvent}. */
|
|
33
|
+
export type VcLogEvent = (typeof VcLogEvent)[keyof typeof VcLogEvent];
|
|
34
|
+
//# sourceMappingURL=log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,+CAA+C;AAC/C,eAAO,MAAM,UAAU;IACrB,+DAA+D;;IAE/D,+DAA+D;;IAE/D,2EAA2E;;IAE3E;;;;OAIG;;CAEK,CAAC;AAEX,qEAAqE;AACrE,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,OAAO,UAAU,CAAC,CAAC"}
|
package/dist/log.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@dwk/vc` — structured observability event taxonomy.
|
|
3
|
+
*
|
|
4
|
+
* Credential issuance and status changes are the security-relevant moments here:
|
|
5
|
+
* an unauthorized issue attempt, a verification that fails its proof, or a
|
|
6
|
+
* revocation are exactly what an operator wants a signal for. Logging and metrics
|
|
7
|
+
* are opt-in via an injected {@link Logger} and {@link Metrics} (see `@dwk/log`)
|
|
8
|
+
* and **share this one vocabulary**: the same dotted event name is passed to the
|
|
9
|
+
* logger and the metrics sink so a log line and its counter line up.
|
|
10
|
+
*
|
|
11
|
+
* Fields follow the redaction policy: never the credential subject, claims, the
|
|
12
|
+
* signing key, or a `proofValue` — only stable reason codes, a credential `type`
|
|
13
|
+
* count or list, the issuer host, and the status purpose/outcome.
|
|
14
|
+
*
|
|
15
|
+
* @packageDocumentation
|
|
16
|
+
*/
|
|
17
|
+
/** Stable event names emitted by `@dwk/vc`. */
|
|
18
|
+
export const VcLogEvent = {
|
|
19
|
+
/** A credential was issued (signed). Fields: `cryptosuite`. */
|
|
20
|
+
Issued: "vc.issued",
|
|
21
|
+
/** A credential was verified. Fields: `verified` (boolean). */
|
|
22
|
+
Verified: "vc.verified",
|
|
23
|
+
/** A credential's status was changed. Fields: `statusPurpose`, `value`. */
|
|
24
|
+
StatusChanged: "vc.status.changed",
|
|
25
|
+
/**
|
|
26
|
+
* A request was rejected before completing. Field: `reason`
|
|
27
|
+
* (`method_not_allowed`, `unauthorized`, `malformed_request`,
|
|
28
|
+
* `unsupported`, `status_disabled`, `not_found`).
|
|
29
|
+
*/
|
|
30
|
+
Rejected: "vc.rejected",
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=log.js.map
|
package/dist/log.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,+CAA+C;AAC/C,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,+DAA+D;IAC/D,MAAM,EAAE,WAAW;IACnB,+DAA+D;IAC/D,QAAQ,EAAE,aAAa;IACvB,2EAA2E;IAC3E,aAAa,EAAE,mBAAmB;IAClC;;;;OAIG;IACH,QAAQ,EAAE,aAAa;CACf,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multibase / Multikey encoding primitives used by Verifiable Credential proofs
|
|
3
|
+
* and DID documents.
|
|
4
|
+
*
|
|
5
|
+
* Data Integrity proof values are **multibase base58-btc** strings (a leading
|
|
6
|
+
* `z`), and a `Multikey` verification method publishes its public key as a
|
|
7
|
+
* multibase base58-btc string over a **multicodec**-prefixed raw key. Status
|
|
8
|
+
* lists are **multibase base64url** (a leading `u`). These helpers are pure
|
|
9
|
+
* byte/string transforms with no Web Crypto or runtime dependency, so they
|
|
10
|
+
* unit-test in isolation.
|
|
11
|
+
*
|
|
12
|
+
* @see https://www.w3.org/TR/controller-document/#multikey
|
|
13
|
+
* @see https://github.com/multiformats/multibase
|
|
14
|
+
*/
|
|
15
|
+
/** Multibase prefix characters this module understands. */
|
|
16
|
+
export declare const MULTIBASE_BASE58BTC = "z";
|
|
17
|
+
export declare const MULTIBASE_BASE64URL = "u";
|
|
18
|
+
/**
|
|
19
|
+
* Multicodec varint prefixes for the key types published as a `Multikey`.
|
|
20
|
+
* Encoded as unsigned LEB128 varints (the on-disk multicodec form).
|
|
21
|
+
*/
|
|
22
|
+
export declare const MULTICODEC_ED25519_PUB: Uint8Array<ArrayBuffer>;
|
|
23
|
+
/** Encode raw bytes as base58-btc (no multibase prefix). */
|
|
24
|
+
export declare function base58btcEncode(bytes: Uint8Array): string;
|
|
25
|
+
/** Decode a base58-btc string (no multibase prefix) to raw bytes. */
|
|
26
|
+
export declare function base58btcDecode(input: string): Uint8Array;
|
|
27
|
+
/** Base64url-encode bytes without padding. */
|
|
28
|
+
export declare function base64urlEncode(bytes: Uint8Array): string;
|
|
29
|
+
/** Decode a base64url string (padded or not) to bytes. */
|
|
30
|
+
export declare function base64urlDecode(input: string): Uint8Array;
|
|
31
|
+
/** Encode bytes as a multibase base58-btc string (`z…`). */
|
|
32
|
+
export declare function encodeMultibaseBase58btc(bytes: Uint8Array): string;
|
|
33
|
+
/** Encode bytes as a multibase base64url string (`u…`). */
|
|
34
|
+
export declare function encodeMultibaseBase64url(bytes: Uint8Array): string;
|
|
35
|
+
/**
|
|
36
|
+
* Decode a multibase string, dispatching on its prefix. Supports base58-btc
|
|
37
|
+
* (`z`) and base64url (`u`) — the two encodings the VC suite emits.
|
|
38
|
+
*/
|
|
39
|
+
export declare function decodeMultibase(input: string): Uint8Array;
|
|
40
|
+
/**
|
|
41
|
+
* Encode a raw Ed25519 public key (32 bytes) as a `Multikey`
|
|
42
|
+
* `publicKeyMultibase` value: multibase base58-btc over the
|
|
43
|
+
* multicodec-prefixed key.
|
|
44
|
+
*/
|
|
45
|
+
export declare function encodeEd25519Multikey(rawPublicKey: Uint8Array): string;
|
|
46
|
+
/** The raw key bytes and key type recovered from a `publicKeyMultibase`. */
|
|
47
|
+
export interface DecodedMultikey {
|
|
48
|
+
readonly keyType: "Ed25519";
|
|
49
|
+
readonly rawPublicKey: Uint8Array;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Decode a `Multikey` `publicKeyMultibase` value into its raw key bytes,
|
|
53
|
+
* validating the multicodec prefix. Only Ed25519 is supported as a Multikey;
|
|
54
|
+
* other key types are published as `publicKeyJwk` instead.
|
|
55
|
+
*/
|
|
56
|
+
export declare function decodeMultikey(publicKeyMultibase: string): DecodedMultikey;
|
|
57
|
+
//# sourceMappingURL=multibase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multibase.d.ts","sourceRoot":"","sources":["../src/multibase.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAcH,2DAA2D;AAC3D,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC;;;GAGG;AACH,eAAO,MAAM,sBAAsB,yBAA4B,CAAC;AAEhE,4DAA4D;AAC5D,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CA2BzD;AAED,qEAAqE;AACrE,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CA6BzD;AAED,8CAA8C;AAC9C,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAOzD;AAED,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAQzD;AAED,4DAA4D;AAC5D,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAElE;AAED,2DAA2D;AAC3D,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAElE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CASzD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,UAAU,GAAG,MAAM,CAYtE;AAED,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAC;CACnC;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,kBAAkB,EAAE,MAAM,GAAG,eAAe,CAa1E"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multibase / Multikey encoding primitives used by Verifiable Credential proofs
|
|
3
|
+
* and DID documents.
|
|
4
|
+
*
|
|
5
|
+
* Data Integrity proof values are **multibase base58-btc** strings (a leading
|
|
6
|
+
* `z`), and a `Multikey` verification method publishes its public key as a
|
|
7
|
+
* multibase base58-btc string over a **multicodec**-prefixed raw key. Status
|
|
8
|
+
* lists are **multibase base64url** (a leading `u`). These helpers are pure
|
|
9
|
+
* byte/string transforms with no Web Crypto or runtime dependency, so they
|
|
10
|
+
* unit-test in isolation.
|
|
11
|
+
*
|
|
12
|
+
* @see https://www.w3.org/TR/controller-document/#multikey
|
|
13
|
+
* @see https://github.com/multiformats/multibase
|
|
14
|
+
*/
|
|
15
|
+
const BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
16
|
+
// Reverse lookup: code point → value, built once.
|
|
17
|
+
const BASE58_LOOKUP = (() => {
|
|
18
|
+
const map = {};
|
|
19
|
+
for (let i = 0; i < BASE58_ALPHABET.length; i++) {
|
|
20
|
+
map[BASE58_ALPHABET[i]] = i;
|
|
21
|
+
}
|
|
22
|
+
return map;
|
|
23
|
+
})();
|
|
24
|
+
/** Multibase prefix characters this module understands. */
|
|
25
|
+
export const MULTIBASE_BASE58BTC = "z";
|
|
26
|
+
export const MULTIBASE_BASE64URL = "u";
|
|
27
|
+
/**
|
|
28
|
+
* Multicodec varint prefixes for the key types published as a `Multikey`.
|
|
29
|
+
* Encoded as unsigned LEB128 varints (the on-disk multicodec form).
|
|
30
|
+
*/
|
|
31
|
+
export const MULTICODEC_ED25519_PUB = Uint8Array.of(0xed, 0x01);
|
|
32
|
+
/** Encode raw bytes as base58-btc (no multibase prefix). */
|
|
33
|
+
export function base58btcEncode(bytes) {
|
|
34
|
+
if (bytes.length === 0)
|
|
35
|
+
return "";
|
|
36
|
+
// Count and preserve leading zero bytes as leading '1's.
|
|
37
|
+
let zeros = 0;
|
|
38
|
+
while (zeros < bytes.length && bytes[zeros] === 0)
|
|
39
|
+
zeros++;
|
|
40
|
+
// Convert the base-256 big-endian integer to base-58 via repeated division.
|
|
41
|
+
const digits = [];
|
|
42
|
+
for (let i = zeros; i < bytes.length; i++) {
|
|
43
|
+
let carry = bytes[i];
|
|
44
|
+
for (let j = 0; j < digits.length; j++) {
|
|
45
|
+
carry += digits[j] << 8;
|
|
46
|
+
digits[j] = carry % 58;
|
|
47
|
+
carry = (carry / 58) | 0;
|
|
48
|
+
}
|
|
49
|
+
while (carry > 0) {
|
|
50
|
+
digits.push(carry % 58);
|
|
51
|
+
carry = (carry / 58) | 0;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
let out = "1".repeat(zeros);
|
|
55
|
+
for (let i = digits.length - 1; i >= 0; i--) {
|
|
56
|
+
out += BASE58_ALPHABET[digits[i]];
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
/** Decode a base58-btc string (no multibase prefix) to raw bytes. */
|
|
61
|
+
export function base58btcDecode(input) {
|
|
62
|
+
if (input.length === 0)
|
|
63
|
+
return new Uint8Array(0);
|
|
64
|
+
let zeros = 0;
|
|
65
|
+
while (zeros < input.length && input[zeros] === "1")
|
|
66
|
+
zeros++;
|
|
67
|
+
const bytes = [];
|
|
68
|
+
for (let i = zeros; i < input.length; i++) {
|
|
69
|
+
const value = BASE58_LOOKUP[input[i]];
|
|
70
|
+
if (value === undefined) {
|
|
71
|
+
throw new Error(`@dwk/vc: invalid base58 character "${input[i]}"`);
|
|
72
|
+
}
|
|
73
|
+
let carry = value;
|
|
74
|
+
for (let j = 0; j < bytes.length; j++) {
|
|
75
|
+
carry += bytes[j] * 58;
|
|
76
|
+
bytes[j] = carry & 0xff;
|
|
77
|
+
carry >>= 8;
|
|
78
|
+
}
|
|
79
|
+
while (carry > 0) {
|
|
80
|
+
bytes.push(carry & 0xff);
|
|
81
|
+
carry >>= 8;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const out = new Uint8Array(zeros + bytes.length);
|
|
85
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
86
|
+
out[zeros + bytes.length - 1 - i] = bytes[i];
|
|
87
|
+
}
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
/** Base64url-encode bytes without padding. */
|
|
91
|
+
export function base64urlEncode(bytes) {
|
|
92
|
+
let binary = "";
|
|
93
|
+
for (const byte of bytes)
|
|
94
|
+
binary += String.fromCharCode(byte);
|
|
95
|
+
return btoa(binary)
|
|
96
|
+
.replace(/\+/g, "-")
|
|
97
|
+
.replace(/\//g, "_")
|
|
98
|
+
.replace(/=+$/, "");
|
|
99
|
+
}
|
|
100
|
+
/** Decode a base64url string (padded or not) to bytes. */
|
|
101
|
+
export function base64urlDecode(input) {
|
|
102
|
+
const b64 = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
103
|
+
const padded = b64.length % 4 === 0 ? b64 : b64 + "=".repeat(4 - (b64.length % 4));
|
|
104
|
+
const binary = atob(padded);
|
|
105
|
+
const bytes = new Uint8Array(binary.length);
|
|
106
|
+
for (let i = 0; i < binary.length; i++)
|
|
107
|
+
bytes[i] = binary.charCodeAt(i);
|
|
108
|
+
return bytes;
|
|
109
|
+
}
|
|
110
|
+
/** Encode bytes as a multibase base58-btc string (`z…`). */
|
|
111
|
+
export function encodeMultibaseBase58btc(bytes) {
|
|
112
|
+
return MULTIBASE_BASE58BTC + base58btcEncode(bytes);
|
|
113
|
+
}
|
|
114
|
+
/** Encode bytes as a multibase base64url string (`u…`). */
|
|
115
|
+
export function encodeMultibaseBase64url(bytes) {
|
|
116
|
+
return MULTIBASE_BASE64URL + base64urlEncode(bytes);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Decode a multibase string, dispatching on its prefix. Supports base58-btc
|
|
120
|
+
* (`z`) and base64url (`u`) — the two encodings the VC suite emits.
|
|
121
|
+
*/
|
|
122
|
+
export function decodeMultibase(input) {
|
|
123
|
+
if (input.length === 0) {
|
|
124
|
+
throw new Error("@dwk/vc: empty multibase value");
|
|
125
|
+
}
|
|
126
|
+
const prefix = input[0];
|
|
127
|
+
const rest = input.slice(1);
|
|
128
|
+
if (prefix === MULTIBASE_BASE58BTC)
|
|
129
|
+
return base58btcDecode(rest);
|
|
130
|
+
if (prefix === MULTIBASE_BASE64URL)
|
|
131
|
+
return base64urlDecode(rest);
|
|
132
|
+
throw new Error(`@dwk/vc: unsupported multibase prefix "${prefix ?? ""}"`);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Encode a raw Ed25519 public key (32 bytes) as a `Multikey`
|
|
136
|
+
* `publicKeyMultibase` value: multibase base58-btc over the
|
|
137
|
+
* multicodec-prefixed key.
|
|
138
|
+
*/
|
|
139
|
+
export function encodeEd25519Multikey(rawPublicKey) {
|
|
140
|
+
if (rawPublicKey.length !== 32) {
|
|
141
|
+
throw new Error(`@dwk/vc: Ed25519 public key must be 32 bytes, got ${rawPublicKey.length}`);
|
|
142
|
+
}
|
|
143
|
+
const prefixed = new Uint8Array(MULTICODEC_ED25519_PUB.length + rawPublicKey.length);
|
|
144
|
+
prefixed.set(MULTICODEC_ED25519_PUB, 0);
|
|
145
|
+
prefixed.set(rawPublicKey, MULTICODEC_ED25519_PUB.length);
|
|
146
|
+
return encodeMultibaseBase58btc(prefixed);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Decode a `Multikey` `publicKeyMultibase` value into its raw key bytes,
|
|
150
|
+
* validating the multicodec prefix. Only Ed25519 is supported as a Multikey;
|
|
151
|
+
* other key types are published as `publicKeyJwk` instead.
|
|
152
|
+
*/
|
|
153
|
+
export function decodeMultikey(publicKeyMultibase) {
|
|
154
|
+
const bytes = decodeMultibase(publicKeyMultibase);
|
|
155
|
+
if (bytes.length === MULTICODEC_ED25519_PUB.length + 32 &&
|
|
156
|
+
bytes[0] === MULTICODEC_ED25519_PUB[0] &&
|
|
157
|
+
bytes[1] === MULTICODEC_ED25519_PUB[1]) {
|
|
158
|
+
return {
|
|
159
|
+
keyType: "Ed25519",
|
|
160
|
+
rawPublicKey: bytes.slice(MULTICODEC_ED25519_PUB.length),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
throw new Error("@dwk/vc: unsupported or malformed Multikey prefix");
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=multibase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multibase.js","sourceRoot":"","sources":["../src/multibase.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bitstring Status List support: the encoded-list codec, credential/entry
|
|
3
|
+
* builders, and a D1-backed authority for flipping a credential's status.
|
|
4
|
+
*
|
|
5
|
+
* A status list is a GZIP-compressed bitstring, multibase base64url-encoded,
|
|
6
|
+
* published inside a `BitstringStatusListCredential`. A credential opts in by
|
|
7
|
+
* carrying a `BitstringStatusListEntry` that points at a list and an index;
|
|
8
|
+
* verifiers read the bit at that index. Flipping a bit (revoking, suspending) is
|
|
9
|
+
* stateful and security-sensitive, so the authoritative bits live in **D1** — a
|
|
10
|
+
* strongly-consistent store — never KV (see `spec/non-functional-requirements.md`).
|
|
11
|
+
*
|
|
12
|
+
* The codec and builders are pure (GZIP via `CompressionStream`, available under
|
|
13
|
+
* both Node and workerd); only {@link createVcStatusStore} touches a binding.
|
|
14
|
+
*
|
|
15
|
+
* @see https://www.w3.org/TR/vc-bitstring-status-list/
|
|
16
|
+
*/
|
|
17
|
+
import type { JsonObject } from "./data-integrity";
|
|
18
|
+
/**
|
|
19
|
+
* The minimum bitstring length the spec mandates (131,072 bits / 16 KB), chosen
|
|
20
|
+
* so an individual entry's index does not leak which credential it refers to.
|
|
21
|
+
*/
|
|
22
|
+
export declare const DEFAULT_STATUS_LIST_LENGTH = 131072;
|
|
23
|
+
export declare const BITSTRING_STATUS_LIST_CREDENTIAL_TYPE = "BitstringStatusListCredential";
|
|
24
|
+
export declare const BITSTRING_STATUS_LIST_ENTRY_TYPE = "BitstringStatusListEntry";
|
|
25
|
+
export declare const BITSTRING_STATUS_LIST_SUBJECT_TYPE = "BitstringStatusList";
|
|
26
|
+
/** A status purpose. `revocation` is permanent; `suspension` is reversible. */
|
|
27
|
+
export type StatusPurpose = "revocation" | "suspension" | (string & {});
|
|
28
|
+
/**
|
|
29
|
+
* A `statusPurpose` value as it appears on a credential or entry. The Bitstring
|
|
30
|
+
* Status List spec allows "one or more" purposes, so a single purpose or an
|
|
31
|
+
* array of them are both valid.
|
|
32
|
+
*/
|
|
33
|
+
export type StatusPurposeValue = StatusPurpose | readonly StatusPurpose[];
|
|
34
|
+
/** Get the bit at `index` in a most-significant-bit-first bitstring. */
|
|
35
|
+
export declare function getBit(bits: Uint8Array, index: number): boolean;
|
|
36
|
+
/** Set the bit at `index` in a most-significant-bit-first bitstring. */
|
|
37
|
+
export declare function setBit(bits: Uint8Array, index: number, value: boolean): void;
|
|
38
|
+
/** GZIP-compress a bitstring and multibase base64url-encode it (`encodedList`). */
|
|
39
|
+
export declare function encodeBitstring(bits: Uint8Array): Promise<string>;
|
|
40
|
+
/** Decode an `encodedList` (multibase base64url + GZIP) back to its bitstring. */
|
|
41
|
+
export declare function decodeBitstring(encodedList: string): Promise<Uint8Array>;
|
|
42
|
+
/**
|
|
43
|
+
* Build an `encodedList` of `length` bits with the given indices set to 1.
|
|
44
|
+
* `length` is the bit count (rounded up to whole bytes).
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildEncodedList(setIndices: Iterable<number>, length?: number): Promise<string>;
|
|
47
|
+
/** Options for {@link buildStatusListCredential}. */
|
|
48
|
+
export interface StatusListCredentialOptions {
|
|
49
|
+
/** The status list credential's id (a URL). */
|
|
50
|
+
readonly id: string;
|
|
51
|
+
/** The status purpose(s) this list tracks (one or more). */
|
|
52
|
+
readonly statusPurpose: StatusPurposeValue;
|
|
53
|
+
/** The pre-built `encodedList` value. */
|
|
54
|
+
readonly encodedList: string;
|
|
55
|
+
/** The issuer (string or object with id). */
|
|
56
|
+
readonly issuer: string | (JsonObject & {
|
|
57
|
+
id: string;
|
|
58
|
+
});
|
|
59
|
+
/** `validFrom` (XSD dateTime). Defaults to now when omitted. */
|
|
60
|
+
readonly validFrom?: Date | string;
|
|
61
|
+
/**
|
|
62
|
+
* `validUntil` (XSD dateTime). Bounds how long a verifier may treat the
|
|
63
|
+
* cached status as authoritative.
|
|
64
|
+
*/
|
|
65
|
+
readonly validUntil?: Date | string;
|
|
66
|
+
/**
|
|
67
|
+
* Cache time-to-live in milliseconds, advertised on both the credential and
|
|
68
|
+
* its status-list subject so verifiers can bound status caching.
|
|
69
|
+
*/
|
|
70
|
+
readonly ttl?: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Assemble an **unsigned** `BitstringStatusListCredential`. The caller signs it
|
|
74
|
+
* with {@link ./data-integrity.addProof} before publishing.
|
|
75
|
+
*
|
|
76
|
+
* Per the Bitstring Status List spec, `validFrom`/`validUntil`/`ttl` bound how
|
|
77
|
+
* long the published status may be cached; `validUntil` and `ttl` are emitted on
|
|
78
|
+
* the credential (and `ttl` also on the subject) when supplied.
|
|
79
|
+
*/
|
|
80
|
+
export declare function buildStatusListCredential(options: StatusListCredentialOptions): JsonObject;
|
|
81
|
+
/** Build a `credentialStatus` entry referencing a list index. */
|
|
82
|
+
export declare function buildStatusEntry(options: {
|
|
83
|
+
readonly statusListCredential: string;
|
|
84
|
+
readonly statusListIndex: number;
|
|
85
|
+
readonly statusPurpose: StatusPurposeValue;
|
|
86
|
+
readonly id?: string;
|
|
87
|
+
}): JsonObject;
|
|
88
|
+
/** Read a `statusListIndex` from a credential's `credentialStatus` entry. */
|
|
89
|
+
export declare function statusEntryIndex(entry: JsonObject): number | undefined;
|
|
90
|
+
/** Locate a `credentialStatus` entry for a purpose on a credential. */
|
|
91
|
+
export declare function findStatusEntry(credential: JsonObject, statusPurpose: StatusPurpose): JsonObject | undefined;
|
|
92
|
+
/** Cloudflare bindings required by the D1-backed status store. */
|
|
93
|
+
export interface VcStatusStoreEnv {
|
|
94
|
+
/** D1 database holding per-list status bits and index allocations. */
|
|
95
|
+
readonly VC_STATUS_DB: D1Database;
|
|
96
|
+
}
|
|
97
|
+
/** Authoritative store for credential status bits, backed by D1. */
|
|
98
|
+
export interface VcStatusStore {
|
|
99
|
+
/** Create the schema if absent. Idempotent. */
|
|
100
|
+
init(): Promise<void>;
|
|
101
|
+
/** Allocate and return the next unused index for a list + purpose. */
|
|
102
|
+
allocateIndex(listId: string, statusPurpose: StatusPurpose): Promise<number>;
|
|
103
|
+
/** Set the status bit at an index (e.g. revoke). */
|
|
104
|
+
setStatus(listId: string, statusPurpose: StatusPurpose, index: number, value: boolean): Promise<void>;
|
|
105
|
+
/** Read the status bit at an index (defaults to `false`/unset). */
|
|
106
|
+
getStatus(listId: string, statusPurpose: StatusPurpose, index: number): Promise<boolean>;
|
|
107
|
+
/** The set (value-1) indices for a list + purpose, ascending. */
|
|
108
|
+
setIndices(listId: string, statusPurpose: StatusPurpose): Promise<number[]>;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Create the D1-backed {@link VcStatusStore}. Fails loudly if the required
|
|
112
|
+
* `VC_STATUS_DB` binding is missing — no silent degradation (composition
|
|
113
|
+
* contract).
|
|
114
|
+
*/
|
|
115
|
+
export declare function createVcStatusStore(env: VcStatusStoreEnv): VcStatusStore;
|
|
116
|
+
//# sourceMappingURL=status-list.d.ts.map
|