UNPKG

@enbox/dids

Version:
298 lines 15.1 kB
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 '@enbox/common'; import { LocalKeyManager } from '@enbox/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