Add comprehensive installation and setup documentation

- Add GETTING_STARTED.md with quick start guide and development modes
- Add INSTALL.sh automated installation script
- Add INSTALLATION_CHECKLIST.md, INSTALLATION_SUCCESS.md, and INSTALLATION_SUMMARY.md
- Add QUICK_REFERENCE.md for common commands
- Add SETUP_GUIDE.md with detailed setup instructions
- Update README.md with improved project overview
- Add did-wallet app dependencies and node_modules
This commit is contained in:
Dorian
2026-01-27 17:18:21 +00:00
parent a81f655133
commit 0d073fa89e
22658 changed files with 4494151 additions and 6 deletions
+196
View File
@@ -0,0 +1,196 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { LocalKeyManager, utils as cryptoUtils } from '@web5/crypto';
import { DidError, DidErrorCode } from './did-error.js';
import { extractDidFragment, getVerificationMethods } from './utils.js';
/**
* Represents a Decentralized Identifier (DID) along with its DID document, key manager, metadata,
* and convenience functions.
*/
export class BearerDid {
constructor({ uri, document, metadata, keyManager }) {
this.uri = uri;
this.document = document;
this.metadata = metadata;
this.keyManager = keyManager;
}
/**
* Converts a `BearerDid` object to a portable format containing the URI and verification methods
* associated with the DID.
*
* This method is useful when you need to represent the key material and metadata associated with
* a DID in format that can be used independently of the specific DID method implementation. It
* extracts both public and private keys from the DID's key manager and organizes them into a
* `PortableDid` structure.
*
* @remarks
* If the DID's key manager does not allow private keys to be exported, the `PortableDid` returned
* will not contain a `privateKeys` property. This enables the importing and exporting DIDs that
* use the same underlying KMS even if the KMS does not support exporting private keys. Examples
* include hardware security modules (HSMs) and cloud-based KMS services like AWS KMS.
*
* If the DID's key manager does support exporting private keys, the resulting `PortableDid` will
* include a `privateKeys` property which contains the same number of entries as there are
* verification methods as the DID document, each with its associated private key and the
* purpose(s) for which the key can be used (e.g., `authentication`, `assertionMethod`, etc.).
*
* @example
* ```ts
* // Assuming `did` is an instance of BearerDid
* const portableDid = await did.export();
* // portableDid now contains the DID URI, document, metadata, and optionally, private keys.
* ```
*
* @returns A `PortableDid` containing the URI, DID document, metadata, and optionally private
* keys associated with the `BearerDid`.
* @throws An error if the DID document does not contain any verification methods or the keys for
* any verification method are missing in the key manager.
*/
export() {
return __awaiter(this, void 0, void 0, function* () {
// Verify the DID document contains at least one verification method.
if (!(Array.isArray(this.document.verificationMethod) && this.document.verificationMethod.length > 0)) {
throw new Error(`DID document for '${this.uri}' is missing verification methods`);
}
// Create a new `PortableDid` object to store the exported data.
let portableDid = {
uri: this.uri,
document: this.document,
metadata: this.metadata
};
// If the BearerDid's key manager supports exporting private keys, add them to the portable DID.
if ('exportKey' in this.keyManager && typeof this.keyManager.exportKey === 'function') {
const privateKeys = [];
for (let vm of this.document.verificationMethod) {
if (!vm.publicKeyJwk) {
throw new Error(`Verification method '${vm.id}' does not contain a public key in JWK format`);
}
// Compute the key URI of the verification method's public key.
const keyUri = yield this.keyManager.getKeyUri({ key: vm.publicKeyJwk });
// Retrieve the private key from the key manager.
const privateKey = yield this.keyManager.exportKey({ keyUri });
// Add the verification method to the key set.
privateKeys.push(Object.assign({}, privateKey));
}
portableDid.privateKeys = privateKeys;
}
return portableDid;
});
}
/**
* Return a {@link Signer} that can be used to sign messages, credentials, or arbitrary data.
*
* If given, the `methodId` parameter is used to select a key from the verification methods
* present in the DID Document.
*
* If `methodID` is not given, the first verification method intended for signing claims is used.
*
* @param params - The parameters for the `getSigner` operation.
* @param params.methodId - ID of the verification method key that will be used for sign and
* verify operations. Optional.
* @returns An instantiated {@link Signer} that can be used to sign and verify data.
*/
getSigner(params) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
// Attempt to find a verification method that matches the given method ID, or if not given,
// find the first verification method intended for signing claims.
const verificationMethod = (_a = this.document.verificationMethod) === null || _a === void 0 ? void 0 : _a.find(vm => { var _a, _b; return extractDidFragment(vm.id) === ((_a = extractDidFragment(params === null || params === void 0 ? void 0 : params.methodId)) !== null && _a !== void 0 ? _a : extractDidFragment((_b = this.document.assertionMethod) === null || _b === void 0 ? void 0 : _b[0])); });
if (!(verificationMethod && verificationMethod.publicKeyJwk)) {
throw new DidError(DidErrorCode.InternalError, 'A verification method intended for signing could not be determined from the DID Document');
}
// Compute the expected key URI of the signing key.
const keyUri = yield this.keyManager.getKeyUri({ key: verificationMethod.publicKeyJwk });
// Get the public key to be used for verify operations, which also verifies that the key is
// present in the key manager's store.
const publicKey = yield this.keyManager.getPublicKey({ keyUri });
// Bind the DID's key manager to the signer.
const keyManager = this.keyManager;
// Determine the signing algorithm.
const algorithm = cryptoUtils.getJoseSignatureAlgorithmFromPublicKey(publicKey);
return {
algorithm: algorithm,
keyId: verificationMethod.id,
sign(_a) {
return __awaiter(this, arguments, void 0, function* ({ data }) {
const signature = yield keyManager.sign({ data, keyUri: keyUri }); // `keyUri` is guaranteed to be defined at this point.
return signature;
});
},
verify(_a) {
return __awaiter(this, arguments, void 0, function* ({ data, signature }) {
const isValid = yield keyManager.verify({ data, key: publicKey, signature }); // `publicKey` is guaranteed to be defined at this point.
return isValid;
});
}
};
});
}
/**
* Instantiates a {@link BearerDid} object from a given {@link PortableDid}.
*
* This method allows for the creation of a `BearerDid` object using a previously created DID's
* key material, DID document, and metadata.
*
* @example
* ```ts
* // Export an existing BearerDid to PortableDid format.
* const portableDid = await did.export();
* // Reconstruct a BearerDid object from the PortableDid.
* const did = await BearerDid.import({ portableDid });
* ```
*
* @param params - The parameters for the import operation.
* @param params.portableDid - The PortableDid object to import.
* @param params.keyManager - Optionally specify an external Key Management System (KMS) used to
* generate keys and sign data. If not given, a new
* {@link LocalKeyManager} instance will be created and
* used.
* @returns A Promise resolving to a `BearerDid` object representing the DID formed from the
* provided PortableDid.
* @throws An error if the PortableDid document does not contain any verification methods or the
* keys for any verification method are missing in the key manager.
*/
static import(_a) {
return __awaiter(this, arguments, void 0, function* ({ portableDid, keyManager = new LocalKeyManager() }) {
var _b;
// Get all verification methods from the given DID document, including embedded methods.
const verificationMethods = getVerificationMethods({ didDocument: portableDid.document });
// Validate that the DID document contains at least one verification method.
if (verificationMethods.length === 0) {
throw new DidError(DidErrorCode.InvalidDidDocument, `At least one verification method is required but 0 were given`);
}
// If given, import the private key material into the key manager.
for (let key of (_b = portableDid.privateKeys) !== null && _b !== void 0 ? _b : []) {
yield keyManager.importKey({ key });
}
// Validate that the key material for every verification method in the DID document is present
// in the key manager.
for (let vm of verificationMethods) {
if (!vm.publicKeyJwk) {
throw new Error(`Verification method '${vm.id}' does not contain a public key in JWK format`);
}
// Compute the key URI of the verification method's public key.
const keyUri = yield keyManager.getKeyUri({ key: vm.publicKeyJwk });
// Verify that the key is present in the key manager. If not, an error is thrown.
yield keyManager.getPublicKey({ keyUri });
}
// Use the given PortableDid to construct the BearerDid object.
const did = new BearerDid({
uri: portableDid.uri,
document: portableDid.document,
metadata: portableDid.metadata,
keyManager
});
return did;
});
}
}
//# sourceMappingURL=bearer-did.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"bearer-did.js","sourceRoot":"","sources":["../../src/bearer-did.ts"],"names":[],"mappings":"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"}
+62
View File
@@ -0,0 +1,62 @@
/**
* A custom error class for DID-related errors.
*/
export class DidError extends Error {
/**
* Constructs an instance of DidError, a custom error class for handling DID-related errors.
*
* @param code - A {@link DidErrorCode} representing the specific type of error encountered.
* @param message - A human-readable description of the error.
*/
constructor(code, message) {
super(`${code}: ${message}`);
this.code = code;
this.name = 'DidError';
// Ensures that instanceof works properly, the correct prototype chain when using inheritance,
// and that V8 stack traces (like Chrome, Edge, and Node.js) are more readable and relevant.
Object.setPrototypeOf(this, new.target.prototype);
// Captures the stack trace in V8 engines (like Chrome, Edge, and Node.js).
// In non-V8 environments, the stack trace will still be captured.
if (Error.captureStackTrace) {
Error.captureStackTrace(this, DidError);
}
}
}
/**
* An enumeration of possible DID error codes.
*/
export var DidErrorCode;
(function (DidErrorCode) {
/** The DID supplied does not conform to valid syntax. */
DidErrorCode["InvalidDid"] = "invalidDid";
/** The supplied method name is not supported by the DID method and/or DID resolver implementation. */
DidErrorCode["MethodNotSupported"] = "methodNotSupported";
/** An unexpected error occurred during the requested DID operation. */
DidErrorCode["InternalError"] = "internalError";
/** The DID document supplied does not conform to valid syntax. */
DidErrorCode["InvalidDidDocument"] = "invalidDidDocument";
/** The byte length of a DID document does not match the expected value. */
DidErrorCode["InvalidDidDocumentLength"] = "invalidDidDocumentLength";
/** The DID URL supplied to the dereferencing function does not conform to valid syntax. */
DidErrorCode["InvalidDidUrl"] = "invalidDidUrl";
/** The given proof of a previous DID is invalid */
DidErrorCode["InvalidPreviousDidProof"] = "invalidPreviousDidProof";
/** An invalid public key is detected during a DID operation. */
DidErrorCode["InvalidPublicKey"] = "invalidPublicKey";
/** The byte length of a public key does not match the expected value. */
DidErrorCode["InvalidPublicKeyLength"] = "invalidPublicKeyLength";
/** An invalid public key type was detected during a DID operation. */
DidErrorCode["InvalidPublicKeyType"] = "invalidPublicKeyType";
/** Verification of a signature failed during a DID operation. */
DidErrorCode["InvalidSignature"] = "invalidSignature";
/** The DID resolver was unable to find the DID document resulting from the resolution request. */
DidErrorCode["NotFound"] = "notFound";
/**
* The representation requested via the `accept` input metadata property is not supported by the
* DID method and/or DID resolver implementation.
*/
DidErrorCode["RepresentationNotSupported"] = "representationNotSupported";
/** The type of a public key is not supported by the DID method and/or DID resolver implementation. */
DidErrorCode["UnsupportedPublicKeyType"] = "unsupportedPublicKeyType";
})(DidErrorCode || (DidErrorCode = {}));
//# sourceMappingURL=did-error.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"did-error.js","sourceRoot":"","sources":["../../src/did-error.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjC;;;;;OAKG;IACH,YAAmB,IAAkB,EAAE,OAAe;QACpD,KAAK,CAAC,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;QADZ,SAAI,GAAJ,IAAI,CAAc;QAEnC,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QAEvB,8FAA8F;QAC9F,4FAA4F;QAC5F,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAElD,2EAA2E;QAC3E,kEAAkE;QAClE,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;CACF;AAED;;GAEG;AACH,MAAM,CAAN,IAAY,YA6CX;AA7CD,WAAY,YAAY;IACtB,yDAAyD;IACzD,yCAAyB,CAAA;IAEzB,sGAAsG;IACtG,yDAAyC,CAAA;IAEzC,uEAAuE;IACvE,+CAA+B,CAAA;IAE/B,kEAAkE;IAClE,yDAAyC,CAAA;IAEzC,2EAA2E;IAC3E,qEAAqD,CAAA;IAErD,2FAA2F;IAC3F,+CAA+B,CAAA;IAE/B,mDAAmD;IACnD,mEAAmD,CAAA;IAEnD,gEAAgE;IAChE,qDAAqC,CAAA;IAErC,yEAAyE;IACzE,iEAAiD,CAAA;IAEjD,sEAAsE;IACtE,6DAA6C,CAAA;IAE7C,iEAAiE;IACjE,qDAAqC,CAAA;IAErC,kGAAkG;IAClG,qCAAqB,CAAA;IAErB;;;OAGG;IACH,yEAAyD,CAAA;IAEzD,sGAAsG;IACtG,qEAAqD,CAAA;AACvD,CAAC,EA7CW,YAAY,KAAZ,YAAY,QA6CvB"}
+114
View File
@@ -0,0 +1,114 @@
/**
* The `Did` class represents a Decentralized Identifier (DID) Uniform Resource Identifier (URI).
*
* This class provides a method for parsing a DID URI string into its component parts, as well as a
* method for serializing a DID URI object into a string.
*
* A DID URI is composed of the following components:
* - scheme
* - method
* - id
* - path
* - query
* - fragment
* - params
*
* @see {@link https://www.w3.org/TR/did-core/#did-syntax | DID Core Specification, § DID Syntax}
*/
export class Did {
/**
* Constructs a new `Did` instance from individual components.
*
* @param params - An object containing the parameters to be included in the DID URI.
* @param params.method - The name of the DID method.
* @param params.id - The DID method identifier.
* @param params.path - Optional. The path component of the DID URI.
* @param params.query - Optional. The query component of the DID URI.
* @param params.fragment - Optional. The fragment component of the DID URI.
* @param params.params - Optional. The query parameters in the DID URI.
*/
constructor({ method, id, path, query, fragment, params }) {
this.uri = `did:${method}:${id}`;
this.method = method;
this.id = id;
this.path = path;
this.query = query;
this.fragment = fragment;
this.params = params;
}
/**
* Parses a DID URI string into its individual components.
*
* @example
* ```ts
* const did = Did.parse('did:example:123?service=agent&relativeRef=/credentials#degree');
*
* console.log(did.uri) // Output: 'did:example:123'
* console.log(did.method) // Output: 'example'
* console.log(did.id) // Output: '123'
* console.log(did.query) // Output: 'service=agent&relativeRef=/credentials'
* console.log(did.fragment) // Output: 'degree'
* console.log(did.params) // Output: { service: 'agent', relativeRef: '/credentials' }
* ```
*
* @params didUri - The DID URI string to be parsed.
* @returns A `Did` object representing the parsed DID URI, or `null` if the input string is not a valid DID URI.
*/
static parse(didUri) {
// Return null if the input string is empty or not provided.
if (!didUri)
return null;
// Execute the regex pattern on the input string to extract URI components.
const match = Did.DID_URI_PATTERN.exec(didUri);
// If the pattern does not match, or if the required groups are not found, return null.
if (!match || !match.groups)
return null;
// Extract the method, id, params, path, query, and fragment from the regex match groups.
const { method, id, path, query, fragment } = match.groups;
// Initialize a new Did object with the uri, method and id.
const did = {
uri: `did:${method}:${id}`,
method,
id,
};
// If path is present, add it to the Did object.
if (path)
did.path = path;
// If query is present, add it to the Did object, removing the leading '?'.
if (query)
did.query = query.slice(1);
// If fragment is present, add it to the Did object, removing the leading '#'.
if (fragment)
did.fragment = fragment.slice(1);
// If query params are present, parse them into a key-value object and add to the Did object.
if (query) {
const parsedParams = {};
// Split the query string by '&' to get individual parameter strings.
const paramPairs = query.slice(1).split('&');
for (const pair of paramPairs) {
// Split each parameter string by '=' to separate keys and values.
const [key, value] = pair.split('=');
parsedParams[key] = value;
}
did.params = parsedParams;
}
return did;
}
}
/** Regular expression pattern for matching the method component of a DID URI. */
Did.METHOD_PATTERN = '([a-z0-9]+)';
/** Regular expression pattern for matching percent-encoded characters in a method identifier. */
Did.PCT_ENCODED_PATTERN = '(?:%[0-9a-fA-F]{2})';
/** Regular expression pattern for matching the characters allowed in a method identifier. */
Did.ID_CHAR_PATTERN = `(?:[a-zA-Z0-9._-]|${Did.PCT_ENCODED_PATTERN})`;
/** Regular expression pattern for matching the method identifier component of a DID URI. */
Did.METHOD_ID_PATTERN = `((?:${Did.ID_CHAR_PATTERN}*:)*(${Did.ID_CHAR_PATTERN}+))`;
/** Regular expression pattern for matching the path component of a DID URI. */
Did.PATH_PATTERN = `(/[^#?]*)?`;
/** Regular expression pattern for matching the query component of a DID URI. */
Did.QUERY_PATTERN = `([?][^#]*)?`;
/** Regular expression pattern for matching the fragment component of a DID URI. */
Did.FRAGMENT_PATTERN = `(#.*)?`;
/** Regular expression pattern for matching all of the components of a DID URI. */
Did.DID_URI_PATTERN = new RegExp(`^did:(?<method>${Did.METHOD_PATTERN}):(?<id>${Did.METHOD_ID_PATTERN})(?<path>${Did.PATH_PATTERN})(?<query>${Did.QUERY_PATTERN})(?<fragment>${Did.FRAGMENT_PATTERN})$`);
//# sourceMappingURL=did.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"did.js","sourceRoot":"","sources":["../../src/did.ts"],"names":[],"mappings":"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"}
+16
View File
@@ -0,0 +1,16 @@
export * from './types/did-core.js';
export * from './types/did-resolution.js';
export * from './did.js';
export * from './did-error.js';
export * from './bearer-did.js';
export * from './methods/did-dht.js';
export * from './methods/did-ion.js';
export * from './methods/did-jwk.js';
export * from './methods/did-key.js';
export * from './methods/did-method.js';
export * from './methods/did-web.js';
export * from './resolver/resolver-cache-level.js';
export * from './resolver/resolver-cache-noop.js';
export * from './resolver/universal-resolver.js';
export * as utils from './utils.js';
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAI1C,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAEhC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AAErC,cAAc,oCAAoC,CAAC;AACnD,cAAc,mCAAmC,CAAC;AAClD,cAAc,kCAAkC,CAAC;AAEjD,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC"}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+570
View File
@@ -0,0 +1,570 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { IonDid, IonRequest } from '@decentralized-identity/ion-sdk';
import { LocalKeyManager, computeJwkThumbprint } from '@web5/crypto';
import { Did } from '../did.js';
import { BearerDid } from '../bearer-did.js';
import { DidMethod } from '../methods/did-method.js';
import { DidError, DidErrorCode } from '../did-error.js';
import { getVerificationRelationshipsById } from '../utils.js';
import { EMPTY_DID_RESOLUTION_RESULT } from '../types/did-resolution.js';
/**
* Enumerates the types of keys that can be used in a DID ION document.
*
* The DID ION method supports various cryptographic key types. These key types are essential for
* the creation and management of DIDs and their associated cryptographic operations like signing
* and encryption.
*/
export var DidIonRegisteredKeyType;
(function (DidIonRegisteredKeyType) {
/**
* Ed25519: A public-key signature system using the EdDSA (Edwards-curve Digital Signature
* Algorithm) and Curve25519.
*/
DidIonRegisteredKeyType["Ed25519"] = "Ed25519";
/**
* secp256k1: A cryptographic curve used for digital signatures in a range of decentralized
* systems.
*/
DidIonRegisteredKeyType["secp256k1"] = "secp256k1";
/**
* secp256r1: Also known as P-256 or prime256v1, this curve is used for cryptographic operations
* and is widely supported in various cryptographic libraries and standards.
*/
DidIonRegisteredKeyType["secp256r1"] = "secp256r1";
/**
* X25519: A Diffie-Hellman key exchange algorithm using Curve25519.
*/
DidIonRegisteredKeyType["X25519"] = "X25519";
})(DidIonRegisteredKeyType || (DidIonRegisteredKeyType = {}));
/**
* Private helper that maps algorithm identifiers to their corresponding DID ION
* {@link DidIonRegisteredKeyType | registered key type}.
*/
const AlgorithmToKeyTypeMap = {
Ed25519: DidIonRegisteredKeyType.Ed25519,
ES256K: DidIonRegisteredKeyType.secp256k1,
ES256: DidIonRegisteredKeyType.secp256r1,
'P-256': DidIonRegisteredKeyType.secp256r1,
secp256k1: DidIonRegisteredKeyType.secp256k1,
secp256r1: DidIonRegisteredKeyType.secp256r1
};
/**
* The default node to use as a gateway to the Sidetree newtork when anchoring, updating, and
* resolving DID documents.
*/
const DEFAULT_GATEWAY_URI = 'https://ion.tbd.engineering';
/**
* The `DidIon` class provides an implementation of the `did:ion` DID method.
*
* Features:
* - DID Creation: Create new `did:ion` DIDs.
* - DID Key Management: Instantiate a DID object from an existing key in a Key Management System
* (KMS). If supported by the KMS, a DID's key can be exported to a portable
* DID format.
* - DID Resolution: Resolve a `did:ion` to its corresponding DID Document stored in the Sidetree
* network.
* - Signature Operations: Sign and verify messages using keys associated with a DID.
*
* @see {@link https://identity.foundation/sidetree/spec/ | Sidetree Protocol Specification}
* @see {@link https://github.com/decentralized-identity/ion/blob/master/docs/design.md | ION Design Document}
*
* @example
* ```ts
* // DID Creation
* const did = await DidIon.create();
*
* // DID Creation with a KMS
* const keyManager = new LocalKeyManager();
* const did = await DidIon.create({ keyManager });
*
* // DID Resolution
* const resolutionResult = await DidIon.resolve({ did: did.uri });
*
* // Signature Operations
* const signer = await did.getSigner();
* const signature = await signer.sign({ data: new TextEncoder().encode('Message') });
* const isValid = await signer.verify({ data: new TextEncoder().encode('Message'), signature });
*
* // Key Management
*
* // Instantiate a DID object for a published DID with existing keys in a KMS
* const did = await DidIon.fromKeyManager({
* didUri: 'did:ion:EiAzB7K-xDIKc1csXo5HX2eNBoemK9feNhL3cKwfukYOug',
* keyManager
* });
*
* // Convert a DID object to a portable format
* const portableDid = await DidIon.toKeys({ did });
* ```
*/
export class DidIon extends DidMethod {
/**
* Creates a new DID using the `did:ion` method formed from a newly generated key.
*
* Notes:
* - If no `options` are given, by default a new Ed25519 key will be generated.
*
* @example
* ```ts
* // DID Creation
* const did = await DidIon.create();
*
* // DID Creation with a KMS
* const keyManager = new LocalKeyManager();
* const did = await DidIon.create({ keyManager });
* ```
*
* @param params - The parameters for the create operation.
* @param params.keyManager - Optionally specify a Key Management System (KMS) used to generate
* keys and sign data.
* @param params.options - Optional parameters that can be specified when creating a new DID.
* @returns A Promise resolving to a {@link BearerDid} object representing the new DID.
*/
static create() {
return __awaiter(this, arguments, void 0, function* ({ keyManager = new LocalKeyManager(), options = {} } = {}) {
// Before processing the create operation, validate DID-method-specific requirements to prevent
// keys from being generated unnecessarily.
var _a, _b, _c, _d, _e, _f, _g;
// Check 1: Validate that the algorithm for any given verification method is supported by the
// DID ION specification.
if ((_a = options.verificationMethods) === null || _a === void 0 ? void 0 : _a.some(vm => !(vm.algorithm in AlgorithmToKeyTypeMap))) {
throw new Error('One or more verification method algorithms are not supported');
}
// Check 2: Validate that the ID for any given verification method is unique.
const methodIds = (_b = options.verificationMethods) === null || _b === void 0 ? void 0 : _b.filter(vm => 'id' in vm).map(vm => vm.id);
if (methodIds && methodIds.length !== new Set(methodIds).size) {
throw new Error('One or more verification method IDs are not unique');
}
// Check 3: Validate that the required properties for any given services are present.
if ((_c = options.services) === null || _c === void 0 ? void 0 : _c.some(s => !s.id || !s.type || !s.serviceEndpoint)) {
throw new Error('One or more services are missing required properties');
}
// If no verification methods were specified, generate a default Ed25519 verification method.
const defaultVerificationMethod = {
algorithm: 'Ed25519',
purposes: ['authentication', 'assertionMethod', 'capabilityDelegation', 'capabilityInvocation']
};
const verificationMethodsToAdd = [];
// Generate random key material for additional verification methods, if any.
for (const vm of (_d = options.verificationMethods) !== null && _d !== void 0 ? _d : [defaultVerificationMethod]) {
// Generate a random key for the verification method.
const keyUri = yield keyManager.generateKey({ algorithm: vm.algorithm });
const publicKey = yield keyManager.getPublicKey({ keyUri });
// Add the verification method to the DID document.
verificationMethodsToAdd.push({
id: vm.id,
publicKeyJwk: publicKey,
purposes: (_e = vm.purposes) !== null && _e !== void 0 ? _e : ['authentication', 'assertionMethod', 'capabilityDelegation', 'capabilityInvocation']
});
}
// Generate a random key for the ION Recovery Key. Sidetree requires secp256k1 recovery keys.
const recoveryKeyUri = yield keyManager.generateKey({ algorithm: DidIonRegisteredKeyType.secp256k1 });
const recoveryKey = yield keyManager.getPublicKey({ keyUri: recoveryKeyUri });
// Generate a random key for the ION Update Key. Sidetree requires secp256k1 update keys.
const updateKeyUri = yield keyManager.generateKey({ algorithm: DidIonRegisteredKeyType.secp256k1 });
const updateKey = yield keyManager.getPublicKey({ keyUri: updateKeyUri });
// Compute the Long Form DID URI from the keys and services, if any.
const longFormDidUri = yield DidIonUtils.computeLongFormDidUri({
recoveryKey,
updateKey,
services: (_f = options.services) !== null && _f !== void 0 ? _f : [],
verificationMethods: verificationMethodsToAdd
});
// Expand the DID URI string to a DID document.
const { didDocument, didResolutionMetadata } = yield DidIon.resolve(longFormDidUri, { gatewayUri: options.gatewayUri });
if (didDocument === null) {
throw new Error(`Unable to resolve DID during creation: ${didResolutionMetadata === null || didResolutionMetadata === void 0 ? void 0 : didResolutionMetadata.error}`);
}
// Create the BearerDid object, including the "Short Form" of the DID URI, the ION update and
// recovery keys, and specifying that the DID has not yet been published.
const did = new BearerDid({
uri: longFormDidUri,
document: didDocument,
metadata: {
published: false,
canonicalId: longFormDidUri.split(':', 3).join(':'),
recoveryKey,
updateKey
},
keyManager
});
// By default, publish the DID document to a Sidetree node unless explicitly disabled.
if ((_g = options.publish) !== null && _g !== void 0 ? _g : true) {
const registrationResult = yield DidIon.publish({ did, gatewayUri: options.gatewayUri });
did.metadata = registrationResult.didDocumentMetadata;
}
return did;
});
}
/**
* Given the W3C DID Document of a `did:ion` DID, return the verification method that will be used
* for signing messages and credentials. If given, the `methodId` parameter is used to select the
* verification method. If not given, the first verification method in the authentication property
* in the DID Document is used.
*
* @param params - The parameters for the `getSigningMethod` operation.
* @param params.didDocument - DID Document to get the verification method from.
* @param params.methodId - ID of the verification method to use for signing.
* @returns Verification method to use for signing.
*/
static getSigningMethod(_a) {
return __awaiter(this, arguments, void 0, function* ({ didDocument, methodId }) {
var _b;
// Verify the DID method is supported.
const parsedDid = Did.parse(didDocument.id);
if (parsedDid && parsedDid.method !== this.methodName) {
throw new DidError(DidErrorCode.MethodNotSupported, `Method not supported: ${parsedDid.method}`);
}
// Get the verification method with either the specified ID or the first assertion method.
const verificationMethod = (_b = didDocument.verificationMethod) === null || _b === void 0 ? void 0 : _b.find(vm => { var _a; return vm.id === (methodId !== null && methodId !== void 0 ? methodId : (_a = didDocument.assertionMethod) === null || _a === void 0 ? void 0 : _a[0]); });
if (!(verificationMethod && verificationMethod.publicKeyJwk)) {
throw new DidError(DidErrorCode.InternalError, 'A verification method intended for signing could not be determined from the DID Document');
}
return verificationMethod;
});
}
/**
* Instantiates a {@link BearerDid} object for the DID ION method from a given {@link PortableDid}.
*
* This method allows for the creation of a `BearerDid` object using a previously created DID's
* key material, DID document, and metadata.
*
* @example
* ```ts
* // Export an existing BearerDid to PortableDid format.
* const portableDid = await did.export();
* // Reconstruct a BearerDid object from the PortableDid.
* const did = await DidIon.import({ portableDid });
* ```
*
* @param params - The parameters for the import operation.
* @param params.portableDid - The PortableDid object to import.
* @param params.keyManager - Optionally specify an external Key Management System (KMS) used to
* generate keys and sign data. If not given, a new
* {@link LocalKeyManager} instance will be created and
* used.
* @returns A Promise resolving to a `BearerDid` object representing the DID formed from the
* provided PortableDid.
* @throws An error if the DID document does not contain any verification methods or the keys for
* any verification method are missing in the key manager.
*/
static import(_a) {
return __awaiter(this, arguments, void 0, function* ({ portableDid, keyManager = new LocalKeyManager() }) {
// Verify the DID method is supported.
const parsedDid = Did.parse(portableDid.uri);
if ((parsedDid === null || parsedDid === void 0 ? void 0 : parsedDid.method) !== DidIon.methodName) {
throw new DidError(DidErrorCode.MethodNotSupported, `Method not supported`);
}
const did = yield BearerDid.import({ portableDid, keyManager });
return did;
});
}
/**
* Publishes a DID to a Sidetree node, making it publicly discoverable and resolvable.
*
* This method handles the publication of a DID Document associated with a `did:ion` DID to a
* Sidetree node.
*
* @remarks
* - This method is typically invoked automatically during the creation of a new DID unless the
* `publish` option is set to `false`.
* - For existing, unpublished DIDs, it can be used to publish the DID Document to a Sidetree node.
* - The method relies on the specified Sidetree node to interface with the network.
*
* @param params - The parameters for the `publish` operation.
* @param params.did - The `BearerDid` object representing the DID to be published.
* @param params.gatewayUri - Optional. The URI of a server involved in executing DID
* method operations. In the context of publishing, the
* endpoint is expected to be a Sidetree node. If not
* specified, a default node is used.
* @returns A Promise resolving to a boolean indicating whether the publication was successful.
*
* @example
* ```ts
* // Generate a new DID and keys but explicitly disable publishing.
* const did = await DidIon.create({ options: { publish: false } });
* // Publish the DID to the Sidetree network.
* const isPublished = await DidIon.publish({ did });
* // `isPublished` is true if the DID was successfully published.
* ```
*/
static publish(_a) {
return __awaiter(this, arguments, void 0, function* ({ did, gatewayUri = DEFAULT_GATEWAY_URI }) {
var _b, _c, _d;
// Construct an ION verification method made up of the id, public key, and purposes from each
// verification method in the DID document.
const verificationMethods = (_c = (_b = did.document.verificationMethod) === null || _b === void 0 ? void 0 : _b.map(vm => ({
id: vm.id,
publicKeyJwk: vm.publicKeyJwk,
purposes: getVerificationRelationshipsById({ didDocument: did.document, methodId: vm.id })
}))) !== null && _c !== void 0 ? _c : [];
// Create the ION document.
const ionDocument = yield DidIonUtils.createIonDocument({
services: (_d = did.document.service) !== null && _d !== void 0 ? _d : [],
verificationMethods
});
// Construct the ION Create Operation request.
const createOperation = yield DidIonUtils.constructCreateRequest({
ionDocument,
recoveryKey: did.metadata.recoveryKey,
updateKey: did.metadata.updateKey
});
try {
// Construct the URL of the SideTree node's operations endpoint.
const operationsUrl = DidIonUtils.appendPathToUrl({
baseUrl: gatewayUri,
path: `/operations`
});
// Submit the Create Operation to the operations endpoint.
const response = yield fetch(operationsUrl, {
method: 'POST',
mode: 'cors',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(createOperation)
});
// Return the result of processing the Create operation, including the updated DID metadata
// with the publishing result.
return {
didDocument: did.document,
didDocumentMetadata: Object.assign(Object.assign({}, did.metadata), { published: response.ok }),
didRegistrationMetadata: {}
};
}
catch (error) {
return {
didDocument: null,
didDocumentMetadata: {
published: false,
},
didRegistrationMetadata: {
error: DidErrorCode.InternalError,
errorMessage: `Failed to publish DID document for: ${did.uri}`
}
};
}
});
}
/**
* Resolves a `did:ion` identifier to its corresponding DID document.
*
* This method performs the resolution of a `did:ion` DID, retrieving its DID Document from the
* Sidetree-based DID overlay network. The process involves querying a Sidetree node to retrieve
* the DID Document that corresponds to the given DID identifier.
*
* @remarks
* - If a `gatewayUri` option is not specified, a default node is used to access the Sidetree
* network.
* - It decodes the DID identifier and retrieves the associated DID Document and metadata.
* - In case of resolution failure, appropriate error information is returned.
*
* @example
* ```ts
* const resolutionResult = await DidIon.resolve('did:ion:example');
* ```
*
* @param didUri - The DID to be resolved.
* @param options - Optional parameters for resolving the DID. Unused by this DID method.
* @returns A Promise resolving to a {@link DidResolutionResult} object representing the result of the resolution.
*/
static resolve(didUri_1) {
return __awaiter(this, arguments, void 0, function* (didUri, options = {}) {
var _a, _b;
// Attempt to parse the DID URI.
const parsedDid = Did.parse(didUri);
// If parsing failed, the DID is invalid.
if (!parsedDid) {
return Object.assign(Object.assign({}, EMPTY_DID_RESOLUTION_RESULT), { didResolutionMetadata: { error: 'invalidDid' } });
}
// If the DID method is not "ion", return an error.
if (parsedDid.method !== DidIon.methodName) {
return Object.assign(Object.assign({}, EMPTY_DID_RESOLUTION_RESULT), { didResolutionMetadata: { error: 'methodNotSupported' } });
}
// To execute the read method operation, use the given gateway URI or a default Sidetree node.
const gatewayUri = (_a = options === null || options === void 0 ? void 0 : options.gatewayUri) !== null && _a !== void 0 ? _a : DEFAULT_GATEWAY_URI;
try {
// Construct the URL to be used in the resolution request.
const resolutionUrl = DidIonUtils.appendPathToUrl({
baseUrl: gatewayUri,
path: `/identifiers/${didUri}`
});
// Attempt to retrieve the DID document and metadata from the Sidetree node.
const response = yield fetch(resolutionUrl);
// If the DID document was not found, return an error.
if (!response.ok) {
throw new DidError(DidErrorCode.NotFound, `Unable to find DID document for: ${didUri}`);
}
// If the DID document was retrieved successfully, return it.
const { didDocument, didDocumentMetadata } = yield response.json();
return Object.assign(Object.assign(Object.assign({}, EMPTY_DID_RESOLUTION_RESULT), didDocument && { didDocument }), { didDocumentMetadata: Object.assign({ published: (_b = didDocumentMetadata === null || didDocumentMetadata === void 0 ? void 0 : didDocumentMetadata.method) === null || _b === void 0 ? void 0 : _b.published }, didDocumentMetadata) });
}
catch (error) {
// Rethrow any unexpected errors that are not a `DidError`.
if (!(error instanceof DidError))
throw new Error(error);
// Return a DID Resolution Result with the appropriate error code.
return Object.assign(Object.assign({}, EMPTY_DID_RESOLUTION_RESULT), { didResolutionMetadata: Object.assign({ error: error.code }, error.message && { errorMessage: error.message }) });
}
});
}
}
/**
* Name of the DID method, as defined in the DID ION specification.
*/
DidIon.methodName = 'ion';
/**
* The `DidIonUtils` class provides utility functions to support operations in the DID ION method.
*/
export class DidIonUtils {
/**
* Appends a specified path to a base URL, ensuring proper formatting of the resulting URL.
*
* This method is useful for constructing URLs for accessing various endpoints, such as Sidetree
* nodes in the ION network. It handles the nuances of URL path concatenation, including the
* addition or removal of leading/trailing slashes, to create a well-formed URL.
*
* @param params - The parameters for URL construction.
* @param params.baseUrl - The base URL to which the path will be appended.
* @param params.path - The path to append to the base URL.
* @returns The fully constructed URL string with the path appended to the base URL.
*/
static appendPathToUrl({ baseUrl, path }) {
const url = new URL(baseUrl);
url.pathname = url.pathname.endsWith('/') ? url.pathname : url.pathname + '/';
url.pathname += path.startsWith('/') ? path.substring(1) : path;
return url.toString();
}
/**
* Computes the Long Form DID URI given an ION DID's recovery key, update key, services, and
* verification methods.
*
* @param params - The parameters for computing the Long Form DID URI.
* @param params.recoveryKey - The ION Recovery Key.
* @param params.updateKey - The ION Update Key.
* @param params.services - An array of services associated with the DID.
* @param params.verificationMethods - An array of verification methods associated with the DID.
* @returns A Promise resolving to the Long Form DID URI.
*/
static computeLongFormDidUri(_a) {
return __awaiter(this, arguments, void 0, function* ({ recoveryKey, updateKey, services, verificationMethods }) {
// Create the ION document.
const ionDocument = yield DidIonUtils.createIonDocument({ services, verificationMethods });
// Normalize JWK to onnly include specific members and in lexicographic order.
const normalizedRecoveryKey = DidIonUtils.normalizeJwk(recoveryKey);
const normalizedUpdateKey = DidIonUtils.normalizeJwk(updateKey);
// Compute the Long Form DID URI.
const longFormDidUri = yield IonDid.createLongFormDid({
document: ionDocument,
recoveryKey: normalizedRecoveryKey,
updateKey: normalizedUpdateKey
});
return longFormDidUri;
});
}
/**
* Constructs a Sidetree Create Operation request for a DID document within the ION network.
*
* This method prepares the necessary payload for submitting a Create Operation to a Sidetree
* node, encapsulating the details of the DID document, recovery key, and update key.
*
* @param params - Parameters required to construct the Create Operation request.
* @param params.ionDocument - The DID document model containing public keys and service endpoints.
* @param params.recoveryKey - The recovery public key in JWK format.
* @param params.updateKey - The update public key in JWK format.
* @returns A promise resolving to the ION Create Operation request model, ready for submission to a Sidetree node.
*/
static constructCreateRequest(_a) {
return __awaiter(this, arguments, void 0, function* ({ ionDocument, recoveryKey, updateKey }) {
// Create an ION DID create request operation.
const createRequest = yield IonRequest.createCreateRequest({
document: ionDocument,
recoveryKey: DidIonUtils.normalizeJwk(recoveryKey),
updateKey: DidIonUtils.normalizeJwk(updateKey)
});
return createRequest;
});
}
/**
* Assembles an ION document model from provided services and verification methods
*
* This model serves as the foundation for a DID document in the ION network, facilitating the
* creation and management of decentralized identities. It translates service endpoints and
* public keys into a format compatible with the Sidetree protocol, ensuring the resulting DID
* document adheres to the required specifications for ION DIDs. This method is essential for
* constructing the payload needed to register or update DIDs within the ION network.
*
* @param params - The parameters containing the services and verification methods to include in the ION document.
* @param params.services - A list of service endpoints to be included in the DID document, specifying ways to interact with the DID subject.
* @param params.verificationMethods - A list of verification methods to be included, detailing the cryptographic keys and their intended uses within the DID document.
* @returns A Promise resolving to an `IonDocumentModel`, ready for use in Sidetree operations like DID creation and updates.
*/
static createIonDocument(_a) {
return __awaiter(this, arguments, void 0, function* ({ services, verificationMethods }) {
var _b, _c;
/**
* STEP 1: Convert verification methods to ION SDK format.
*/
const ionPublicKeys = [];
for (const vm of verificationMethods) {
// Use the given ID, the key's ID, or the key's thumbprint as the verification method ID.
let methodId = (_c = (_b = vm.id) !== null && _b !== void 0 ? _b : vm.publicKeyJwk.kid) !== null && _c !== void 0 ? _c : yield computeJwkThumbprint({ jwk: vm.publicKeyJwk });
methodId = `${methodId.split('#').pop()}`; // Remove fragment prefix, if any.
// Convert public key JWK to ION format.
const publicKey = {
id: methodId,
publicKeyJwk: DidIonUtils.normalizeJwk(vm.publicKeyJwk),
purposes: vm.purposes,
type: 'JsonWebKey2020'
};
ionPublicKeys.push(publicKey);
}
/**
* STEP 2: Convert service entries, if any, to ION SDK format.
*/
const ionServices = services.map(service => (Object.assign(Object.assign({}, service), { id: `${service.id.split('#').pop()}` // Remove fragment prefix, if any.
})));
/**
* STEP 3: Format as ION document.
*/
const ionDocumentModel = {
publicKeys: ionPublicKeys,
services: ionServices
};
return ionDocumentModel;
});
}
/**
* Normalize the given JWK to include only specific members and in lexicographic order.
*
* @param jwk - The JWK to normalize.
* @returns The normalized JWK.
*/
static normalizeJwk(jwk) {
const keyType = jwk.kty;
let normalizedJwk;
if (keyType === 'EC') {
normalizedJwk = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y };
}
else if (keyType === 'oct') {
normalizedJwk = { k: jwk.k, kty: jwk.kty };
}
else if (keyType === 'OKP') {
normalizedJwk = { crv: jwk.crv, kty: jwk.kty, x: jwk.x };
}
else if (keyType === 'RSA') {
normalizedJwk = { e: jwk.e, kty: jwk.kty, n: jwk.n };
}
else {
throw new Error(`Unsupported key type: ${keyType}`);
}
return normalizedJwk;
}
}
//# sourceMappingURL=did-ion.js.map
File diff suppressed because one or more lines are too long
+298
View File
@@ -0,0 +1,298 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Convert } from '@web5/common';
import { LocalKeyManager } from '@web5/crypto';
import { Did } from '../did.js';
import { DidMethod } from './did-method.js';
import { BearerDid } from '../bearer-did.js';
import { DidError, DidErrorCode } from '../did-error.js';
import { EMPTY_DID_RESOLUTION_RESULT } from '../types/did-resolution.js';
/**
* The `DidJwk` class provides an implementation of the `did:jwk` DID method.
*
* Features:
* - DID Creation: Create new `did:jwk` DIDs.
* - DID Key Management: Instantiate a DID object from an existing verification method key set or
* or a key in a Key Management System (KMS). If supported by the KMS, a DID's
* key can be exported to a portable DID format.
* - DID Resolution: Resolve a `did:jwk` to its corresponding DID Document.
* - Signature Operations: Sign and verify messages using keys associated with a DID.
*
* @remarks
* The `did:jwk` DID method uses a single JSON Web Key (JWK) to generate a DID and does not rely
* on any external system such as a blockchain or centralized database. This characteristic makes
* it suitable for use cases where a assertions about a DID Subject can be self-verifiable by
* third parties.
*
* The DID URI is formed by Base64URL-encoding the JWK and prefixing with `did:jwk:`. The DID
* Document of a `did:jwk` DID contains a single verification method, which is the JWK used
* to generate the DID. The verification method is identified by the key ID `#0`.
*
* @see {@link https://github.com/quartzjer/did-jwk/blob/main/spec.md | DID JWK Specification}
*
* @example
* ```ts
* // DID Creation
* const did = await DidJwk.create();
*
* // DID Creation with a KMS
* const keyManager = new LocalKeyManager();
* const did = await DidJwk.create({ keyManager });
*
* // DID Resolution
* const resolutionResult = await DidJwk.resolve({ did: did.uri });
*
* // Signature Operations
* const signer = await did.getSigner();
* const signature = await signer.sign({ data: new TextEncoder().encode('Message') });
* const isValid = await signer.verify({ data: new TextEncoder().encode('Message'), signature });
*
* // Key Management
*
* // Instantiate a DID object from an existing key in a KMS
* const did = await DidJwk.fromKeyManager({
* didUri: 'did:jwk:eyJrIjoiT0tQIiwidCI6IkV1c2UyNTYifQ',
* keyManager
* });
*
* // Instantiate a DID object from an existing verification method key
* const did = await DidJwk.fromKeys({
* verificationMethods: [{
* publicKeyJwk: {
* kty: 'OKP',
* crv: 'Ed25519',
* x: 'cHs7YMLQ3gCWjkacMURBsnEJBcEsvlsE5DfnsfTNDP4'
* },
* privateKeyJwk: {
* kty: 'OKP',
* crv: 'Ed25519',
* x: 'cHs7YMLQ3gCWjkacMURBsnEJBcEsvlsE5DfnsfTNDP4',
* d: 'bdcGE4KzEaekOwoa-ee3gAm1a991WvNj_Eq3WKyqTnE'
* }
* }]
* });
*
* // Convert a DID object to a portable format
* const portableDid = await DidJwk.toKeys({ did });
*
* // Reconstruct a DID object from a portable format
* const did = await DidJwk.fromKeys(portableDid);
* ```
*/
export class DidJwk extends DidMethod {
/**
* Creates a new DID using the `did:jwk` method formed from a newly generated key.
*
* @remarks
* The DID URI is formed by Base64URL-encoding the JWK and prefixing with `did:jwk:`.
*
* Notes:
* - If no `options` are given, by default a new Ed25519 key will be generated.
* - The `algorithm` and `verificationMethods` options are mutually exclusive. If both are given,
* an error will be thrown.
*
* @example
* ```ts
* // DID Creation
* const did = await DidJwk.create();
*
* // DID Creation with a KMS
* const keyManager = new LocalKeyManager();
* const did = await DidJwk.create({ keyManager });
* ```
*
* @param params - The parameters for the create operation.
* @param params.keyManager - Optionally specify a Key Management System (KMS) used to generate
* keys and sign data.
* @param params.options - Optional parameters that can be specified when creating a new DID.
* @returns A Promise resolving to a {@link BearerDid} object representing the new DID.
*/
static create() {
return __awaiter(this, arguments, void 0, function* ({ keyManager = new LocalKeyManager(), options = {} } = {}) {
// Before processing the create operation, validate DID-method-specific requirements to prevent
// keys from being generated unnecessarily.
var _a, _b, _c, _d;
// Check 1: Validate that `algorithm` or `verificationMethods` options are not both given.
if (options.algorithm && options.verificationMethods) {
throw new Error(`The 'algorithm' and 'verificationMethods' options are mutually exclusive`);
}
// Check 2: If `verificationMethods` is given, it must contain exactly one entry since DID JWK
// only supports a single verification method.
if (options.verificationMethods && options.verificationMethods.length !== 1) {
throw new Error(`The 'verificationMethods' option must contain exactly one entry`);
}
// Default to Ed25519 key generation if an algorithm is not given.
const algorithm = (_d = (_a = options.algorithm) !== null && _a !== void 0 ? _a : (_c = (_b = options.verificationMethods) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.algorithm) !== null && _d !== void 0 ? _d : 'Ed25519';
// Generate a new key using the specified `algorithm`.
const keyUri = yield keyManager.generateKey({ algorithm });
const publicKey = yield keyManager.getPublicKey({ keyUri });
// Compute the DID identifier from the public key by serializing the JWK to a UTF-8 string and
// encoding in Base64URL format.
const identifier = Convert.object(publicKey).toBase64Url();
// Attach the prefix `did:jwk` to form the complete DID URI.
const didUri = `did:${DidJwk.methodName}:${identifier}`;
// Expand the DID URI string to a DID document.
const didResolutionResult = yield DidJwk.resolve(didUri);
const document = didResolutionResult.didDocument;
// Create the BearerDid object from the generated key material.
const did = new BearerDid({
uri: didUri,
document,
metadata: {},
keyManager
});
return did;
});
}
/**
* Given the W3C DID Document of a `did:jwk` DID, return the verification method that will be used
* for signing messages and credentials. If given, the `methodId` parameter is used to select the
* verification method. If not given, the first verification method in the DID Document is used.
*
* Note that for DID JWK, only one verification method can exist so specifying `methodId` could be
* considered redundant or unnecessary. The option is provided for consistency with other DID
* method implementations.
*
* @param params - The parameters for the `getSigningMethod` operation.
* @param params.didDocument - DID Document to get the verification method from.
* @param params.methodId - ID of the verification method to use for signing.
* @returns Verification method to use for signing.
*/
static getSigningMethod(_a) {
return __awaiter(this, arguments, void 0, function* ({ didDocument }) {
var _b;
// Verify the DID method is supported.
const parsedDid = Did.parse(didDocument.id);
if (parsedDid && parsedDid.method !== this.methodName) {
throw new DidError(DidErrorCode.MethodNotSupported, `Method not supported: ${parsedDid.method}`);
}
// Attempt to find the verification method in the DID Document.
const [verificationMethod] = (_b = didDocument.verificationMethod) !== null && _b !== void 0 ? _b : [];
if (!(verificationMethod && verificationMethod.publicKeyJwk)) {
throw new DidError(DidErrorCode.InternalError, 'A verification method intended for signing could not be determined from the DID Document');
}
return verificationMethod;
});
}
/**
* Instantiates a {@link BearerDid} object for the DID JWK method from a given {@link PortableDid}.
*
* This method allows for the creation of a `BearerDid` object using a previously created DID's
* key material, DID document, and metadata.
*
* @remarks
* The `verificationMethod` array of the DID document must contain exactly one key since the
* `did:jwk` method only supports a single verification method.
*
* @example
* ```ts
* // Export an existing BearerDid to PortableDid format.
* const portableDid = await did.export();
* // Reconstruct a BearerDid object from the PortableDid.
* const did = await DidJwk.import({ portableDid });
* ```
*
* @param params - The parameters for the import operation.
* @param params.portableDid - The PortableDid object to import.
* @param params.keyManager - Optionally specify an external Key Management System (KMS) used to
* generate keys and sign data. If not given, a new
* {@link LocalKeyManager} instance will be created and
* used.
* @returns A Promise resolving to a `BearerDid` object representing the DID formed from the provided keys.
* @throws An error if the DID document does not contain exactly one verification method.
*/
static import(_a) {
return __awaiter(this, arguments, void 0, function* ({ portableDid, keyManager = new LocalKeyManager() }) {
// Verify the DID method is supported.
const parsedDid = Did.parse(portableDid.uri);
if ((parsedDid === null || parsedDid === void 0 ? void 0 : parsedDid.method) !== DidJwk.methodName) {
throw new DidError(DidErrorCode.MethodNotSupported, `Method not supported`);
}
// Use the given PortableDid to construct the BearerDid object.
const did = yield BearerDid.import({ portableDid, keyManager });
// Validate that the given DID document contains exactly one verification method.
// Note: The non-undefined assertion is necessary because the type system cannot infer that
// the `verificationMethod` property is defined -- which is checked by `BearerDid.import()`.
if (did.document.verificationMethod.length !== 1) {
throw new DidError(DidErrorCode.InvalidDidDocument, `DID document must contain exactly one verification method`);
}
return did;
});
}
/**
* Resolves a `did:jwk` identifier to a DID Document.
*
* @param didUri - The DID to be resolved.
* @param _options - Optional parameters for resolving the DID. Unused by this DID method.
* @returns A Promise resolving to a {@link DidResolutionResult} object representing the result of the resolution.
*/
static resolve(didUri, _options) {
return __awaiter(this, void 0, void 0, function* () {
// Attempt to parse the DID URI.
const parsedDid = Did.parse(didUri);
// Attempt to decode the Base64URL-encoded JWK.
let publicKey;
try {
publicKey = Convert.base64Url(parsedDid.id).toObject();
}
catch ( /* Consume the error so that a DID resolution error can be returned later. */_a) { /* Consume the error so that a DID resolution error can be returned later. */ }
// If parsing or decoding failed, the DID is invalid.
if (!parsedDid || !publicKey) {
return Object.assign(Object.assign({}, EMPTY_DID_RESOLUTION_RESULT), { didResolutionMetadata: { error: 'invalidDid' } });
}
// If the DID method is not "jwk", return an error.
if (parsedDid.method !== DidJwk.methodName) {
return Object.assign(Object.assign({}, EMPTY_DID_RESOLUTION_RESULT), { didResolutionMetadata: { error: 'methodNotSupported' } });
}
const didDocument = {
'@context': [
'https://www.w3.org/ns/did/v1'
],
id: parsedDid.uri
};
const keyUri = `${didDocument.id}#0`;
// Set the Verification Method property.
didDocument.verificationMethod = [{
id: keyUri,
type: 'JsonWebKey',
controller: didDocument.id,
publicKeyJwk: publicKey
}];
// Set the Verification Relationship properties.
didDocument.authentication = [keyUri];
didDocument.assertionMethod = [keyUri];
didDocument.capabilityInvocation = [keyUri];
didDocument.capabilityDelegation = [keyUri];
didDocument.keyAgreement = [keyUri];
// If the JWK contains a `use` property with the value "sig" then the `keyAgreement` property
// is not included in the DID Document. If the `use` value is "enc" then only the `keyAgreement`
// property is included in the DID Document.
switch (publicKey.use) {
case 'sig': {
delete didDocument.keyAgreement;
break;
}
case 'enc': {
delete didDocument.authentication;
delete didDocument.assertionMethod;
delete didDocument.capabilityInvocation;
delete didDocument.capabilityDelegation;
break;
}
}
return Object.assign(Object.assign({}, EMPTY_DID_RESOLUTION_RESULT), { didDocument });
});
}
}
/**
* Name of the DID method, as defined in the DID JWK specification.
*/
DidJwk.methodName = 'jwk';
//# sourceMappingURL=did-jwk.js.map
@@ -0,0 +1 @@
{"version":3,"file":"did-jwk.js","sourceRoot":"","sources":["../../../src/methods/did-jwk.ts"],"names":[],"mappings":"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"}
+983
View File
@@ -0,0 +1,983 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Multicodec, universalTypeOf } from '@web5/common';
import { X25519, Ed25519, Secp256k1, Secp256r1, LocalKeyManager, } from '@web5/crypto';
import { Did } from '../did.js';
import { DidMethod } from './did-method.js';
import { BearerDid } from '../bearer-did.js';
import { DidError, DidErrorCode } from '../did-error.js';
import { EMPTY_DID_RESOLUTION_RESULT } from '../types/did-resolution.js';
import { getVerificationMethodTypes, keyBytesToMultibaseId, multibaseIdToKeyBytes } from '../utils.js';
/**
* Enumerates the types of keys that can be used in a DID Key document.
*
* The DID Key method supports various cryptographic key types. These key types are essential for
* the creation and management of DIDs and their associated cryptographic operations like signing
* and encryption.
*/
export var DidKeyRegisteredKeyType;
(function (DidKeyRegisteredKeyType) {
/**
* Ed25519: A public-key signature system using the EdDSA (Edwards-curve Digital Signature
* Algorithm) and Curve25519.
*/
DidKeyRegisteredKeyType["Ed25519"] = "Ed25519";
/**
* secp256k1: A cryptographic curve used for digital signatures in a range of decentralized
* systems.
*/
DidKeyRegisteredKeyType["secp256k1"] = "secp256k1";
/**
* secp256r1: Also known as P-256 or prime256v1, this curve is used for cryptographic operations
* and is widely supported in various cryptographic libraries and standards.
*/
DidKeyRegisteredKeyType["secp256r1"] = "secp256r1";
/**
* X25519: A Diffie-Hellman key exchange algorithm using Curve25519.
*/
DidKeyRegisteredKeyType["X25519"] = "X25519";
})(DidKeyRegisteredKeyType || (DidKeyRegisteredKeyType = {}));
/**
* Enumerates the verification method types supported by the DID Key method.
*
* This enum defines the URIs associated with common verification methods used in DID Documents.
* These URIs represent cryptographic suites or key types standardized for use across decentralized
* identifiers (DIDs).
*/
export const DidKeyVerificationMethodType = {
/** Represents an Ed25519 public key used for digital signatures. */
Ed25519VerificationKey2020: 'https://w3id.org/security/suites/ed25519-2020/v1',
/** Represents a JSON Web Key (JWK) used for digital signatures and key agreement protocols. */
JsonWebKey2020: 'https://w3id.org/security/suites/jws-2020/v1',
/** Represents an X25519 public key used for key agreement protocols. */
X25519KeyAgreementKey2020: 'https://w3id.org/security/suites/x25519-2020/v1',
};
/**
* Private helper that maps algorithm identifiers to their corresponding DID Key
* {@link DidKeyRegisteredKeyType | registered key type}.
*/
const AlgorithmToKeyTypeMap = {
Ed25519: DidKeyRegisteredKeyType.Ed25519,
ES256K: DidKeyRegisteredKeyType.secp256k1,
ES256: DidKeyRegisteredKeyType.secp256r1,
'P-256': DidKeyRegisteredKeyType.secp256r1,
secp256k1: DidKeyRegisteredKeyType.secp256k1,
secp256r1: DidKeyRegisteredKeyType.secp256r1,
X25519: DidKeyRegisteredKeyType.X25519
};
/**
* The `DidKey` class provides an implementation of the 'did:key' DID method.
*
* Features:
* - DID Creation: Create new `did:key` DIDs.
* - DID Key Management: Instantiate a DID object from an existing verification method key set or
* or a key in a Key Management System (KMS). If supported by the KMS, a DID's
* key can be exported to a portable DID format.
* - DID Resolution: Resolve a `did:key` to its corresponding DID Document.
* - Signature Operations: Sign and verify messages using keys associated with a DID.
*
* @remarks
* The `did:key` DID method uses a single public key to generate a DID and does not rely
* on any external system such as a blockchain or centralized database. This characteristic makes
* it suitable for use cases where a assertions about a DID Subject can be self-verifiable by
* third parties.
*
* The method-specific identifier is formed by
* {@link https://datatracker.ietf.org/doc/html/draft-multiformats-multibase#name-base-58-bitcoin-encoding | Multibase base58-btc}
* encoding the concatenation of the
* {@link https://github.com/multiformats/multicodec/blob/master/README.md | Multicodec} identifier
* for the public key type and the raw public key bytes. To form the DID URI, the method-specific
* identifier is prefixed with the string 'did:key:'.
*
* This method can optionally derive an encryption key from the public key used to create the DID
* if and only if the public key algorithm is `Ed25519`. This feature enables the same DID to be
* used for encrypted communication, in addition to signature verification. To enable this
* feature when calling {@link DidKey.create | `DidKey.create()`}, first specify an `algorithm` of
* `Ed25519` or provide a `keySet` referencing an `Ed25519` key and then set the
* `enableEncryptionKeyDerivation` option to `true`.
*
* Note:
* - The authors of the DID Key specification have indicated that use of this method for long-lived
* use cases is only recommended when accompanied with high confidence that private keys are
* securely protected by software or hardware isolation.
*
* @see {@link https://w3c-ccg.github.io/did-method-key/ | DID Key Specification}
*
* @example
* ```ts
* // DID Creation
* const did = await DidKey.create();
*
* // DID Creation with a KMS
* const keyManager = new LocalKeyManager();
* const did = await DidKey.create({ keyManager });
*
* // DID Resolution
* const resolutionResult = await DidKey.resolve({ did: did.uri });
*
* // Signature Operations
* const signer = await did.getSigner();
* const signature = await signer.sign({ data: new TextEncoder().encode('Message') });
* const isValid = await signer.verify({ data: new TextEncoder().encode('Message'), signature });
*
* // Key Management
*
* // Instantiate a DID object from an existing key in a KMS
* const did = await DidKey.fromKeyManager({
* didUri: 'did:key:z6MkpUzNmYVTGpqhStxK8yRKXWCRNm1bGYz8geAg2zmjYHKX',
* keyManager
* });
*
* // Instantiate a DID object from an existing verification method key
* const did = await DidKey.fromKeys({
* verificationMethods: [{
* publicKeyJwk: {
* kty: 'OKP',
* crv: 'Ed25519',
* x: 'cHs7YMLQ3gCWjkacMURBsnEJBcEsvlsE5DfnsfTNDP4'
* },
* privateKeyJwk: {
* kty: 'OKP',
* crv: 'Ed25519',
* x: 'cHs7YMLQ3gCWjkacMURBsnEJBcEsvlsE5DfnsfTNDP4',
* d: 'bdcGE4KzEaekOwoa-ee3gAm1a991WvNj_Eq3WKyqTnE'
* }
* }]
* });
*
* // Convert a DID object to a portable format
* const portableDid = await DidKey.toKeys({ did });
*
* // Reconstruct a DID object from a portable format
* const did = await DidKey.fromKeys(portableDid);
* ```
*/
export class DidKey extends DidMethod {
/**
* Creates a new DID using the `did:key` method formed from a newly generated key.
*
* @remarks
* The DID URI is formed by
* {@link https://datatracker.ietf.org/doc/html/draft-multiformats-multibase#name-base-58-bitcoin-encoding | Multibase base58-btc}
* encoding the
* {@link https://github.com/multiformats/multicodec/blob/master/README.md | Multicodec}-encoded
* public key and prefixing with `did:key:`.
*
* This method can optionally derive an encryption key from the public key used to create the DID
* if and only if the public key algorithm is `Ed25519`. This feature enables the same DID to be
* used for encrypted communication, in addition to signature verification. To enable this
* feature, specify an `algorithm` of `Ed25519` as either a top-level option or in a
* `verificationMethod` and set the `enableEncryptionKeyDerivation` option to `true`.
*
* Notes:
* - If no `options` are given, by default a new Ed25519 key will be generated.
* - The `algorithm` and `verificationMethods` options are mutually exclusive. If both are given,
* an error will be thrown.
*
* @example
* ```ts
* // DID Creation
* const did = await DidKey.create();
*
* // DID Creation with a KMS
* const keyManager = new LocalKeyManager();
* const did = await DidKey.create({ keyManager });
* ```
*
* @param params - The parameters for the create operation.
* @param params.keyManager - Key Management System (KMS) used to generate keys and sign data.
* @param params.options - Optional parameters that can be specified when creating a new DID.
* @returns A Promise resolving to a {@link BearerDid} object representing the new DID.
*/
static create() {
return __awaiter(this, arguments, void 0, function* ({ keyManager = new LocalKeyManager(), options = {} } = {}) {
// Before processing the create operation, validate DID-method-specific requirements to prevent
// keys from being generated unnecessarily.
var _a, _b, _c, _d;
// Check 1: Validate that `algorithm` or `verificationMethods` options are not both given.
if (options.algorithm && options.verificationMethods) {
throw new Error(`The 'algorithm' and 'verificationMethods' options are mutually exclusive`);
}
// Check 2: If `verificationMethods` is given, it must contain exactly one entry since DID Key
// only supports a single verification method.
if (options.verificationMethods && options.verificationMethods.length !== 1) {
throw new Error(`The 'verificationMethods' option must contain exactly one entry`);
}
// Default to Ed25519 key generation if an algorithm is not given.
const algorithm = (_d = (_a = options.algorithm) !== null && _a !== void 0 ? _a : (_c = (_b = options.verificationMethods) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.algorithm) !== null && _d !== void 0 ? _d : 'Ed25519';
// Generate a new key using the specified `algorithm`.
const keyUri = yield keyManager.generateKey({ algorithm });
const publicKey = yield keyManager.getPublicKey({ keyUri });
// Compute the DID identifier from the public key by converting the JWK to a multibase-encoded
// multicodec value.
const identifier = yield DidKeyUtils.publicKeyToMultibaseId({ publicKey });
// Attach the prefix `did:key` to form the complete DID URI.
const didUri = `did:${DidKey.methodName}:${identifier}`;
// Expand the DID URI string to a DID document.
const didResolutionResult = yield DidKey.resolve(didUri, options);
const document = didResolutionResult.didDocument;
// Create the BearerDid object from the generated key material.
const did = new BearerDid({
uri: didUri,
document,
metadata: {},
keyManager
});
return did;
});
}
/**
* Given the W3C DID Document of a `did:key` DID, return the verification method that will be used
* for signing messages and credentials. With DID Key, the first verification method in the
* authentication property in the DID Document is used.
*
* Note that for DID Key, only one verification method intended for signing can exist so
* specifying `methodId` could be considered redundant or unnecessary. The option is provided for
* consistency with other DID method implementations.
*
* @param params - The parameters for the `getSigningMethod` operation.
* @param params.didDocument - DID Document to get the verification method from.
* @param params.methodId - ID of the verification method to use for signing.
* @returns Verification method to use for signing.
*/
static getSigningMethod(_a) {
return __awaiter(this, arguments, void 0, function* ({ didDocument }) {
var _b;
// Verify the DID method is supported.
const parsedDid = Did.parse(didDocument.id);
if (parsedDid && parsedDid.method !== this.methodName) {
throw new DidError(DidErrorCode.MethodNotSupported, `Method not supported: ${parsedDid.method}`);
}
// Attempt to ge the first verification method intended for signing claims.
const [methodId] = didDocument.assertionMethod || [];
const verificationMethod = (_b = didDocument.verificationMethod) === null || _b === void 0 ? void 0 : _b.find(vm => vm.id === methodId);
if (!(verificationMethod && verificationMethod.publicKeyJwk)) {
throw new DidError(DidErrorCode.InternalError, 'A verification method intended for signing could not be determined from the DID Document');
}
return verificationMethod;
});
}
/**
* Instantiates a {@link BearerDid} object for the DID Key method from a given {@link PortableDid}.
*
* This method allows for the creation of a `BearerDid` object using a previously created DID's
* key material, DID document, and metadata.
*
* @remarks
* The `verificationMethod` array of the DID document must contain exactly one key since the
* `did:key` method only supports a single verification method.
*
* @example
* ```ts
* // Export an existing BearerDid to PortableDid format.
* const portableDid = await did.export();
* // Reconstruct a BearerDid object from the PortableDid.
* const did = await DidKey.import({ portableDid });
* ```
*
* @param params - The parameters for the import operation.
* @param params.portableDid - The PortableDid object to import.
* @param params.keyManager - Optionally specify an external Key Management System (KMS) used to
* generate keys and sign data. If not given, a new
* {@link LocalKeyManager} instance will be created and
* used.
* @returns A Promise resolving to a `BearerDid` object representing the DID formed from the provided keys.
* @throws An error if the DID document does not contain exactly one verification method.
*/
static import(_a) {
return __awaiter(this, arguments, void 0, function* ({ portableDid, keyManager = new LocalKeyManager() }) {
// Verify the DID method is supported.
const parsedDid = Did.parse(portableDid.uri);
if ((parsedDid === null || parsedDid === void 0 ? void 0 : parsedDid.method) !== DidKey.methodName) {
throw new DidError(DidErrorCode.MethodNotSupported, `Method not supported`);
}
// Use the given PortableDid to construct the BearerDid object.
const did = yield BearerDid.import({ portableDid, keyManager });
// Validate that the given DID document contains exactly one verification method.
// Note: The non-undefined assertion is necessary because the type system cannot infer that
// the `verificationMethod` property is defined -- which is checked by `BearerDid.import()`.
if (did.document.verificationMethod.length !== 1) {
throw new DidError(DidErrorCode.InvalidDidDocument, `DID document must contain exactly one verification method`);
}
return did;
});
}
/**
* Resolves a `did:key` identifier to a DID Document.
*
* @param didUri - The DID to be resolved.
* @param options - Optional parameters for resolving the DID.
* @returns A Promise resolving to a {@link DidResolutionResult} object representing the result of the resolution.
*/
static resolve(didUri, options) {
return __awaiter(this, void 0, void 0, function* () {
try {
// Attempt to expand the DID URI string to a DID document.
const didDocument = yield DidKey.createDocument({ didUri, options });
// If the DID document was created successfully, return it.
return Object.assign(Object.assign({}, EMPTY_DID_RESOLUTION_RESULT), { didDocument });
}
catch (error) {
// Rethrow any unexpected errors that are not a `DidError`.
if (!(error instanceof DidError))
throw new Error(error);
// Return a DID Resolution Result with the appropriate error code.
return Object.assign(Object.assign({}, EMPTY_DID_RESOLUTION_RESULT), { didResolutionMetadata: Object.assign({ error: error.code }, error.message && { errorMessage: error.message }) });
}
});
}
/**
* Expands a did:key identifier to a DID Document.
*
* Reference: https://w3c-ccg.github.io/did-method-key/#document-creation-algorithm
*
* @param options
* @returns - A DID dodcument.
*/
static createDocument(_a) {
return __awaiter(this, arguments, void 0, function* ({ didUri, options = {} }) {
const { defaultContext = 'https://www.w3.org/ns/did/v1', enableEncryptionKeyDerivation = false, enableExperimentalPublicKeyTypes = false, publicKeyFormat = 'JsonWebKey2020' } = options;
/**
* 1. Initialize document to an empty object.
*/
const didDocument = { id: '' };
/**
* 2. Using a colon (:) as the delimiter, split the identifier into its
* components: a scheme, a method, a version, and a multibaseValue.
* If there are only three components set the version to the string
* value 1 and use the last value as the multibaseValue.
*/
const parsedDid = Did.parse(didUri);
if (!parsedDid) {
throw new DidError(DidErrorCode.InvalidDid, `Invalid DID URI: ${didUri}`);
}
const multibaseValue = parsedDid.id;
/**
* 3. Check the validity of the input identifier.
* The scheme MUST be the value did. The method MUST be the value key.
* The version MUST be convertible to a positive integer value. The
* multibaseValue MUST be a string and begin with the letter z. If any
* of these requirements fail, an invalidDid error MUST be raised.
*/
if (parsedDid.method !== DidKey.methodName) {
throw new DidError(DidErrorCode.MethodNotSupported, `Method not supported: ${parsedDid.method}`);
}
if (!DidKey.validateIdentifier(parsedDid)) {
throw new DidError(DidErrorCode.InvalidDid, `Invalid DID URI: ${didUri}`);
}
/**
* 4. Initialize the signatureVerificationMethod to the result of passing
* identifier, multibaseValue, and options to a
* {@link https://w3c-ccg.github.io/did-method-key/#signature-method-creation-algorithm | Signature Method Creation Algorithm}.
*/
const signatureVerificationMethod = yield DidKey.createSignatureMethod({
didUri,
multibaseValue,
options: { enableExperimentalPublicKeyTypes, publicKeyFormat }
});
/**
* 5. Set document.id to identifier. If document.id is not a valid DID,
* an invalidDid error MUST be raised.
*
* Note: Identifier was already confirmed to be valid in Step 3, so
* skipping the redundant validation.
*/
didDocument.id = parsedDid.uri;
/**
* 6. Initialize the verificationMethod property in document to an array
* where the first value is the signatureVerificationMethod.
*/
didDocument.verificationMethod = [signatureVerificationMethod];
/**
* 7. Initialize the authentication, assertionMethod, capabilityInvocation,
* and the capabilityDelegation properties in document to an array where
* the first item is the value of the id property in
* signatureVerificationMethod.
*/
didDocument.authentication = [signatureVerificationMethod.id];
didDocument.assertionMethod = [signatureVerificationMethod.id];
didDocument.capabilityInvocation = [signatureVerificationMethod.id];
didDocument.capabilityDelegation = [signatureVerificationMethod.id];
/**
* 8. If options.enableEncryptionKeyDerivation is set to true:
* Add the encryptionVerificationMethod value to the verificationMethod
* array. Initialize the keyAgreement property in document to an array
* where the first item is the value of the id property in
* encryptionVerificationMethod.
*/
if (enableEncryptionKeyDerivation === true) {
/**
* Although not covered by the did:key method specification, a sensible
* default will be taken to use the 'X25519KeyAgreementKey2020'
* verification method type if the given publicKeyFormat is
* 'Ed25519VerificationKey2020' and 'JsonWebKey2020' otherwise.
*/
const encryptionPublicKeyFormat = (publicKeyFormat === 'Ed25519VerificationKey2020')
? 'X25519KeyAgreementKey2020'
: 'JsonWebKey2020';
/**
* 8.1 Initialize the encryptionVerificationMethod to the result of
* passing identifier, multibaseValue, and options to an
* {@link https://w3c-ccg.github.io/did-method-key/#encryption-method-creation-algorithm | Encryption Method Creation Algorithm}.
*/
const encryptionVerificationMethod = yield this.createEncryptionMethod({
didUri,
multibaseValue,
options: { enableExperimentalPublicKeyTypes, publicKeyFormat: encryptionPublicKeyFormat }
});
/**
* 8.2 Add the encryptionVerificationMethod value to the
* verificationMethod array.
*/
didDocument.verificationMethod.push(encryptionVerificationMethod);
/**
* 8.3. Initialize the keyAgreement property in document to an array
* where the first item is the value of the id property in
* encryptionVerificationMethod.
*/
didDocument.keyAgreement = [encryptionVerificationMethod.id];
}
/**
* 9. Initialize the @context property in document to the result of passing document and options to the Context
* Creation algorithm.
*/
// Set contextArray to an array that is initialized to options.defaultContext.
const contextArray = [defaultContext];
// For every object in every verification relationship listed in document,
// add a string value to the contextArray based on the object type value,
// if it doesn't already exist, according to the following table:
// {@link https://w3c-ccg.github.io/did-method-key/#context-creation-algorithm | Context Type URL}
const verificationMethodTypes = getVerificationMethodTypes({ didDocument });
verificationMethodTypes.forEach((typeName) => {
const typeUrl = DidKeyVerificationMethodType[typeName];
contextArray.push(typeUrl);
});
didDocument['@context'] = contextArray;
/**
* 10. Return document.
*/
return didDocument;
});
}
/**
* Decoding a multibase-encoded multicodec value into a verification method
* that is suitable for verifying that encrypted information will be
* received by the intended recipient.
*/
static createEncryptionMethod(_a) {
return __awaiter(this, arguments, void 0, function* ({ didUri, multibaseValue, options }) {
const { enableExperimentalPublicKeyTypes, publicKeyFormat } = options;
/**
* 1. Initialize verificationMethod to an empty object.
*/
const verificationMethod = { id: '', type: '', controller: '' };
/**
* 2. Set multicodecValue and raw publicKeyBytes to the result of passing multibaseValue and
* options to a Derive Encryption Key algorithm.
*/
const { keyBytes: publicKeyBytes, multicodecCode: multicodecValue, } = yield DidKey.deriveEncryptionKey({ multibaseValue });
/**
* 3. Ensure the proper key length of raw publicKeyBytes based on the multicodecValue table
* provided below:
*
* Multicodec hexadecimal value: 0xec
*
* If the byte length of raw publicKeyBytes does not match the expected public key length for
* the associated multicodecValue, an invalidPublicKeyLength error MUST be raised.
*/
const actualLength = publicKeyBytes.byteLength;
const expectedLength = DidKeyUtils.MULTICODEC_PUBLIC_KEY_LENGTH[multicodecValue];
if (actualLength !== expectedLength) {
throw new DidError(DidErrorCode.InvalidPublicKeyLength, `Expected ${actualLength} bytes. Actual: ${expectedLength}`);
}
/**
* 4. Create the multibaseValue by concatenating the letter 'z' and the
* base58-btc encoding of the concatenation of the multicodecValue and
* the raw publicKeyBytes.
*/
const kemMultibaseValue = keyBytesToMultibaseId({
keyBytes: publicKeyBytes,
multicodecCode: multicodecValue
});
/**
* 5. Set the verificationMethod.id value by concatenating identifier,
* a hash character (#), and the multibaseValue. If verificationMethod.id
* is not a valid DID URL, an invalidDidUrl error MUST be raised.
*/
verificationMethod.id = `${didUri}#${kemMultibaseValue}`;
try {
new URL(verificationMethod.id);
}
catch (error) {
throw new DidError(DidErrorCode.InvalidDidUrl, 'Verification Method ID is not a valid DID URL.');
}
/**
* 6. Set the publicKeyFormat value to the options.publicKeyFormat value.
* 7. If publicKeyFormat is not known to the implementation, an
* unsupportedPublicKeyType error MUST be raised.
*/
if (!(publicKeyFormat in DidKeyVerificationMethodType)) {
throw new DidError(DidErrorCode.UnsupportedPublicKeyType, `Unsupported format: ${publicKeyFormat}`);
}
/**
* 8. If options.enableExperimentalPublicKeyTypes is set to false and publicKeyFormat is not
* Multikey, JsonWebKey2020, or X25519KeyAgreementKey2020, an invalidPublicKeyType error MUST be
* raised.
*/
const StandardPublicKeyTypes = ['Multikey', 'JsonWebKey2020', 'X25519KeyAgreementKey2020'];
if (enableExperimentalPublicKeyTypes === false
&& !(StandardPublicKeyTypes.includes(publicKeyFormat))) {
throw new DidError(DidErrorCode.InvalidPublicKeyType, `Specified '${publicKeyFormat}' without setting enableExperimentalPublicKeyTypes to true.`);
}
/**
* 9. Set verificationMethod.type to the publicKeyFormat value.
*/
verificationMethod.type = publicKeyFormat;
/**
* 10. Set verificationMethod.controller to the identifier value.
*/
verificationMethod.controller = didUri;
/**
* 11. If publicKeyFormat is Multikey or X25519KeyAgreementKey2020, set the verificationMethod.publicKeyMultibase
* value to multibaseValue.
*
* Note: This implementation does not currently support the Multikey
* format.
*/
if (publicKeyFormat === 'X25519KeyAgreementKey2020') {
verificationMethod.publicKeyMultibase = kemMultibaseValue;
}
/**
* 12. If publicKeyFormat is JsonWebKey2020, set the verificationMethod.publicKeyJwk value to
* the result of passing multicodecValue and rawPublicKeyBytes to a JWK encoding algorithm.
*/
if (publicKeyFormat === 'JsonWebKey2020') {
const { crv } = yield DidKeyUtils.multicodecToJwk({ code: multicodecValue });
verificationMethod.publicKeyJwk = yield DidKeyUtils.keyConverter(crv).bytesToPublicKey({ publicKeyBytes });
}
/**
* 13. Return verificationMethod.
*/
return verificationMethod;
});
}
/**
* Decodes a multibase-encoded multicodec value into a verification method
* that is suitable for verifying digital signatures.
* @param options - Signature method creation algorithm inputs.
* @returns - A verification method.
*/
static createSignatureMethod(_a) {
return __awaiter(this, arguments, void 0, function* ({ didUri, multibaseValue, options }) {
const { enableExperimentalPublicKeyTypes, publicKeyFormat } = options;
/**
* 1. Initialize verificationMethod to an empty object.
*/
const verificationMethod = { id: '', type: '', controller: '' };
/**
* 2. Set multicodecValue and publicKeyBytes to the result of passing
* multibaseValue and options to a Decode Public Key algorithm.
*/
const { keyBytes: publicKeyBytes, multicodecCode: multicodecValue, multicodecName } = multibaseIdToKeyBytes({ multibaseKeyId: multibaseValue });
/**
* 3. Ensure the proper key length of publicKeyBytes based on the multicodecValue
* {@link https://w3c-ccg.github.io/did-method-key/#signature-method-creation-algorithm | table provided}.
* If the byte length of rawPublicKeyBytes does not match the expected public key length for the
* associated multicodecValue, an invalidPublicKeyLength error MUST be raised.
*/
const actualLength = publicKeyBytes.byteLength;
const expectedLength = DidKeyUtils.MULTICODEC_PUBLIC_KEY_LENGTH[multicodecValue];
if (actualLength !== expectedLength) {
throw new DidError(DidErrorCode.InvalidPublicKeyLength, `Expected ${actualLength} bytes. Actual: ${expectedLength}`);
}
/**
* 4. Ensure the publicKeyBytes are a proper encoding of the public key type as specified by
* the multicodecValue. If an invalid public key value is detected, an invalidPublicKey error
* MUST be raised.
*/
let isValid = false;
switch (multicodecName) {
case 'secp256k1-pub':
isValid = yield Secp256k1.validatePublicKey({ publicKeyBytes });
break;
case 'ed25519-pub':
isValid = yield Ed25519.validatePublicKey({ publicKeyBytes });
break;
case 'x25519-pub':
// TODO: Validate key once/if X25519.validatePublicKey() is implemented.
// isValid = X25519.validatePublicKey({ key: rawPublicKeyBytes})
isValid = true;
break;
}
if (!isValid) {
throw new DidError(DidErrorCode.InvalidPublicKey, 'Invalid public key detected.');
}
/**
* 5. Set the verificationMethod.id value by concatenating identifier, a hash character (#), and
* the multibaseValue. If verificationMethod.id is not a valid DID URL, an invalidDidUrl error
* MUST be raised.
*/
verificationMethod.id = `${didUri}#${multibaseValue}`;
try {
new URL(verificationMethod.id);
}
catch (error) {
throw new DidError(DidErrorCode.InvalidDidUrl, 'Verification Method ID is not a valid DID URL.');
}
/**
* 6. Set the publicKeyFormat value to the options.publicKeyFormat value.
* 7. If publicKeyFormat is not known to the implementation, an unsupportedPublicKeyType error
* MUST be raised.
*/
if (!(publicKeyFormat in DidKeyVerificationMethodType)) {
throw new DidError(DidErrorCode.UnsupportedPublicKeyType, `Unsupported format: ${publicKeyFormat}`);
}
/**
* 8. If options.enableExperimentalPublicKeyTypes is set to false and publicKeyFormat is not
* Multikey, JsonWebKey2020, or Ed25519VerificationKey2020, an invalidPublicKeyType error MUST
* be raised.
*/
const StandardPublicKeyTypes = ['Multikey', 'JsonWebKey2020', 'Ed25519VerificationKey2020'];
if (enableExperimentalPublicKeyTypes === false
&& !(StandardPublicKeyTypes.includes(publicKeyFormat))) {
throw new DidError(DidErrorCode.InvalidPublicKeyType, `Specified '${publicKeyFormat}' without setting enableExperimentalPublicKeyTypes to true.`);
}
/**
* 9. Set verificationMethod.type to the publicKeyFormat value.
*/
verificationMethod.type = publicKeyFormat;
/**
* 10. Set verificationMethod.controller to the identifier value.
*/
verificationMethod.controller = didUri;
/**
* 11. If publicKeyFormat is Multikey or Ed25519VerificationKey2020,
* set the verificationMethod.publicKeyMultibase value to multibaseValue.
*
* Note: This implementation does not currently support the Multikey
* format.
*/
if (publicKeyFormat === 'Ed25519VerificationKey2020') {
verificationMethod.publicKeyMultibase = multibaseValue;
}
/**
* 12. If publicKeyFormat is JsonWebKey2020, set the verificationMethod.publicKeyJwk value to
* the result of passing multicodecValue and rawPublicKeyBytes to a JWK encoding algorithm.
*/
if (publicKeyFormat === 'JsonWebKey2020') {
const { crv } = yield DidKeyUtils.multicodecToJwk({ code: multicodecValue });
verificationMethod.publicKeyJwk = yield DidKeyUtils.keyConverter(crv).bytesToPublicKey({ publicKeyBytes });
}
/**
* 13. Return verificationMethod.
*/
return verificationMethod;
});
}
/**
* Transform a multibase-encoded multicodec value to public encryption key
* components that are suitable for encrypting messages to a receiver. A
* mathematical proof elaborating on the safety of performing this operation
* is available in:
* {@link https://eprint.iacr.org/2021/509.pdf | On using the same key pair for Ed25519 and an X25519 based KEM}
*/
static deriveEncryptionKey(_a) {
return __awaiter(this, arguments, void 0, function* ({ multibaseValue }) {
/**
* 1. Set publicEncryptionKey to an empty object.
*/
let publicEncryptionKey = {
keyBytes: new Uint8Array(),
multicodecCode: 0
};
/**
* 2. Decode multibaseValue using the base58-btc multibase alphabet and
* set multicodecValue to the multicodec header for the decoded value.
* Implementers are cautioned to ensure that the multicodecValue is set
* to the result after performing varint decoding.
*
* 3. Set the rawPublicKeyBytes to the bytes remaining after the multicodec
* header.
*/
const { keyBytes: publicKeyBytes, multicodecCode: multicodecValue } = multibaseIdToKeyBytes({ multibaseKeyId: multibaseValue });
/**
* 4. If the multicodecValue is 0xed (Ed25519 public key), derive a public X25519 encryption key
* by using the raw publicKeyBytes and the algorithm defined in
* {@link https://datatracker.ietf.org/doc/html/draft-ietf-core-oscore-groupcomm | Group OSCORE - Secure Group Communication for CoAP}
* for Curve25519 in Section 2.4.2: ECDH with Montgomery Coordinates and set
* generatedPublicEncryptionKeyBytes to the result.
*/
if (multicodecValue === 0xed) {
const ed25519PublicKey = yield DidKeyUtils.keyConverter('Ed25519').bytesToPublicKey({
publicKeyBytes
});
const generatedPublicEncryptionKey = yield Ed25519.convertPublicKeyToX25519({
publicKey: ed25519PublicKey
});
const generatedPublicEncryptionKeyBytes = yield DidKeyUtils.keyConverter('Ed25519').publicKeyToBytes({
publicKey: generatedPublicEncryptionKey
});
/**
* 5. Set multicodecValue to 0xec.
* 6. Set raw public keyBytes to generatedPublicEncryptionKeyBytes.
*/
publicEncryptionKey = {
keyBytes: generatedPublicEncryptionKeyBytes,
multicodecCode: 0xec
};
}
/**
* 7. Return publicEncryptionKey.
*/
return publicEncryptionKey;
});
}
/**
* Validates the structure and components of a DID URI against the `did:key` method specification.
*
* @param parsedDid - An object representing the parsed components of a DID URI, including the
* scheme, method, and method-specific identifier.
* @returns `true` if the DID URI meets the `did:key` method's structural requirements, `false` otherwise.
*
*/
static validateIdentifier(parsedDid) {
const { method, id: multibaseValue } = parsedDid;
const [scheme] = parsedDid.uri.split(':', 1);
/**
* Note: The W3C DID specification makes no mention of a version value being part of the DID
* syntax. Additionally, there does not appear to be any real-world usage of the version
* number. Consequently, this implementation will ignore the version related guidance in
* the did:key specification.
*/
const version = '1';
return (scheme === 'did' &&
method === 'key' &&
Number(version) > 0 &&
universalTypeOf(multibaseValue) === 'String' &&
multibaseValue.startsWith('z'));
}
}
/**
* Name of the DID method, as defined in the DID Key specification.
*/
DidKey.methodName = 'key';
/**
* The `DidKeyUtils` class provides utility functions to support operations in the DID Key method.
*/
export class DidKeyUtils {
/**
* Converts a JWK (JSON Web Key) to a Multicodec code and name.
*
* @example
* ```ts
* const jwk: Jwk = { crv: 'Ed25519', kty: 'OKP', x: '...' };
* const { code, name } = await DidKeyUtils.jwkToMulticodec({ jwk });
* ```
*
* @param params - The parameters for the conversion.
* @param params.jwk - The JSON Web Key to be converted.
* @returns A promise that resolves to a Multicodec definition.
*/
static jwkToMulticodec(_a) {
return __awaiter(this, arguments, void 0, function* ({ jwk }) {
const params = [];
if (jwk.crv) {
params.push(jwk.crv);
if (jwk.d) {
params.push('private');
}
else {
params.push('public');
}
}
const lookupKey = params.join(':');
const name = DidKeyUtils.JWK_TO_MULTICODEC[lookupKey];
if (name === undefined) {
throw new Error(`Unsupported JWK to Multicodec conversion: '${lookupKey}'`);
}
const code = Multicodec.getCodeFromName({ name });
return { code, name };
});
}
/**
* Returns the appropriate public key compressor for the specified cryptographic curve.
*
* @param curve - The cryptographic curve to use for the key conversion.
* @returns A public key compressor for the specified curve.
*/
static keyCompressor(curve) {
// ): ({ publicKeyBytes }: { publicKeyBytes: Uint8Array }) => Promise<Uint8Array> {
const compressors = {
'P-256': Secp256r1.compressPublicKey,
'secp256k1': Secp256k1.compressPublicKey
};
const compressor = compressors[curve];
if (!compressor)
throw new DidError(DidErrorCode.InvalidPublicKeyType, `Unsupported curve: ${curve}`);
return compressor;
}
/**
* Returns the appropriate key converter for the specified cryptographic curve.
*
* @param curve - The cryptographic curve to use for the key conversion.
* @returns An `AsymmetricKeyConverter` for the specified curve.
*/
static keyConverter(curve) {
const converters = {
'Ed25519': Ed25519,
'P-256': Secp256r1,
'secp256k1': Secp256k1,
'X25519': X25519
};
const converter = converters[curve];
if (!converter)
throw new DidError(DidErrorCode.InvalidPublicKeyType, `Unsupported curve: ${curve}`);
return converter;
}
/**
* Converts a Multicodec code or name to parial JWK (JSON Web Key).
*
* @example
* ```ts
* const partialJwk = await DidKeyUtils.multicodecToJwk({ name: 'ed25519-pub' });
* ```
*
* @param params - The parameters for the conversion.
* @param params.code - Optional Multicodec code to convert.
* @param params.name - Optional Multicodec name to convert.
* @returns A promise that resolves to a JOSE format key.
*/
static multicodecToJwk(_a) {
return __awaiter(this, arguments, void 0, function* ({ code, name }) {
// Either code or name must be specified, but not both.
if (!(name ? !code : code)) {
throw new Error(`Either 'name' or 'code' must be defined, but not both.`);
}
// If name is undefined, lookup by code.
name = (name === undefined) ? Multicodec.getNameFromCode({ code: code }) : name;
const lookupKey = name;
const jose = DidKeyUtils.MULTICODEC_TO_JWK[lookupKey];
if (jose === undefined) {
throw new Error(`Unsupported Multicodec to JWK conversion`);
}
return Object.assign({}, jose);
});
}
/**
* Converts a public key in JWK (JSON Web Key) format to a multibase identifier.
*
* @remarks
* Note: All secp public keys are converted to compressed point encoding
* before the multibase identifier is computed.
*
* Per {@link https://github.com/multiformats/multicodec/blob/master/table.csv | Multicodec table}:
* Public keys for Elliptic Curve cryptography algorithms (e.g., secp256k1,
* secp256k1r1, secp384r1, etc.) are always represented with compressed point
* encoding (e.g., secp256k1-pub, p256-pub, p384-pub, etc.).
*
* Per {@link https://datatracker.ietf.org/doc/html/rfc8812#name-jose-and-cose-secp256k1-cur | RFC 8812}:
* "As a compressed point encoding representation is not defined for JWK
* elliptic curve points, the uncompressed point encoding defined there
* MUST be used. The x and y values represented MUST both be exactly
* 256 bits, with any leading zeros preserved."
*
* @example
* ```ts
* const publicKey = { crv: 'Ed25519', kty: 'OKP', x: '...' };
* const multibaseId = await DidKeyUtils.publicKeyToMultibaseId({ publicKey });
* ```
*
* @param params - The parameters for the conversion.
* @param params.publicKey - The public key in JWK format.
* @returns A promise that resolves to the multibase identifier.
*/
static publicKeyToMultibaseId(_a) {
return __awaiter(this, arguments, void 0, function* ({ publicKey }) {
var _b;
if (!((publicKey === null || publicKey === void 0 ? void 0 : publicKey.crv) && publicKey.crv in AlgorithmToKeyTypeMap)) {
throw new DidError(DidErrorCode.InvalidPublicKeyType, `Public key contains an unsupported key type: ${(_b = publicKey === null || publicKey === void 0 ? void 0 : publicKey.crv) !== null && _b !== void 0 ? _b : 'undefined'}`);
}
// Convert the public key from JWK format to a byte array.
let publicKeyBytes = yield DidKeyUtils.keyConverter(publicKey.crv).publicKeyToBytes({ publicKey });
// Compress the public key if it is an elliptic curve key.
if (/^(secp256k1|P-256|P-384|P-521)$/.test(publicKey.crv)) {
publicKeyBytes = yield DidKeyUtils.keyCompressor(publicKey.crv)({ publicKeyBytes });
}
// Convert the JSON Web Key (JWK) parameters to a Multicodec name.
const { name: multicodecName } = yield DidKeyUtils.jwkToMulticodec({ jwk: publicKey });
// Compute the multibase identifier based on the provided key.
const multibaseId = keyBytesToMultibaseId({
keyBytes: publicKeyBytes,
multicodecName
});
return multibaseId;
});
}
}
/**
* A mapping from JSON Web Key (JWK) property descriptors to multicodec names.
*
* This mapping is used to convert keys in JWK (JSON Web Key) format to multicodec format.
*
* @remarks
* The keys of this object are strings that describe the JOSE key type and usage,
* such as 'Ed25519:public', 'Ed25519:private', etc. The values are the corresponding multicodec
* names used to represent these key types.
*
* @example
* ```ts
* const multicodecName = JWK_TO_MULTICODEC['Ed25519:public'];
* // Returns 'ed25519-pub', the multicodec name for an Ed25519 public key
* ```
*/
DidKeyUtils.JWK_TO_MULTICODEC = {
'Ed25519:public': 'ed25519-pub',
'Ed25519:private': 'ed25519-priv',
'secp256k1:public': 'secp256k1-pub',
'secp256k1:private': 'secp256k1-priv',
'X25519:public': 'x25519-pub',
'X25519:private': 'x25519-priv',
};
/**
* Defines the expected byte lengths for public keys associated with different cryptographic
* algorithms, indexed by their multicodec code values.
*/
DidKeyUtils.MULTICODEC_PUBLIC_KEY_LENGTH = {
// secp256k1-pub - Secp256k1 public key (compressed) - 33 bytes
0xe7: 33,
// x25519-pub - Curve25519 public key - 32 bytes
0xec: 32,
// ed25519-pub - Ed25519 public key - 32 bytes
0xed: 32
};
/**
* A mapping from multicodec names to their corresponding JOSE (JSON Object Signing and Encryption)
* representations. This mapping facilitates the conversion of multicodec key formats to
* JWK (JSON Web Key) formats.
*
* @remarks
* The keys of this object are multicodec names, such as 'ed25519-pub', 'ed25519-priv', etc.
* The values are objects representing the corresponding JWK properties for that key type.
*
* @example
* ```ts
* const joseKey = MULTICODEC_TO_JWK['ed25519-pub'];
* // Returns a partial JWK for an Ed25519 public key
* ```
*/
DidKeyUtils.MULTICODEC_TO_JWK = {
'ed25519-pub': { crv: 'Ed25519', kty: 'OKP', x: '' },
'ed25519-priv': { crv: 'Ed25519', kty: 'OKP', x: '', d: '' },
'secp256k1-pub': { crv: 'secp256k1', kty: 'EC', x: '', y: '' },
'secp256k1-priv': { crv: 'secp256k1', kty: 'EC', x: '', y: '', d: '' },
'x25519-pub': { crv: 'X25519', kty: 'OKP', x: '' },
'x25519-priv': { crv: 'X25519', kty: 'OKP', x: '', d: '' },
};
//# sourceMappingURL=did-key.js.map
File diff suppressed because one or more lines are too long
+53
View File
@@ -0,0 +1,53 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
/**
* Base abstraction for all Decentralized Identifier (DID) method implementations.
*
* This base class serves as a foundational structure upon which specific DID methods
* can be implemented. Subclasses should furnish particular method and data models adherent
* to various DID methods, taking care to adhere to the
* {@link https://www.w3.org/TR/did-core/ | W3C DID Core specification} and the
* respective DID method specifications.
*/
export class DidMethod {
/**
* MUST be implemented by all DID method implementations that extend {@link DidMethod}.
*
* Given the W3C DID Document of a DID, return the verification method that will be used for
* signing messages and credentials. If given, the `methodId` parameter is used to select the
* verification method. If not given, each DID method implementation will select a default
* verification method from the DID Document.
*
* @param _params - The parameters for the `getSigningMethod` operation.
* @param _params.didDocument - DID Document to get the verification method from.
* @param _params.methodId - ID of the verification method to use for signing.
* @returns Verification method to use for signing.
*/
static getSigningMethod(_params) {
return __awaiter(this, void 0, void 0, function* () {
throw new Error(`Not implemented: Classes extending DidMethod must implement getSigningMethod()`);
});
}
/**
* MUST be implemented by all DID method implementations that extend {@link DidMethod}.
*
* Resolves a DID URI to a DID Document.
*
* @param _didUri - The DID to be resolved.
* @param _options - Optional parameters for resolving the DID.
* @returns A Promise resolving to a {@link DidResolutionResult} object representing the result of the resolution.
*/
static resolve(_didUri, _options) {
return __awaiter(this, void 0, void 0, function* () {
throw new Error(`Not implemented: Classes extending DidMethod must implement resolve()`);
});
}
}
//# sourceMappingURL=did-method.js.map
@@ -0,0 +1 @@
{"version":3,"file":"did-method.js","sourceRoot":"","sources":["../../../src/methods/did-method.ts"],"names":[],"mappings":";;;;;;;;;AAyOA;;;;;;;;GAQG;AACH,MAAM,OAAO,SAAS;IACpB;;;;;;;;;;;;OAYG;IACI,MAAM,CAAO,gBAAgB,CAAC,OAGpC;;YACC,MAAM,IAAI,KAAK,CAAC,gFAAgF,CAAC,CAAC;QACpG,CAAC;KAAA;IAED;;;;;;;;OAQG;IACI,MAAM,CAAO,OAAO,CAAC,OAAe,EAAE,QAA+B;;YAC1E,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAC;QAC3F,CAAC;KAAA;CACF"}
+83
View File
@@ -0,0 +1,83 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Did } from '../did.js';
import { DidMethod } from './did-method.js';
import { EMPTY_DID_RESOLUTION_RESULT } from '../types/did-resolution.js';
/**
* The `DidWeb` class provides an implementation of the `did:web` DID method.
*
* Features:
* - DID Resolution: Resolve a `did:web` to its corresponding DID Document.
*
* @remarks
* The `did:web` method uses a web domain's existing reputation and aims to integrate decentralized
* identities with the existing web infrastructure to drive adoption. It leverages familiar web
* security models and domain ownership to provide accessible, interoperable digital identity
* management.
*
* @see {@link https://w3c-ccg.github.io/did-method-web/ | DID Web Specification}
*
* @example
* ```ts
* // DID Resolution
* const resolutionResult = await DidWeb.resolve({ did: did.uri });
* ```
*/
export class DidWeb extends DidMethod {
/**
* Resolves a `did:web` identifier to a DID Document.
*
* @param didUri - The DID to be resolved.
* @param _options - Optional parameters for resolving the DID. Unused by this DID method.
* @returns A Promise resolving to a {@link DidResolutionResult} object representing the result of the resolution.
*/
static resolve(didUri, _options) {
return __awaiter(this, void 0, void 0, function* () {
// Attempt to parse the DID URI.
const parsedDid = Did.parse(didUri);
// If parsing failed, the DID is invalid.
if (!parsedDid) {
return Object.assign(Object.assign({}, EMPTY_DID_RESOLUTION_RESULT), { didResolutionMetadata: { error: 'invalidDid' } });
}
// If the DID method is not "web", return an error.
if (parsedDid.method !== DidWeb.methodName) {
return Object.assign(Object.assign({}, EMPTY_DID_RESOLUTION_RESULT), { didResolutionMetadata: { error: 'methodNotSupported' } });
}
// Replace ":" with "/" in the identifier and prepend "https://" to obtain the fully qualified
// domain name and optional path.
let baseUrl = `https://${parsedDid.id.replace(/:/g, '/')}`;
// If the domain contains a percent encoded port value, decode the colon.
baseUrl = decodeURIComponent(baseUrl);
// Append the expected location of the DID document depending on whether a path was specified.
const didDocumentUrl = parsedDid.id.includes(':') ?
`${baseUrl}/did.json` :
`${baseUrl}/.well-known/did.json`;
try {
// Perform an HTTP GET request to obtain the DID document.
const response = yield fetch(didDocumentUrl);
// If the response status code is not 200, return an error.
if (!response.ok)
throw new Error('HTTP error status code returned');
// Parse the DID document.
const didDocument = yield response.json();
return Object.assign(Object.assign({}, EMPTY_DID_RESOLUTION_RESULT), { didDocument });
}
catch (error) {
// If the DID document could not be retrieved, return an error.
return Object.assign(Object.assign({}, EMPTY_DID_RESOLUTION_RESULT), { didResolutionMetadata: { error: 'notFound' } });
}
});
}
}
/**
* Name of the DID method, as defined in the DID Web specification.
*/
DidWeb.methodName = 'web';
//# sourceMappingURL=did-web.js.map
@@ -0,0 +1 @@
{"version":3,"file":"did-web.js","sourceRoot":"","sources":["../../../src/methods/did-web.ts"],"names":[],"mappings":";;;;;;;;;AAEA,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,MAAO,SAAQ,SAAS;IAOnC;;;;;;OAMG;IACI,MAAM,CAAO,OAAO,CAAC,MAAc,EAAE,QAA+B;;YACzE,gCAAgC;YAChC,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAEpC,yCAAyC;YACzC,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,uCACK,2BAA2B,KAC9B,qBAAqB,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,IAC9C;YACJ,CAAC;YAED,mDAAmD;YACnD,IAAI,SAAS,CAAC,MAAM,KAAK,MAAM,CAAC,UAAU,EAAE,CAAC;gBAC3C,uCACK,2BAA2B,KAC9B,qBAAqB,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE,IACtD;YACJ,CAAC;YAED,8FAA8F;YAC9F,iCAAiC;YACjC,IAAI,OAAO,GAAG,WAAW,SAAS,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;YAE3D,yEAAyE;YACzE,OAAO,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;YAEtC,8FAA8F;YAC9F,MAAM,cAAc,GAAG,SAAS,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;gBACjD,GAAG,OAAO,WAAW,CAAC,CAAC;gBACvB,GAAG,OAAO,uBAAuB,CAAC;YAEpC,IAAI,CAAC;gBACH,0DAA0D;gBAC1D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,cAAc,CAAC,CAAC;gBAE7C,2DAA2D;gBAC3D,IAAI,CAAC,QAAQ,CAAC,EAAE;oBAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;gBAErE,0BAA0B;gBAC1B,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAiB,CAAC;gBAEzD,uCACK,2BAA2B,KAC9B,WAAW,IACX;YAEJ,CAAC;YAAC,OAAO,KAAU,EAAE,CAAC;gBACpB,+DAA+D;gBAC/D,uCACK,2BAA2B,KAC9B,qBAAqB,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,IAC5C;YACJ,CAAC;QACH,CAAC;KAAA;;AAlED;;GAEG;AACW,iBAAU,GAAG,KAAK,CAAC"}
@@ -0,0 +1,101 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import ms from 'ms';
import { Level } from 'level';
/**
* A Level-based cache implementation for storing and retrieving DID resolution results.
*
* This cache uses LevelDB for storage, allowing data persistence across process restarts or
* browser refreshes. It's suitable for both Node.js and browser environments.
*
* @remarks
* The LevelDB cache keeps data in memory for fast access and also writes to the filesystem in
* Node.js or indexedDB in browsers. Time-to-live (TTL) for cache entries is configurable.
*
* @example
* ```
* const cache = new DidResolverCacheLevel({ ttl: '15m' });
* ```
*/
export class DidResolverCacheLevel {
constructor({ db, location = 'DATA/DID_RESOLVERCACHE', ttl = '15m' } = {}) {
this.cache = db !== null && db !== void 0 ? db : new Level(location);
this.ttl = ms(ttl);
}
/**
* Retrieves a DID resolution result from the cache.
*
* If the cached item has exceeded its TTL, it's scheduled for deletion and undefined is returned.
*
* @param did - The DID string used as the key for retrieving the cached result.
* @returns The cached DID resolution result or undefined if not found or expired.
*/
get(did) {
return __awaiter(this, void 0, void 0, function* () {
try {
const str = yield this.cache.get(did);
const cachedDidResolutionResult = JSON.parse(str);
if (Date.now() >= cachedDidResolutionResult.ttlMillis) {
// defer deletion to be called in the next tick of the js event loop
this.cache.nextTick(() => this.cache.del(did));
return;
}
else {
return cachedDidResolutionResult.value;
}
}
catch (error) {
// Don't throw when a key wasn't found.
if (error.notFound) {
return;
}
throw error;
}
});
}
/**
* Stores a DID resolution result in the cache with a TTL.
*
* @param did - The DID string used as the key for storing the result.
* @param value - The DID resolution result to be cached.
* @returns A promise that resolves when the operation is complete.
*/
set(did, value) {
const cachedDidResolutionResult = { ttlMillis: Date.now() + this.ttl, value };
const str = JSON.stringify(cachedDidResolutionResult);
return this.cache.put(did, str);
}
/**
* Deletes a DID resolution result from the cache.
*
* @param did - The DID string used as the key for deletion.
* @returns A promise that resolves when the operation is complete.
*/
delete(did) {
return this.cache.del(did);
}
/**
* Clears all entries from the cache.
*
* @returns A promise that resolves when the operation is complete.
*/
clear() {
return this.cache.clear();
}
/**
* Closes the underlying LevelDB store.
*
* @returns A promise that resolves when the store is closed.
*/
close() {
return this.cache.close();
}
}
//# sourceMappingURL=resolver-cache-level.js.map
@@ -0,0 +1 @@
{"version":3,"file":"resolver-cache-level.js","sourceRoot":"","sources":["../../../src/resolver/resolver-cache-level.ts"],"names":[],"mappings":";;;;;;;;;AAEA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAuD9B;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,qBAAqB;IAOhC,YAAY,EACV,EAAE,EACF,QAAQ,GAAG,wBAAwB,EACnC,GAAG,GAAG,KAAK,KACoB,EAAE;QACjC,IAAI,CAAC,KAAK,GAAG,EAAE,aAAF,EAAE,cAAF,EAAE,GAAI,IAAI,KAAK,CAAiB,QAAQ,CAAC,CAAC;QACvD,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAED;;;;;;;OAOG;IACG,GAAG,CAAC,GAAW;;YACnB,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACtC,MAAM,yBAAyB,GAA8B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAE7E,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,yBAAyB,CAAC,SAAS,EAAE,CAAC;oBACtD,oEAAoE;oBACpE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;oBAE/C,OAAO;gBACT,CAAC;qBAAM,CAAC;oBACN,OAAO,yBAAyB,CAAC,KAAK,CAAC;gBACzC,CAAC;YAEH,CAAC;YAAC,OAAM,KAAU,EAAE,CAAC;gBACnB,uCAAuC;gBACvC,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;oBACnB,OAAO;gBACT,CAAC;gBAED,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;KAAA;IAED;;;;;;OAMG;IACH,GAAG,CAAC,GAAW,EAAE,KAA0B;QACzC,MAAM,yBAAyB,GAA8B,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC;QACzG,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;QAEtD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAClC,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,GAAW;QAChB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED;;;;OAIG;IACH,KAAK;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACH,KAAK;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC;CACF"}
@@ -0,0 +1,24 @@
/**
* No-op cache that is used as the default cache for did-resolver.
*
* The motivation behind using a no-op cache as the default stems from the desire to maximize the
* potential for this library to be used in as many JS runtimes as possible.
*/
export const DidResolverCacheNoop = {
get: function (_key) {
return null;
},
set: function (_key, _value) {
return null;
},
delete: function (_key) {
return null;
},
clear: function () {
return null;
},
close: function () {
return null;
}
};
//# sourceMappingURL=resolver-cache-noop.js.map
@@ -0,0 +1 @@
{"version":3,"file":"resolver-cache-noop.js","sourceRoot":"","sources":["../../../src/resolver/resolver-cache-noop.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAqB;IACpD,GAAG,EAAE,UAAU,IAAY;QACzB,OAAO,IAAW,CAAC;IACrB,CAAC;IACD,GAAG,EAAE,UAAU,IAAY,EAAE,MAA2B;QACtD,OAAO,IAAW,CAAC;IACrB,CAAC;IACD,MAAM,EAAE,UAAU,IAAY;QAC5B,OAAO,IAAW,CAAC;IACrB,CAAC;IACD,KAAK,EAAE;QACL,OAAO,IAAW,CAAC;IACrB,CAAC;IACD,KAAK,EAAE;QACL,OAAO,IAAW,CAAC;IACrB,CAAC;CACF,CAAC"}
@@ -0,0 +1,187 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Did } from '../did.js';
import { DidErrorCode } from '../did-error.js';
import { DidResolverCacheNoop } from './resolver-cache-noop.js';
import { EMPTY_DID_RESOLUTION_RESULT } from '../types/did-resolution.js';
/**
* The `DidResolver` class provides mechanisms for resolving Decentralized Identifiers (DIDs) to
* their corresponding DID documents.
*
* The class is designed to handle various DID methods by utilizing an array of `DidMethodResolver`
* instances, each responsible for a specific DID method.
*
* Providing a cache implementation can significantly enhance resolution performance by avoiding
* redundant resolutions for previously resolved DIDs. If omitted, a no-operation cache is used,
* which effectively disables caching.
*
* Usage:
* - Construct the `DidResolver` with an array of `DidMethodResolver` instances and an optional cache.
* - Use `resolve` to resolve a DID to its DID Resolution Result.
* - Use `dereference` to extract specific resources from a DID URL, like service endpoints or verification methods.
*
* @example
* ```ts
* const resolver = new DidResolver({
* didResolvers: [<array of DidMethodResolver instances>],
* cache: new DidResolverCacheNoop()
* });
*
* const resolutionResult = await resolver.resolve('did:example:123456');
* const dereferenceResult = await resolver.dereference({ didUri: 'did:example:123456#key-1' });
* ```
*/
export class UniversalResolver {
/**
* Constructs a new `DidResolver`.
*
* @param params - The parameters for constructing the `DidResolver`.
*/
constructor({ cache, didResolvers }) {
/**
* A map to store method resolvers against method names.
*/
this.didResolvers = new Map();
this.cache = cache || DidResolverCacheNoop;
for (const resolver of didResolvers) {
this.didResolvers.set(resolver.methodName, resolver);
}
}
/**
* Resolves a DID to a DID Resolution Result.
*
* If the DID Resolution Result is present in the cache, it returns the cached result. Otherwise,
* it uses the appropriate method resolver to resolve the DID, stores the resolution result in the
* cache, and returns the resolultion result.
*
* @param didUri - The DID or DID URL to resolve.
* @returns A promise that resolves to the DID Resolution Result.
*/
resolve(didUri, options) {
return __awaiter(this, void 0, void 0, function* () {
const parsedDid = Did.parse(didUri);
if (!parsedDid) {
return Object.assign(Object.assign({}, EMPTY_DID_RESOLUTION_RESULT), { didResolutionMetadata: {
error: DidErrorCode.InvalidDid,
errorMessage: `Invalid DID URI: ${didUri}`
} });
}
const resolver = this.didResolvers.get(parsedDid.method);
if (!resolver) {
return Object.assign(Object.assign({}, EMPTY_DID_RESOLUTION_RESULT), { didResolutionMetadata: {
error: DidErrorCode.MethodNotSupported,
errorMessage: `Method not supported: ${parsedDid.method}`
} });
}
const cachedResolutionResult = yield this.cache.get(parsedDid.uri);
if (cachedResolutionResult) {
return cachedResolutionResult;
}
else {
const resolutionResult = yield resolver.resolve(parsedDid.uri, options);
if (!resolutionResult.didResolutionMetadata.error) {
// Cache the resolution result if it was successful.
yield this.cache.set(parsedDid.uri, resolutionResult);
}
return resolutionResult;
}
});
}
/**
* Dereferences a DID (Decentralized Identifier) URL to a corresponding DID resource.
*
* This method interprets the DID URL's components, which include the DID method, method-specific
* identifier, path, query, and fragment, and retrieves the related resource as per the DID Core
* specifications.
*
* The dereferencing process involves resolving the DID contained in the DID URL to a DID document,
* and then extracting the specific part of the document identified by the fragment in the DID URL.
* If no fragment is specified, the entire DID document is returned.
*
* This method supports resolution of different components within a DID document such as service
* endpoints and verification methods, based on their IDs. It accommodates both full and
* DID URLs as specified in the DID Core specification.
*
* More information on DID URL dereferencing can be found in the
* {@link https://www.w3.org/TR/did-core/#did-url-dereferencing | DID Core specification}.
*
* TODO: This is a partial implementation and does not fully implement DID URL dereferencing. (https://github.com/TBD54566975/web5-js/issues/387)
*
* @param didUrl - The DID URL string to dereference.
* @param [_options] - Input options to the dereference function. Optional.
* @returns a {@link DidDereferencingResult}
*/
dereference(didUrl, _options) {
return __awaiter(this, void 0, void 0, function* () {
// Validate the given `didUrl` confirms to the DID URL syntax.
const parsedDidUrl = Did.parse(didUrl);
if (!parsedDidUrl) {
return {
dereferencingMetadata: { error: DidErrorCode.InvalidDidUrl },
contentStream: null,
contentMetadata: {}
};
}
// Obtain the DID document for the input DID by executing DID resolution.
const { didDocument, didResolutionMetadata, didDocumentMetadata } = yield this.resolve(parsedDidUrl.uri);
if (!didDocument) {
return {
dereferencingMetadata: { error: didResolutionMetadata.error },
contentStream: null,
contentMetadata: {}
};
}
// Return the entire DID Document if no query or fragment is present on the DID URL.
if (!parsedDidUrl.fragment || parsedDidUrl.query) {
return {
dereferencingMetadata: { contentType: 'application/did+json' },
contentStream: didDocument,
contentMetadata: didDocumentMetadata
};
}
const { service = [], verificationMethod = [] } = didDocument;
// Create a set of possible id matches. The DID spec allows for an id to be the entire
// did#fragment or just #fragment.
// @see {@link }https://www.w3.org/TR/did-core/#relative-did-urls | Section 3.2.2, Relative DID URLs}.
// Using a Set for fast string comparison since some DID methods have long identifiers.
const idSet = new Set([didUrl, parsedDidUrl.fragment, `#${parsedDidUrl.fragment}`]);
let didResource;
// Find the first matching verification method in the DID document.
for (let vm of verificationMethod) {
if (idSet.has(vm.id)) {
didResource = vm;
break;
}
}
// Find the first matching service in the DID document.
for (let svc of service) {
if (idSet.has(svc.id)) {
didResource = svc;
break;
}
}
if (didResource) {
return {
dereferencingMetadata: { contentType: 'application/did+json' },
contentStream: didResource,
contentMetadata: didResolutionMetadata
};
}
else {
return {
dereferencingMetadata: { error: DidErrorCode.NotFound },
contentStream: null,
contentMetadata: {},
};
}
});
}
}
//# sourceMappingURL=universal-resolver.js.map
@@ -0,0 +1 @@
{"version":3,"file":"universal-resolver.js","sourceRoot":"","sources":["../../../src/resolver/universal-resolver.ts"],"names":[],"mappings":";;;;;;;;;AAIA,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AA8BzE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,OAAO,iBAAiB;IAW5B;;;;OAIG;IACH,YAAY,EAAE,KAAK,EAAE,YAAY,EAA2B;QAV5D;;WAEG;QACK,iBAAY,GAAmC,IAAI,GAAG,EAAE,CAAC;QAQ/D,IAAI,CAAC,KAAK,GAAG,KAAK,IAAI,oBAAoB,CAAC;QAE3C,KAAK,MAAM,QAAQ,IAAI,YAAY,EAAE,CAAC;YACpC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED;;;;;;;;;OASG;IACU,OAAO,CAAC,MAAc,EAAE,OAA8B;;YAEjE,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACpC,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,uCACK,2BAA2B,KAC9B,qBAAqB,EAAE;wBACrB,KAAK,EAAU,YAAY,CAAC,UAAU;wBACtC,YAAY,EAAG,oBAAoB,MAAM,EAAE;qBAC5C,IACD;YACJ,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YACzD,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,uCACK,2BAA2B,KAC9B,qBAAqB,EAAE;wBACrB,KAAK,EAAU,YAAY,CAAC,kBAAkB;wBAC9C,YAAY,EAAG,yBAAyB,SAAS,CAAC,MAAM,EAAE;qBAC3D,IACD;YACJ,CAAC;YAED,MAAM,sBAAsB,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAEnE,IAAI,sBAAsB,EAAE,CAAC;gBAC3B,OAAO,sBAAsB,CAAC;YAChC,CAAC;iBAAM,CAAC;gBACN,MAAM,gBAAgB,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;gBACxE,IAAI,CAAC,gBAAgB,CAAC,qBAAqB,CAAC,KAAK,EAAE,CAAC;oBAClD,oDAAoD;oBACpD,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;gBACxD,CAAC;gBAED,OAAO,gBAAgB,CAAC;YAC1B,CAAC;QACH,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,WAAW,CACf,MAAc,EACd,QAAkC;;YAGlC,8DAA8D;YAC9D,MAAM,YAAY,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAEvC,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,OAAO;oBACL,qBAAqB,EAAG,EAAE,KAAK,EAAE,YAAY,CAAC,aAAa,EAAE;oBAC7D,aAAa,EAAW,IAAI;oBAC5B,eAAe,EAAS,EAAE;iBAC3B,CAAC;YACJ,CAAC;YAED,yEAAyE;YACzE,MAAM,EAAE,WAAW,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;YAEzG,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,OAAO;oBACL,qBAAqB,EAAG,EAAE,KAAK,EAAE,qBAAqB,CAAC,KAAK,EAAE;oBAC9D,aAAa,EAAW,IAAI;oBAC5B,eAAe,EAAS,EAAE;iBAC3B,CAAC;YACJ,CAAC;YAED,oFAAoF;YACpF,IAAI,CAAC,YAAY,CAAC,QAAQ,IAAI,YAAY,CAAC,KAAK,EAAE,CAAC;gBACjD,OAAO;oBACL,qBAAqB,EAAG,EAAE,WAAW,EAAE,sBAAsB,EAAE;oBAC/D,aAAa,EAAW,WAAW;oBACnC,eAAe,EAAS,mBAAmB;iBAC5C,CAAC;YACJ,CAAC;YAED,MAAM,EAAE,OAAO,GAAG,EAAE,EAAE,kBAAkB,GAAG,EAAE,EAAE,GAAG,WAAW,CAAC;YAE9D,sFAAsF;YACtF,kCAAkC;YAClC,sGAAsG;YACtG,uFAAuF;YACvF,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,YAAY,CAAC,QAAQ,EAAE,IAAI,YAAY,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;YAEpF,IAAI,WAAoC,CAAC;YAEzC,mEAAmE;YACnE,KAAK,IAAI,EAAE,IAAI,kBAAkB,EAAE,CAAC;gBAClC,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;oBACrB,WAAW,GAAG,EAAE,CAAC;oBACjB,MAAM;gBACR,CAAC;YACH,CAAC;YAED,uDAAuD;YACvD,KAAK,IAAI,GAAG,IAAI,OAAO,EAAE,CAAC;gBACxB,IAAI,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;oBACtB,WAAW,GAAG,GAAG,CAAC;oBAClB,MAAM;gBACR,CAAC;YACH,CAAC;YAED,IAAI,WAAW,EAAE,CAAC;gBAChB,OAAO;oBACL,qBAAqB,EAAG,EAAE,WAAW,EAAE,sBAAsB,EAAE;oBAC/D,aAAa,EAAW,WAAW;oBACnC,eAAe,EAAS,qBAAqB;iBAC9C,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,OAAO;oBACL,qBAAqB,EAAG,EAAE,KAAK,EAAE,YAAY,CAAC,QAAQ,EAAE;oBACxD,aAAa,EAAW,IAAI;oBAC5B,eAAe,EAAS,EAAE;iBAC3B,CAAC;YACJ,CAAC;QACH,CAAC;KAAA;CACF"}
+51
View File
@@ -0,0 +1,51 @@
/**
* Represents the various verification relationships defined in a DID document.
*
* These verification relationships indicate the intended usage of verification methods within a DID
* document. Each relationship signifies a different purpose or context in which a verification
* method can be used, such as authentication, assertionMethod, keyAgreement, capabilityDelegation,
* and capabilityInvocation. The array provides a standardized set of relationship names for
* consistent referencing and implementation across different DID methods.
*
* @see {@link https://www.w3.org/TR/did-core/#verification-relationships | DID Core Specification, § Verification Relationships}
*/
export var DidVerificationRelationship;
(function (DidVerificationRelationship) {
/**
* Specifies how the DID subject is expected to be authenticated. This is commonly used for
* purposes like logging into a website or participating in challenge-response protocols.
*
* @see {@link https://www.w3.org/TR/did-core/#authentication | DID Core Specification, § Authentication}
*/
DidVerificationRelationship["authentication"] = "authentication";
/**
* Specifies how the DID subject is expected to express claims, such as for issuing Verifiable
* Credentials. This relationship is typically used when the DID subject is the issuer of a
* credential.
*
* @see {@link https://www.w3.org/TR/did-core/#assertion | DID Core Specification, § Assertion}
*/
DidVerificationRelationship["assertionMethod"] = "assertionMethod";
/**
* Specifies how an entity can generate encryption material to communicate confidentially with the
* DID subject. Often used in scenarios requiring secure communication channels.
*
* @see {@link https://www.w3.org/TR/did-core/#key-agreement | DID Core Specification, § Key Agreement}
*/
DidVerificationRelationship["keyAgreement"] = "keyAgreement";
/**
* Specifies a verification method used by the DID subject to invoke a cryptographic capability.
* This is frequently associated with authorization actions, like updating the DID Document.
*
* @see {@link https://www.w3.org/TR/did-core/#capability-invocation | DID Core Specification, § Capability Invocation}
*/
DidVerificationRelationship["capabilityInvocation"] = "capabilityInvocation";
/**
* Specifies a mechanism used by the DID subject to delegate a cryptographic capability to another
* party. This can include delegating access to a specific resource or API.
*
* @see {@link https://www.w3.org/TR/did-core/#capability-delegation | DID Core Specification, § Capability Delegation}
*/
DidVerificationRelationship["capabilityDelegation"] = "capabilityDelegation";
})(DidVerificationRelationship || (DidVerificationRelationship = {}));
//# sourceMappingURL=did-core.js.map
@@ -0,0 +1 @@
{"version":3,"file":"did-core.js","sourceRoot":"","sources":["../../../src/types/did-core.ts"],"names":[],"mappings":"AA+gBA;;;;;;;;;;GAUG;AACH,MAAM,CAAN,IAAY,2BAyCX;AAzCD,WAAY,2BAA2B;IACrC;;;;;OAKG;IACH,gEAAiC,CAAA;IAEjC;;;;;;OAMG;IACH,kEAAmC,CAAA;IAEnC;;;;;OAKG;IACH,4DAA6B,CAAA;IAE7B;;;;;OAKG;IACH,4EAA6C,CAAA;IAE7C;;;;;OAKG;IACH,4EAA6C,CAAA;AAC/C,CAAC,EAzCW,2BAA2B,KAA3B,2BAA2B,QAyCtC"}
@@ -0,0 +1,12 @@
/**
* A constant representing an empty DID Resolution Result. This object is used as the basis for a
* result of DID resolution and is typically augmented with additional properties by the
* DID method resolver.
*/
export const EMPTY_DID_RESOLUTION_RESULT = {
'@context': 'https://w3id.org/did-resolution/v1',
didResolutionMetadata: {},
didDocument: null,
didDocumentMetadata: {},
};
//# sourceMappingURL=did-resolution.js.map
@@ -0,0 +1 @@
{"version":3,"file":"did-resolution.js","sourceRoot":"","sources":["../../../src/types/did-resolution.ts"],"names":[],"mappings":"AAkFA;;;;GAIG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAwB;IAC9D,UAAU,EAAc,oCAAoC;IAC5D,qBAAqB,EAAG,EAAE;IAC1B,WAAW,EAAa,IAAI;IAC5B,mBAAmB,EAAK,EAAE;CAC3B,CAAC"}
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=multibase.js.map
@@ -0,0 +1 @@
{"version":3,"file":"multibase.js","sourceRoot":"","sources":["../../../src/types/multibase.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=portable-did.js.map
@@ -0,0 +1 @@
{"version":3,"file":"portable-did.js","sourceRoot":"","sources":["../../../src/types/portable-did.ts"],"names":[],"mappings":""}
+458
View File
@@ -0,0 +1,458 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Convert, Multicodec } from '@web5/common';
import { computeJwkThumbprint } from '@web5/crypto';
import { DidError, DidErrorCode } from './did-error.js';
import { DidVerificationRelationship, } from './types/did-core.js';
/**
* Extracts the fragment part of a Decentralized Identifier (DID) verification method identifier.
*
* This function takes any input and aims to return only the fragment of a DID identifier,
* which comes after the '#' symbol in a DID string. It's designed specifically for handling
* DID verification method identifiers. The function returns undefined for non-string inputs, inputs
* that do not contain a '#', or complex data structures like objects or arrays, ensuring that only
* the fragment part of a DID string is extracted when present.
*
* @example
* ```ts
* console.log(extractDidFragment("did:example:123#key-1")); // Output: "key-1"
* console.log(extractDidFragment("did:example:123")); // Output: undefined
* console.log(extractDidFragment({ id: "did:example:123#0", type: "JsonWebKey" })); // Output: undefined
* console.log(extractDidFragment(undefined)); // Output: undefined
* ```
*
* @param input - The input to be processed. Can be of any type, but the function is designed
* to work with strings that represent DID verification method identifiers.
* @returns The fragment part of the DID identifier if the input is a string containing a '#'.
* Returns an empty string for all other inputs, including non-string types, strings
* without a '#', and complex data structures.
*/
export function extractDidFragment(input) {
if (typeof input !== 'string')
return undefined;
if (input.length === 0)
return undefined;
return input.split('#').pop();
}
/**
* Retrieves services from a given DID document, optionally filtered by `id` or `type`.
*
* If no `id` or `type` filters are provided, all defined services are returned.
*
* The given DID Document must adhere to the
* {@link https://www.w3.org/TR/did-core/ | W3C DID Core Specification}.
*
* @example
* ```ts
* const didDocument = { ... }; // W3C DID document
* const services = getServices({ didDocument, type: 'DecentralizedWebNode' });
* ```
*
* @param params - An object containing input parameters for retrieving services.
* @param params.didDocument - The DID document from which services are retrieved.
* @param params.id - Optional. A string representing the specific service ID to match. If provided, only the service with this ID will be returned.
* @param params.type - Optional. A string representing the specific service type to match. If provided, only the service(s) of this type will be returned.
* @returns An array of services. If no matching service is found, an empty array is returned.
*/
export function getServices({ didDocument, id, type }) {
var _a, _b;
return (_b = (_a = didDocument === null || didDocument === void 0 ? void 0 : didDocument.service) === null || _a === void 0 ? void 0 : _a.filter(service => {
if (id && service.id !== id)
return false;
if (type && service.type !== type)
return false;
return true;
})) !== null && _b !== void 0 ? _b : [];
}
/**
* Retrieves a verification method object from a DID document if there is a match for the given
* public key.
*
* This function searches the verification methods in a given DID document for a match with the
* provided public key (either in JWK or multibase format). If a matching verification method is
* found it is returned. If no match is found `null` is returned.
*
*
* @example
* ```ts
* const didDocument = {
* // ... contents of a DID document ...
* };
* const publicKeyJwk = { kty: 'OKP', crv: 'Ed25519', x: '...' };
*
* const verificationMethod = await getVerificationMethodByKey({
* didDocument,
* publicKeyJwk
* });
* ```
*
* @param params - An object containing input parameters for retrieving the verification method ID.
* @param params.didDocument - The DID document to search for the verification method.
* @param params.publicKeyJwk - The public key in JSON Web Key (JWK) format to match against the verification methods in the DID document.
* @param params.publicKeyMultibase - The public key as a multibase encoded string to match against the verification methods in the DID document.
* @returns A promise that resolves with the matching verification method, or `null` if no match is found.
* @throws Throws an `Error` if the `didDocument` parameter is missing or if the `didDocument` does not contain any verification methods.
*/
export function getVerificationMethodByKey(_a) {
return __awaiter(this, arguments, void 0, function* ({ didDocument, publicKeyJwk, publicKeyMultibase }) {
// Collect all verification methods from the DID document.
const verificationMethods = getVerificationMethods({ didDocument });
for (let method of verificationMethods) {
if (publicKeyJwk && method.publicKeyJwk) {
const publicKeyThumbprint = yield computeJwkThumbprint({ jwk: publicKeyJwk });
if (publicKeyThumbprint === (yield computeJwkThumbprint({ jwk: method.publicKeyJwk }))) {
return method;
}
}
else if (publicKeyMultibase && method.publicKeyMultibase) {
if (publicKeyMultibase === method.publicKeyMultibase) {
return method;
}
}
}
return null;
});
}
/**
* Retrieves all verification methods from a given DID document, including embedded methods.
*
* This function consolidates all verification methods into a single array for easy access and
* processing. It checks both the primary `verificationMethod` array and the individual verification
* relationship properties `authentication`, `assertionMethod`, `keyAgreement`,
* `capabilityInvocation`, and `capabilityDelegation` for embedded methods.
*
* The given DID Document must adhere to the
* {@link https://www.w3.org/TR/did-core/ | W3C DID Core Specification}.
*
* @example
* ```ts
* const didDocument = { ... }; // W3C DID document
* const verificationMethods = getVerificationMethods({ didDocument });
* ```
*
* @param params - An object containing input parameters for retrieving verification methods.
* @param params.didDocument - The DID document from which verification methods are retrieved.
* @returns An array of `DidVerificationMethod`. If no verification methods are found, an empty array is returned.
* @throws Throws an `TypeError` if the `didDocument` parameter is missing.
*/
export function getVerificationMethods({ didDocument }) {
var _a, _b;
if (!didDocument)
throw new TypeError(`Required parameter missing: 'didDocument'`);
const verificationMethods = [];
// Check the 'verificationMethod' array.
verificationMethods.push(...(_b = (_a = didDocument.verificationMethod) === null || _a === void 0 ? void 0 : _a.filter(isDidVerificationMethod)) !== null && _b !== void 0 ? _b : []);
// Check verification relationship properties for embedded verification methods.
Object.keys(DidVerificationRelationship).forEach((relationship) => {
var _a, _b;
verificationMethods.push(...(_b = (_a = didDocument[relationship]) === null || _a === void 0 ? void 0 : _a.filter(isDidVerificationMethod)) !== null && _b !== void 0 ? _b : []);
});
return verificationMethods;
}
/**
* Retrieves all DID verification method types from a given DID document.
*
* The given DID Document must adhere to the
* {@link https://www.w3.org/TR/did-core/ | W3C DID Core Specification}.
*
* @example
* ```ts
* const didDocument = {
* verificationMethod: [
* {
* 'id' : 'did:example:123#key-0',
* 'type' : 'Ed25519VerificationKey2018',
* 'controller' : 'did:example:123',
* 'publicKeyBase58' : '3M5RCDjPTWPkKSN3sxUmmMqHbmRPegYP1tjcKyrDbt9J'
* },
* {
* 'id' : 'did:example:123#key-1',
* 'type' : 'X25519KeyAgreementKey2019',
* 'controller' : 'did:example:123',
* 'publicKeyBase58' : 'FbQWLPRhTH95MCkQUeFYdiSoQt8zMwetqfWoxqPgaq7x'
* },
* {
* 'id' : 'did:example:123#key-3',
* 'type' : 'JsonWebKey2020',
* 'controller' : 'did:example:123',
* 'publicKeyJwk' : {
* 'kty' : 'EC',
* 'crv' : 'P-256',
* 'x' : 'Er6KSSnAjI70ObRWhlaMgqyIOQYrDJTE94ej5hybQ2M',
* 'y' : 'pPVzCOTJwgikPjuUE6UebfZySqEJ0ZtsWFpj7YSPGEk'
* }
* }
* ]
* },
* const vmTypes = getVerificationMethodTypes({ didDocument });
* console.log(vmTypes);
* // Output: ['Ed25519VerificationKey2018', 'X25519KeyAgreementKey2019', 'JsonWebKey2020']
* ```
*
* @param params - An object containing input parameters for retrieving types.
* @param params.didDocument - The DID document from which types are retrieved.
* @returns An array of types. If no types were found, an empty array is returned.
*/
export function getVerificationMethodTypes({ didDocument }) {
// Collect all verification methods from the DID document.
const verificationMethods = getVerificationMethods({ didDocument });
// Map to extract 'type' from each verification method.
const types = verificationMethods.map(method => method.type);
return [...new Set(types)]; // Return only unique types.
}
/**
* Retrieves a list of DID verification relationships by a specific method ID from a DID document.
*
* This function examines the specified DID document to identify any verification relationships
* (e.g., `authentication`, `assertionMethod`) that reference a verification method by its method ID
* or contain an embedded verification method matching the method ID. The method ID is typically a
* fragment of a DID (e.g., `did:example:123#key-1`) that uniquely identifies a verification method
* within the DID document.
*
* The search considers both direct references to verification methods by their IDs and verification
* methods embedded within the verification relationship arrays. It returns an array of
* `DidVerificationRelationship` enums corresponding to the verification relationships that contain
* the specified method ID.
*
* @param params - An object containing input parameters for retrieving verification relationships.
* @param params.didDocument - The DID document to search for verification relationships.
* @param params.methodId - The method ID to search for within the verification relationships.
* @returns An array of `DidVerificationRelationship` enums representing the types of verification
* relationships that reference the specified method ID.
*
* @example
* ```ts
* const didDocument: DidDocument = {
* // ...contents of a DID document...
* };
*
* const relationships = getVerificationRelationshipsById({
* didDocument,
* methodId: 'key-1'
* });
* console.log(relationships);
* // Output might include ['authentication', 'assertionMethod'] if those relationships
* // reference or contain the specified method ID.
* ```
*/
export function getVerificationRelationshipsById({ didDocument, methodId }) {
const relationships = [];
Object.keys(DidVerificationRelationship).forEach((relationship) => {
if (Array.isArray(didDocument[relationship])) {
const relationshipMethods = didDocument[relationship];
const methodIdFragment = extractDidFragment(methodId);
// Check if the verification relationship property contains a matching method ID either
// directly referenced or as an embedded verification method.
const containsMethodId = relationshipMethods.some(method => {
const isByReferenceMatch = extractDidFragment(method) === methodIdFragment;
const isEmbeddedMethodMatch = isDidVerificationMethod(method) && extractDidFragment(method.id) === methodIdFragment;
return isByReferenceMatch || isEmbeddedMethodMatch;
});
if (containsMethodId) {
relationships.push(relationship);
}
}
});
return relationships;
}
/**
* Checks if a given object is a {@link DidService}.
*
* A {@link DidService} in the context of DID resources must include the properties `id`, `type`,
* and `serviceEndpoint`. The `serviceEndpoint` can be a `DidServiceEndpoint` or an array of
* `DidServiceEndpoint` objects.
*
* @example
* ```ts
* const service = {
* id: "did:example:123#service-1",
* type: "OidcService",
* serviceEndpoint: "https://example.com/oidc"
* };
*
* if (isDidService(service)) {
* console.log('The object is a DidService');
* } else {
* console.log('The object is not a DidService');
* }
* ```
*
* @param obj - The object to be checked.
* @returns `true` if `obj` is a `DidService`; otherwise, `false`.
*/
export function isDidService(obj) {
// Validate that the given value is an object.
if (!obj || typeof obj !== 'object' || obj === null)
return false;
// Validate that the object has the necessary properties of DidService.
return 'id' in obj && 'type' in obj && 'serviceEndpoint' in obj;
}
/**
* Checks if a given object is a {@link DwnDidService}.
*
* A {@link DwnDidService} is defined as {@link DidService} object with a `type` of
* "DecentralizedWebNode" and `enc` and `sig` properties, where both properties are either strings
* or arrays of strings.
*
* @example
* ```ts
* const didDocument: DidDocument = {
* id: 'did:example:123',
* verificationMethod: [
* {
* id: 'did:example:123#key-1',
* type: 'JsonWebKey2020',
* controller: 'did:example:123',
* publicKeyJwk: { ... }
* },
* {
* id: 'did:example:123#key-2',
* type: 'JsonWebKey2020',
* controller: 'did:example:123',
* publicKeyJwk: { ... }
* }
* ],
* service: [
* {
* id: 'did:example:123#dwn',
* type: 'DecentralizedWebNode',
* serviceEndpoint: 'https://dwn.tbddev.org/dwn0',
* enc: 'did:example:123#key-1',
* sig: 'did:example:123#key-2'
* }
* ]
* };
*
* if (isDwnService(didDocument.service[0])) {
* console.log('The object is a DwnDidService');
* } else {
* console.log('The object is not a DwnDidService');
* }
* ```
*
* @see {@link https://identity.foundation/decentralized-web-node/spec/ | Decentralized Web Node (DWN) Specification}
*
* @param obj - The object to be checked.
* @returns `true` if `obj` is a DwnDidService; otherwise, `false`.
*/
export function isDwnDidService(obj) {
// Validate that the given value is a {@link DidService}.
if (!isDidService(obj))
return false;
// Validate that the `type` property is `DecentralizedWebNode`.
if (obj.type !== 'DecentralizedWebNode')
return false;
// Validate that the given object has the `enc` and `sig` properties.
if (!('enc' in obj && 'sig' in obj))
return false;
// Validate that the `enc` and `sig` properties are either strings or arrays of strings.
const isStringOrStringArray = (prop) => typeof prop === 'string' || Array.isArray(prop) && prop.every(item => typeof item === 'string');
return (isStringOrStringArray(obj.enc)) && (isStringOrStringArray(obj.sig));
}
/**
* Checks if a given object is a DID Verification Method.
*
* A {@link DidVerificationMethod} in the context of DID resources must include the properties `id`,
* `type`, and `controller`.
*
* @example
* ```ts
* const resource = {
* id : "did:example:123#0",
* type : "JsonWebKey2020",
* controller : "did:example:123",
* publicKeyJwk : { ... }
* };
*
* if (isDidVerificationMethod(resource)) {
* console.log('The resource is a DidVerificationMethod');
* } else {
* console.log('The resource is not a DidVerificationMethod');
* }
* ```
*
* @param obj - The object to be checked.
* @returns `true` if `obj` is a `DidVerificationMethod`; otherwise, `false`.
*/
export function isDidVerificationMethod(obj) {
// Validate that the given value is an object.
if (!obj || typeof obj !== 'object' || obj === null)
return false;
// Validate that the object has the necessary properties of a DidVerificationMethod.
if (!('id' in obj && 'type' in obj && 'controller' in obj))
return false;
if (typeof obj.id !== 'string')
return false;
if (typeof obj.type !== 'string')
return false;
if (typeof obj.controller !== 'string')
return false;
return true;
}
/**
* Converts a cryptographic key to a multibase identifier.
*
* @remarks
* This method provides a way to represent a cryptographic key as a multibase identifier.
* It takes a `Uint8Array` representing the key, and either the multicodec code or multicodec name
* as input. The method first adds the multicodec prefix to the key, then encodes it into Base58
* format. Finally, it converts the Base58 encoded key into a multibase identifier.
*
* @example
* ```ts
* const key = new Uint8Array([...]); // Cryptographic key as Uint8Array
* const multibaseId = keyBytesToMultibaseId({ key, multicodecName: 'ed25519-pub' });
* ```
*
* @param params - The parameters for the conversion.
* @returns The multibase identifier as a string.
*/
export function keyBytesToMultibaseId({ keyBytes, multicodecCode, multicodecName }) {
const prefixedKey = Multicodec.addPrefix({
code: multicodecCode,
data: keyBytes,
name: multicodecName
});
const prefixedKeyB58 = Convert.uint8Array(prefixedKey).toBase58Btc();
const multibaseKeyId = Convert.base58Btc(prefixedKeyB58).toMultibase();
return multibaseKeyId;
}
/**
* Converts a multibase identifier to a cryptographic key.
*
* @remarks
* This function decodes a multibase identifier back into a cryptographic key. It first decodes the
* identifier from multibase format into Base58 format, and then converts it into a `Uint8Array`.
* Afterward, it removes the multicodec prefix, extracting the raw key data along with the
* multicodec code and name.
*
* @example
* ```ts
* const multibaseKeyId = '...'; // Multibase identifier of the key
* const { key, multicodecCode, multicodecName } = multibaseIdToKey({ multibaseKeyId });
* ```
*
* @param params - The parameters for the conversion.
* @param params.multibaseKeyId - The multibase identifier string of the key.
* @returns An object containing the key as a `Uint8Array` and its multicodec code and name.
* @throws `DidError` if the multibase identifier is invalid.
*/
export function multibaseIdToKeyBytes({ multibaseKeyId }) {
try {
const prefixedKeyB58 = Convert.multibase(multibaseKeyId).toBase58Btc();
const prefixedKey = Convert.base58Btc(prefixedKeyB58).toUint8Array();
const { code, data, name } = Multicodec.removePrefix({ prefixedData: prefixedKey });
return { keyBytes: data, multicodecCode: code, multicodecName: name };
}
catch (error) {
throw new DidError(DidErrorCode.InvalidDid, `Invalid multibase identifier: ${multibaseKeyId}`);
}
}
//# sourceMappingURL=utils.js.map
File diff suppressed because one or more lines are too long