UNPKG

@nuwa-ai/identity-kit

Version:

SDK for NIP-1 Agent Single DID Multi-Key Model and NIP-3 CADOP (Custodian-Assisted DID Onboarding Protocol)

1,594 lines (1,576 loc) 144 kB
var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // src/types/crypto.ts var KeyType = /* @__PURE__ */ ((KeyType7) => { KeyType7["ED25519"] = "Ed25519VerificationKey2020"; KeyType7["SECP256K1"] = "EcdsaSecp256k1VerificationKey2019"; KeyType7["ECDSAR1"] = "EcdsaSecp256r1VerificationKey2019"; return KeyType7; })(KeyType || {}); var KEY_TYPE = KeyType; function isKeyType(value) { return Object.values(KeyType).includes(value); } function toKeyType(value) { if (isKeyType(value)) { return value; } throw new Error(`Invalid key type: ${value}`); } function roochSignatureSchemeToKeyType(scheme) { if (scheme === "Secp256k1") { return "EcdsaSecp256k1VerificationKey2019" /* SECP256K1 */; } else if (scheme === "ED25519") { return "Ed25519VerificationKey2020" /* ED25519 */; } else if (scheme === "EcdsaR1") { return "EcdsaSecp256r1VerificationKey2019" /* ECDSAR1 */; } throw new Error(`Unsupported Rooch signature scheme: ${scheme}`); } function keyTypeToRoochSignatureScheme(keyType) { if (keyType === "EcdsaSecp256k1VerificationKey2019" /* SECP256K1 */) { return "Secp256k1"; } else if (keyType === "Ed25519VerificationKey2020" /* ED25519 */) { return "ED25519"; } else if (keyType === "EcdsaSecp256r1VerificationKey2019" /* ECDSAR1 */) { return "EcdsaR1"; } throw new Error(`Unsupported key type: ${keyType}`); } function algorithmToKeyType(algorithm) { switch (algorithm) { case -8: return "Ed25519VerificationKey2020" /* ED25519 */; case -7: return "EcdsaSecp256r1VerificationKey2019" /* ECDSAR1 */; default: return void 0; } } function keyTypeToAlgorithm(keyType) { switch (keyType) { case "Ed25519VerificationKey2020" /* ED25519 */: return -8; case "EcdsaSecp256r1VerificationKey2019" /* ECDSAR1 */: return -7; default: return void 0; } } function getSupportedAlgorithms() { return [-8, -7]; } // src/cache/InMemoryLRUDIDDocumentCache.ts var InMemoryLRUDIDDocumentCache = class { constructor(maxEntries = 1e3) { this.capacity = maxEntries; this.map = /* @__PURE__ */ new Map(); } get(did) { if (!this.map.has(did)) return void 0; const value = this.map.get(did) ?? null; this.map.delete(did); this.map.set(did, value); return value; } set(did, doc) { if (this.map.has(did)) { this.map.delete(did); } else if (this.map.size >= this.capacity) { const lruKey = this.map.keys().next().value; if (lruKey !== void 0) { this.map.delete(lruKey); } } this.map.set(did, doc); } has(did) { return this.map.has(did); } delete(did) { this.map.delete(did); } clear() { this.map.clear(); } }; // src/vdr/VDRRegistry.ts var VDRRegistry = class _VDRRegistry { constructor() { this.vdrs = /* @__PURE__ */ new Map(); this.cache = new InMemoryLRUDIDDocumentCache(); } static getInstance() { if (!this.instance) { this.instance = new _VDRRegistry(); } return this.instance; } /** Register a VDR implementation for its DID method (e.g., 'key', 'rooch'). */ registerVDR(vdr) { this.vdrs.set(vdr.getMethod(), vdr); } /** Retrieve a previously registered VDR implementation by its method. */ getVDR(method) { return this.vdrs.get(method); } /** * Override the default cache implementation. * This allows developers to provide their own cache (e.g., Redis, browser storage). */ setCache(cache) { this.cache = cache; } /** Returns the currently configured cache instance. */ getCache() { return this.cache; } async resolveDID(did, options) { const method = did.split(":")[1]; const vdr = this.vdrs.get(method); if (!vdr) { throw new Error(`No VDR available for method: ${method}`); } if (!options?.forceRefresh) { const cached = this.cache.get(did); if (cached !== void 0) { return cached; } } const resolved = await vdr.resolve(did); this.cache.set(did, resolved); return resolved; } async createDID(method, creationRequest, options) { const vdr = this.vdrs.get(method); if (!vdr) { throw new Error(`No VDR available for method: ${method}`); } const result = await vdr.create(creationRequest, options); if (result.success && result.didDocument) { this.cache.set(result.didDocument.id, result.didDocument); } return result; } async createDIDViaCADOP(method, creationRequest, options) { const vdr = this.vdrs.get(method); if (!vdr) { throw new Error(`No VDR available for method: ${method}`); } const result = await vdr.createViaCADOP(creationRequest, options); if (result.success && result.didDocument) { this.cache.set(result.didDocument.id, result.didDocument); } return result; } async exists(did) { const method = did.split(":")[1]; const vdr = this.vdrs.get(method); if (!vdr) { throw new Error(`No VDR available for method: ${method}`); } if (this.cache.has(did)) { const doc = this.cache.get(did); return doc !== null; } const exists = await vdr.exists(did); return exists; } }; // src/multibase/base.ts import { base58btc } from "multiformats/bases/base58"; import { base64pad, base64, base64url, base64urlpad } from "multiformats/bases/base64"; import { base16 } from "multiformats/bases/base16"; // src/utils/bytes.ts function stringToBytes(str) { if (typeof TextEncoder !== "undefined") { return new TextEncoder().encode(str); } if (typeof Buffer !== "undefined" && typeof Buffer.from === "function") { return Uint8Array.from(Buffer.from(str, "utf-8")); } throw new Error("No TextEncoder or Buffer available in this environment."); } function bytesToString(bytes) { if (typeof TextDecoder !== "undefined") { return new TextDecoder("utf-8").decode(bytes); } if (typeof Buffer !== "undefined" && typeof Buffer.from === "function") { return Buffer.from(bytes).toString("utf-8"); } throw new Error("No TextDecoder or Buffer available in this environment."); } function base64urlToBytes(base64url2) { const padding = base64url2.length % 4; const paddedBase64url = base64url2 + "=".repeat(padding === 0 ? 0 : 4 - padding); const base642 = paddedBase64url.replace(/-/g, "+").replace(/_/g, "/"); const binaryString = atob(base642); const bytes = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes; } var Bytes = { stringToBytes, bytesToString, base64urlToBytes }; // src/multibase/base.ts var ENCODER_MAP = { base58btc, base64pad, base64, base64url, base64urlpad, base16 }; var MultibaseCodec = class { /** * Generic encode * Example: `MultibaseCodec.encode(bytes, 'base64url')` */ static encode(data, base) { const encoder = ENCODER_MAP[base]; if (!encoder) { throw new Error(`Unsupported multibase: ${base}`); } const bytes = typeof data === "string" ? stringToBytes(data) : data; return encoder.encode(bytes); } /** * Encode bytes to base58btc format * @param bytes The bytes to encode * @returns base58btc encoded string with 'z' prefix */ static encodeBase58btc(bytes) { return this.encode(bytes, "base58btc"); } /** * Encode bytes to base64pad format * @param bytes The bytes to encode * @returns base64pad encoded string with 'M' prefix */ static encodeBase64pad(data) { return this.encode(data, "base64pad"); } /** * Encode bytes to base16 (hex) format * @param bytes The bytes to encode * @returns base16 encoded string with 'f' prefix */ static encodeBase16(bytes) { return this.encode(bytes, "base16"); } /** * Encode bytes to base64 format * @param bytes The bytes to encode * @returns base64 encoded string */ static encodeBase64(data) { return this.encode(data, "base64"); } /** * Encode bytes to base64url format (RFC4648 URL-safe, no padding) * @param bytes The bytes to encode * @returns base64url encoded string with 'u' prefix */ static encodeBase64url(data) { return this.encode(data, "base64url"); } /** * Encode bytes to base64urlpad format (URL-safe with padding) * @param bytes The bytes to encode * @returns base64urlpad encoded string with 'U' prefix */ static encodeBase64urlpad(data) { return this.encode(data, "base64urlpad"); } /** * Decode base58btc string to bytes * @param encoded The base58btc encoded string * @returns decoded bytes */ static decodeBase58btc(encoded) { return base58btc.decode(encoded); } /** * Decode base64pad string to bytes * @param encoded The base64pad encoded string * @returns decoded bytes */ static decodeBase64pad(encoded) { return base64pad.decode(encoded); } /** * Decode base16 string to bytes * @param encoded The base16 encoded string * @returns decoded bytes */ static decodeBase16(encoded) { return base16.decode(encoded); } /** * Decode base64 string to bytes * @param encoded The base64 encoded string * @returns decoded bytes */ static decodeBase64(encoded) { return base64.decode(encoded); } /** * Decode base64url string to bytes * @param encoded The base64url encoded string * @returns decoded bytes */ static decodeBase64url(encoded) { return base64url.decode(encoded); } /** * Decode base64url string to string * @param encoded The base64url encoded string * @returns decoded string */ static decodeBase64urlToString(encoded) { return bytesToString(this.decodeBase64url(encoded)); } /** * Decode base64urlpad string to bytes * @param encoded The base64urlpad encoded string * @returns decoded bytes */ static decodeBase64urlpad(encoded) { return base64urlpad.decode(encoded); } /** * Decode base64urlpad string to string * @param encoded The base64urlpad encoded string * @returns decoded string */ static decodeBase64urlpadToString(encoded) { return bytesToString(this.decodeBase64urlpad(encoded)); } /** * Decode multibase encoded string to bytes * After multiformats v9, there is no longer a single "universal base" object; * the official recommendation is to manually dispatch prefixes between the few *.decoder objects you use. * @param encoded The multibase encoded string * @returns decoded bytes */ static decode(encoded) { const prefix = encoded[0]; switch (prefix) { case "z": return base58btc.decode(encoded); case "M": return base64pad.decode(encoded); case "f": return base16.decode(encoded); case "m": return base64.decode(encoded); case "u": return base64url.decode(encoded); case "U": return base64urlpad.decode(encoded); default: throw new Error(`Unsupported multibase prefix: ${prefix}`); } } }; // src/multibase/key.ts var KeyMultibaseCodec = class { /** * Encode public key with multicodec prefix * @param bytes The public key bytes * @param keyType The key type * @returns multibase encoded string */ static encodeWithType(bytes, keyType) { const expectedLength = this.getExpectedKeyLength(keyType); if (bytes.length !== expectedLength) { throw new Error( `Invalid key length for ${keyType}. Expected ${expectedLength} bytes, got ${bytes.length}` ); } const prefix = this.getMulticodecPrefix(keyType); const prefixedKey = this.concatenateBytes(prefix, bytes); return MultibaseCodec.encodeBase58btc(prefixedKey); } /** * Decode multibase encoded key * @param encoded The multibase encoded string * @returns The key type and public key bytes */ static decodeWithType(encoded) { try { const decoded = MultibaseCodec.decodeBase58btc(encoded); if (decoded.length < 2) { throw new Error("Invalid key format: too short"); } const keyType = this.extractKeyType(decoded); const bytes = this.extractBytes(decoded); const expectedLength = this.getExpectedKeyLength(keyType); if (bytes.length !== expectedLength) { throw new Error( `Invalid key length for ${keyType}. Expected ${expectedLength} bytes, got ${bytes.length}` ); } return { keyType, bytes }; } catch (error) { if (error instanceof Error && error.message === "Non-base58btc character") { throw new Error("Invalid multibase format"); } throw error; } } static getMulticodecPrefix(keyType) { switch (keyType) { case KEY_TYPE.ED25519: return this.ED25519_PREFIX; case KEY_TYPE.SECP256K1: return this.SECP256K1_PREFIX; case KEY_TYPE.ECDSAR1: return this.ECDSA_R1_PREFIX; default: throw new Error(`Unsupported key type: ${keyType}`); } } static concatenateBytes(a, b) { const result = new Uint8Array(a.length + b.length); result.set(a); result.set(b, a.length); return result; } static extractKeyType(prefixedBytes) { if (prefixedBytes[0] === 237 && prefixedBytes[1] === 1) { return KEY_TYPE.ED25519; } else if (prefixedBytes[0] === 231 && prefixedBytes[1] === 1) { return KEY_TYPE.SECP256K1; } else if (prefixedBytes[0] === 18 && prefixedBytes[1] === 0) { return KEY_TYPE.ECDSAR1; } throw new Error("Unknown key type prefix"); } static extractBytes(prefixedBytes) { return prefixedBytes.slice(2); } static getExpectedKeyLength(keyType) { switch (keyType) { case KEY_TYPE.ED25519: return this.ED25519_KEY_LENGTH; case KEY_TYPE.SECP256K1: return this.SECP256K1_KEY_LENGTH; case KEY_TYPE.ECDSAR1: return this.ECDSA_R1_KEY_LENGTH; default: throw new Error(`Unsupported key type: ${keyType}`); } } }; KeyMultibaseCodec.ED25519_PREFIX = new Uint8Array([237, 1]); KeyMultibaseCodec.SECP256K1_PREFIX = new Uint8Array([231, 1]); KeyMultibaseCodec.ECDSA_R1_PREFIX = new Uint8Array([18, 0]); KeyMultibaseCodec.ED25519_KEY_LENGTH = 32; KeyMultibaseCodec.SECP256K1_KEY_LENGTH = 33; KeyMultibaseCodec.ECDSA_R1_KEY_LENGTH = 33; // src/multibase/did.ts var DidKeyCodec = class { /** * Generate did:key from public key * @param publicKey The public key bytes * @param keyType The key type * @returns did:key identifier */ static generateDidKey(publicKey, keyType) { const multibase = KeyMultibaseCodec.encodeWithType(publicKey, keyType); return `did:key:${multibase}`; } /** * Parse did:key to get key type and public key * @param didKey The did:key identifier * @returns The key type and public key bytes */ static parseDidKey(didKey) { if (!didKey.startsWith("did:key:")) { throw new Error("Invalid did:key format"); } const multibase = didKey.substring(8); const { keyType, bytes } = KeyMultibaseCodec.decodeWithType(multibase); return { keyType, publicKey: bytes }; } }; // src/utils/did.ts function parseDid(did) { if (!did.startsWith("did:")) { throw new Error(`Invalid DID: ${did}`); } const afterPrefix = did.slice(4); const methodEnd = afterPrefix.indexOf(":"); if (methodEnd === -1) { throw new Error(`Invalid DID \u2013 missing method/identifier separator: ${did}`); } const method = afterPrefix.slice(0, methodEnd); const idPlusFrag = afterPrefix.slice(methodEnd + 1); if (!method || !idPlusFrag) { throw new Error(`Invalid DID: ${did}`); } const hashIdx = idPlusFrag.indexOf("#"); return hashIdx === -1 ? { method, identifier: idPlusFrag } : { method, identifier: idPlusFrag.slice(0, hashIdx), fragment: idPlusFrag.slice(hashIdx + 1) }; } function extractMethod(did) { return parseDid(did).method; } function extractIdentifier(did) { return parseDid(did).identifier; } function extractFragment(idOrDid) { const idx = idOrDid.indexOf("#"); if (idx === -1) { throw new Error(`No fragment found in ${idOrDid}`); } return idOrDid.slice(idx + 1); } var extractFragmentFromId = extractFragment; function buildDid(method, identifier) { return `did:${method}:${identifier}`; } function sameDid(a, b) { const pa = parseDid(a); const pb = parseDid(b); return pa.method === pb.method && pa.identifier === pb.identifier; } function getDidWithoutFragment(did) { const { method, identifier } = parseDid(did); return buildDid(method, identifier); } // src/utils/DebugLogger.ts var LEVEL_ORDER = { debug: 10, info: 20, warn: 30, error: 40, silent: 50 }; function detectInitialGlobalLevel() { if (typeof process !== "undefined" && process.env) { const envLevel = process.env.NUWA_LOG_LEVEL; if (envLevel && envLevel in LEVEL_ORDER) return envLevel; } if (typeof window !== "undefined" && window.__NUWA_LOG_LEVEL__) { const envLevel = window.__NUWA_LOG_LEVEL__; if (envLevel && envLevel in LEVEL_ORDER) return envLevel; } return "info"; } var _DebugLogger = class _DebugLogger { constructor(namespace) { this.namespace = namespace; this.levelOverridden = false; this.level = _DebugLogger.globalLevel; } /** Acquire (or create) a logger for the given namespace. */ static get(namespace) { if (!_DebugLogger.loggers.has(namespace)) { _DebugLogger.loggers.set(namespace, new _DebugLogger(namespace)); } return _DebugLogger.loggers.get(namespace); } /** Override global log level at runtime. */ static setGlobalLevel(level) { _DebugLogger.globalLevel = level; for (const logger3 of _DebugLogger.loggers.values()) { if (!logger3.levelOverridden) { logger3.level = level; } } } /** Read current global level. */ static getGlobalLevel() { return _DebugLogger.globalLevel; } /** Set default namespace used by static convenience methods. */ static setDefaultNamespace(namespace) { _DebugLogger.defaultNamespace = namespace; } // --------------------------------------------------------------------------- // Static convenience logging methods // --------------------------------------------------------------------------- /** * Log using the default namespace. Useful when callers don't need per-module loggers. * Example: DebugLogger.debug('hello') */ static debug(...args) { _DebugLogger.get(_DebugLogger.defaultNamespace).debug(...args); } static info(...args) { _DebugLogger.get(_DebugLogger.defaultNamespace).info(...args); } static warn(...args) { _DebugLogger.get(_DebugLogger.defaultNamespace).warn(...args); } static error(...args) { _DebugLogger.get(_DebugLogger.defaultNamespace).error(...args); } /** Override level for this logger only. */ setLevel(level) { this.level = level; this.levelOverridden = true; } // ------------------------------------------------------- // Logging helpers // ------------------------------------------------------- debug(...args) { this._log("debug", args); } info(...args) { this._log("info", args); } warn(...args) { this._log("warn", args); } error(...args) { this._log("error", args); } // prettier-ignore _log(level, args) { if (LEVEL_ORDER[level] < LEVEL_ORDER[this.level]) { return; } const prefix = `[${this.namespace}]`; switch (level) { case "debug": console.debug(prefix, ...args); break; case "info": console.info(prefix, ...args); break; case "warn": console.warn(prefix, ...args); break; case "error": console.error(prefix, ...args); break; } } }; // --------------------------------------------------------------------------- // Static section // --------------------------------------------------------------------------- _DebugLogger.globalLevel = detectInitialGlobalLevel(); _DebugLogger.loggers = /* @__PURE__ */ new Map(); _DebugLogger.defaultNamespace = "global"; var DebugLogger = _DebugLogger; // src/vdr/abstractVDR.ts var logger = DebugLogger.get("AbstractVDR"); var AbstractVDR = class { /** * Creates a new AbstractVDR instance * * @param method The DID method this VDR handles */ constructor(method) { this.method = method; } /** * Gets the DID method this VDR handles * * @returns The DID method string */ getMethod() { return this.method; } /** * Validates that a given DID matches the method this VDR handles * * @param did The DID to validate * @throws Error if the DID doesn't match this VDR's method */ validateDIDMethod(did) { const { method } = parseDid(did); if (method !== this.method) { throw new Error(`DID ${did} is not a valid did:${this.method} identifier`); } } /** * Validates a DID document's basic structure * * @param document The DID document to validate * @returns true if valid, throws an error otherwise */ validateDocument(document) { if (!document.id) { throw new Error("DID document must have an id"); } this.validateDIDMethod(document.id); if (!document["@context"]) { throw new Error("DID document must have a @context property"); } if (!document.verificationMethod || document.verificationMethod.length === 0) { throw new Error("DID document must have at least one verification method"); } return true; } /** * Check if a key has a specific verification relationship in a DID document * * @param didDocument The DID document to check * @param keyId The ID of the verification method * @param relationship The verification relationship to check * @returns True if the key has the relationship */ hasVerificationRelationship(didDocument, keyId, relationship) { const relationshipArray = didDocument[relationship]; if (!relationshipArray) return false; return relationshipArray.some((item) => { if (typeof item === "string") { return item === keyId; } else if (typeof item === "object" && item.id) { return item.id === keyId; } return false; }); } /** * Validates if a key has permission to perform an operation * * @param didDocument The DID document * @param keyId The ID of the key * @param requiredRelationship The required verification relationship * @returns True if the key has permission */ validateKeyPermission(didDocument, keyId, requiredRelationship) { const keyExists = didDocument.verificationMethod?.some((vm) => vm.id === keyId); if (!keyExists) { logger.error(`Key ${keyId} not found in DID document`); return false; } const isPrimaryKey = didDocument.verificationMethod?.[0]?.id === keyId; if (isPrimaryKey) { return true; } const hasPermission = didDocument[requiredRelationship]?.includes(keyId); if (!hasPermission) { logger.error(`Key ${keyId} does not have ${requiredRelationship} permission`); return false; } return true; } /** * Default create implementation - throws not implemented error for base class * Subclasses must override this method to provide actual implementation */ async create(request, options) { throw new Error(`create method not implemented for ${this.method} VDR`); } /** * Default CADOP implementation - throws not implemented error */ async createViaCADOP(request, options) { throw new Error(`createViaCADOP not implemented for ${this.method} VDR`); } /** * Build DID Document from creation request */ buildDIDDocumentFromRequest(request) { const did = request.preferredDID; const controllerForVM = Array.isArray(request.controller) ? request.controller[0] : request.controller || did; const verificationMethod = { id: `${did}#account-key`, type: request.keyType || "EcdsaSecp256k1VerificationKey2019", controller: controllerForVM, publicKeyMultibase: request.publicKeyMultibase }; const didDocument = { "@context": ["https://www.w3.org/ns/did/v1"], id: did, controller: request.controller ? Array.isArray(request.controller) ? request.controller : [request.controller] : [did], verificationMethod: [verificationMethod, ...request.additionalVerificationMethods || []], service: request.initialServices || [] }; const relationships = request.initialRelationships || [ "authentication", "assertionMethod", "capabilityInvocation", "capabilityDelegation" ]; const vmId = verificationMethod.id; relationships.forEach((rel) => { if (!didDocument[rel]) { didDocument[rel] = []; } didDocument[rel].push(vmId); }); return didDocument; } /** * Checks if a DID exists in the registry * Default implementation tries to resolve and checks if result is not null */ async exists(did) { try { const doc = await this.resolve(did); return doc !== null; } catch (error) { return false; } } /** * Add a verification method to a DID document * Default implementation that should be overridden by specific VDR implementations */ async addVerificationMethod(did, verificationMethod, relationships, options) { throw new Error(`addVerificationMethod not implemented for ${this.method} VDR`); } /** * Remove a verification method from a DID document * Default implementation that should be overridden by specific VDR implementations */ async removeVerificationMethod(did, id, options) { throw new Error(`removeVerificationMethod not implemented for ${this.method} VDR`); } /** * Add a service to a DID document * Default implementation that should be overridden by specific VDR implementations */ async addService(did, service, options) { throw new Error(`addService not implemented for ${this.method} VDR`); } /** * Remove a service from a DID document * Default implementation that should be overridden by specific VDR implementations */ async removeService(did, id, options) { throw new Error(`removeService not implemented for ${this.method} VDR`); } /** * Update verification relationships for a verification method * Default implementation that should be overridden by specific VDR implementations */ async updateRelationships(did, id, add, remove, options) { throw new Error(`updateRelationships not implemented for ${this.method} VDR`); } /** * Update the controller of a DID document * Default implementation that should be overridden by specific VDR implementations */ async updateController(did, controller, options) { throw new Error(`updateController not implemented for ${this.method} VDR`); } /** * Validates options for update operations and ensures proper permissions * * @param did The DID being operated on * @param document The resolved DID document * @param keyId The key ID used for signing * @param requiredRelationship The required verification relationship for this operation * @throws Error if validation fails */ async validateUpdateOperation(did, document, keyId, requiredRelationship) { this.validateDIDMethod(did); if (!document) { throw new Error(`DID document ${did} not found`); } if (!this.validateKeyPermission(document, keyId, requiredRelationship)) { throw new Error( `Key ${keyId} does not have ${requiredRelationship} permission required for this operation` ); } return document; } /** * Validates that inputs to addVerificationMethod are correct * * @param did The DID being operated on * @param verificationMethod The verification method to validate * @param document The current DID document * @throws Error if validation fails */ validateVerificationMethod(did, verificationMethod, document) { if (!verificationMethod.id.startsWith(did)) { throw new Error(`Verification method ID ${verificationMethod.id} must start with DID ${did}`); } if (document.verificationMethod?.some((vm) => vm.id === verificationMethod.id)) { throw new Error(`Verification method ${verificationMethod.id} already exists`); } if (!verificationMethod.type) { throw new Error("Verification method must have a type"); } if (!verificationMethod.controller) { throw new Error("Verification method must have a controller"); } if (!verificationMethod.publicKeyMultibase && !verificationMethod.publicKeyJwk) { throw new Error("Verification method must have at least one form of public key material"); } } /** * Validates that inputs to addService are correct * * @param did The DID being operated on * @param service The service to validate * @param document The current DID document * @throws Error if validation fails */ validateService(did, service, document) { if (!service.id.startsWith(did)) { throw new Error(`Service ID ${service.id} must start with DID ${did}`); } if (document.service?.some((s) => s.id === service.id)) { throw new Error(`Service ${service.id} already exists`); } if (!service.type) { throw new Error("Service must have a type"); } if (!service.serviceEndpoint) { throw new Error("Service must have a serviceEndpoint"); } } /** * Makes a deep copy of a DID document for modification * * @param document The DID document to copy * @returns A deep copy of the document */ copyDocument(document) { return JSON.parse(JSON.stringify(document)); } }; // src/vdr/keyVDR.ts var logger2 = DebugLogger.get("KeyVDR"); var _KeyVDR = class _KeyVDR extends AbstractVDR { constructor() { super("key"); } /** * Resets the document cache - primarily for testing purposes * to ensure tests don't interfere with each other. */ reset() { _KeyVDR.documentCache.clear(); } /** * Override resolve to handle test mode */ async resolve(did) { try { if (_KeyVDR.documentCache.has(did)) { return _KeyVDR.documentCache.get(did); } return null; } catch (error) { logger2.error(`Error resolving ${did}:`, error); return null; } } /** * Add a verification method to a did:key document * For did:key, this is mostly a simulation as the document is derived from the key * This operation will update the local cache but not the actual structure of the did:key * * @param did The DID to update * @param verificationMethod The verification method to add * @param relationships Optional relationships to add the verification method to * @param options Additional options like keyId for signing * @returns Promise resolving to true if successful in updating the cache */ async addVerificationMethod(did, verificationMethod, relationships = [], options) { try { const originalDocument = await this.resolve(did); if (!originalDocument) { throw new Error(`DID ${did} not found`); } await this.validateUpdateOperation( did, originalDocument, options?.keyId, "capabilityDelegation" ); this.validateVerificationMethod(did, verificationMethod, originalDocument); if (originalDocument.verificationMethod?.some((vm) => vm.id === verificationMethod.id)) { throw new Error(`Verification method ${verificationMethod.id} already exists`); } if (!originalDocument.verificationMethod) { originalDocument.verificationMethod = []; } originalDocument.verificationMethod.push(verificationMethod); relationships.forEach((relationship) => { if (!originalDocument[relationship]) { originalDocument[relationship] = []; } if (!originalDocument[relationship].includes(verificationMethod.id)) { originalDocument[relationship].push(verificationMethod.id); } }); _KeyVDR.documentCache.set(did, originalDocument); return true; } catch (error) { logger2.error(`Error adding verification method to ${did}:`, error); throw error; } } /** * Remove a verification method from a did:key document * For did:key, this is mostly a simulation as the document is derived from the key * This operation will update the local cache but not the actual structure of the did:key * * @param did The DID to update * @param keyId The ID of the verification method to remove * @param options Additional options * @returns Promise resolving to true if successful in updating the cache */ async removeVerificationMethod(did, keyId, options) { try { const originalDocument = await this.resolve(did); if (!originalDocument) { throw new Error(`DID ${did} not found`); } await this.validateUpdateOperation( did, originalDocument, options?.keyId, "capabilityDelegation" ); const verificationMethods = originalDocument.verificationMethod || []; const vmIndex = verificationMethods.findIndex((vm) => vm.id === keyId); if (vmIndex === -1) { return true; } const isPrimaryKey = vmIndex === 0; if (isPrimaryKey) { throw new Error(`Cannot remove the primary key ${keyId} from did:key document`); } originalDocument.verificationMethod = verificationMethods.filter((vm) => vm.id !== keyId); const relationships = [ "authentication", "assertionMethod", "capabilityInvocation", "capabilityDelegation" ]; relationships.forEach((relationship) => { if (originalDocument[relationship]) { originalDocument[relationship] = originalDocument[relationship].filter( (id) => id !== keyId ); } }); _KeyVDR.documentCache.set(did, originalDocument); return true; } catch (error) { logger2.error(`Error removing verification method from ${did}:`, error); throw error; } } /** * Add a service to a did:key document * For did:key, this is mostly a simulation as the document is derived from the key * This operation will update the local cache but not the actual structure of the did:key * * @param did The DID to update * @param service The service to add * @param options Additional options * @returns Promise resolving to true if successful in updating the cache */ async addService(did, service, options) { try { const originalDocument = await this.resolve(did); if (!originalDocument) { throw new Error(`DID ${did} not found`); } await this.validateUpdateOperation( did, originalDocument, options?.keyId, "capabilityInvocation" ); this.validateService(did, service, originalDocument); if (!originalDocument.service) { originalDocument.service = []; } originalDocument.service.push(service); _KeyVDR.documentCache.set(did, originalDocument); return true; } catch (error) { logger2.error(`Error adding service to ${did}:`, error); throw error; } } /** * Remove a service from a did:key document * For did:key, this is mostly a simulation as the document is derived from the key * This operation will update the local cache but not the actual structure of the did:key * * @param did The DID to update * @param id The ID of the service to remove * @param options Additional options * @returns Promise resolving to true if successful in updating the cache */ async removeService(did, serviceId, options) { try { const originalDocument = await this.resolve(did); if (!originalDocument) { throw new Error(`DID ${did} not found`); } await this.validateUpdateOperation( did, originalDocument, options?.keyId, "capabilityInvocation" ); if (!originalDocument.service || !originalDocument.service.some((s) => s.id === serviceId)) { return true; } originalDocument.service = originalDocument.service.filter((s) => s.id !== serviceId); _KeyVDR.documentCache.set(did, originalDocument); return true; } catch (error) { logger2.error(`Error removing service from ${did}:`, error); throw error; } } /** * Override create method for did:key * For did:key, we can generate the DID from the public key */ async create(request) { try { if (!request.publicKeyMultibase || !request.publicKeyMultibase.startsWith("z")) { return { success: false, error: 'Invalid key format: publicKeyMultibase must start with "z"' }; } if (!request.preferredDID || !request.controller) { return { success: false, error: "Missing required parameters: preferredDID and controller" }; } const didDocument = this.buildDIDDocumentFromRequest(request); _KeyVDR.documentCache.set(request.preferredDID, didDocument); return { success: true, didDocument }; } catch (error) { logger2.error(`Error creating DID document:`, error); return { success: false, error: error instanceof Error ? error.message : "Unknown error" }; } } async createViaCADOP(request, _options) { let { keyType, publicKey } = DidKeyCodec.parseDidKey(request.userDidKey); try { const didCreationRequest = { publicKeyMultibase: MultibaseCodec.encodeBase58btc(publicKey), preferredDID: request.userDidKey, keyType, controller: request.userDidKey, initialRelationships: ["authentication", "capabilityDelegation"] }; const didDocument = this.buildDIDDocumentFromRequest(didCreationRequest); _KeyVDR.documentCache.set(didDocument.id, didDocument); return { success: true, didDocument }; } catch (error) { logger2.error(`Error creating DID document via CADOP:`, error); return { success: false, error: error instanceof Error ? error.message : "Unknown error" }; } } /** * Update verification relationships for a verification method */ async updateRelationships(did, keyId, add, remove, options) { try { const originalDocument = await this.resolve(did); if (!originalDocument) { throw new Error(`DID ${did} not found`); } await this.validateUpdateOperation( did, originalDocument, options?.keyId, "capabilityDelegation" ); const verificationMethod = originalDocument.verificationMethod?.find((vm) => vm.id === keyId); if (!verificationMethod) { throw new Error(`Verification method ${keyId} not found`); } remove.forEach((relationship) => { if (originalDocument[relationship]) { originalDocument[relationship] = originalDocument[relationship].filter( (id) => id !== keyId ); } }); add.forEach((relationship) => { if (!originalDocument[relationship]) { originalDocument[relationship] = []; } if (!originalDocument[relationship].includes(keyId)) { originalDocument[relationship].push(keyId); } }); _KeyVDR.documentCache.set(did, originalDocument); return true; } catch (error) { logger2.error(`Error updating relationships for ${did}:`, error); throw error; } } }; // In-memory cache of documents, shared across all instances _KeyVDR.documentCache = /* @__PURE__ */ new Map(); var KeyVDR = _KeyVDR; // src/vdr/roochVDR.ts import { RoochClient, Transaction as Transaction2, Args, getRoochNodeUrl as sdkGetRoochNodeUrl, Signer as Signer2 } from "@roochnetwork/rooch-sdk"; // src/crypto/providers/ed25519.ts function getCrypto() { if (typeof globalThis !== "undefined" && globalThis.crypto) { return globalThis.crypto; } throw new Error("Web Crypto API is not available in the current runtime"); } var Ed25519Provider = class { constructor() { this.crypto = getCrypto(); } async generateKeyPair() { const { publicKey, privateKey } = await this.crypto.subtle.generateKey( { name: "Ed25519" }, true, ["sign", "verify"] ); const exportedPublic = new Uint8Array(await this.crypto.subtle.exportKey("raw", publicKey)); const exportedPrivate = new Uint8Array(await this.crypto.subtle.exportKey("pkcs8", privateKey)); return { publicKey: exportedPublic, privateKey: exportedPrivate }; } async sign(data, privateKey) { let key; if (privateKey instanceof Uint8Array) { key = await this.crypto.subtle.importKey( "pkcs8", privateKey, { name: "Ed25519" }, false, ["sign"] ); } else { key = privateKey; } const signature = await this.crypto.subtle.sign("Ed25519", key, data); return new Uint8Array(signature); } async verify(data, signature, publicKey) { let key; if (publicKey instanceof Uint8Array) { key = await this.crypto.subtle.importKey( "raw", publicKey, { name: "Ed25519" }, false, ["verify"] ); } else { key = await this.crypto.subtle.importKey( "jwk", publicKey, { name: "Ed25519" }, false, ["verify"] ); } return await this.crypto.subtle.verify("Ed25519", key, signature, data); } getKeyType() { return KEY_TYPE.ED25519; } async derivePublicKey(privateKey) { const cryptoKey = await this.crypto.subtle.importKey( "pkcs8", privateKey, { name: "Ed25519" }, true, ["sign"] ); const jwk = await this.crypto.subtle.exportKey("jwk", cryptoKey); if (!jwk.x) { throw new Error("Failed to derive public key from private key"); } const publicKeyBytes = base64urlToBytes(jwk.x); return publicKeyBytes; } }; // src/crypto/providers/secp256k1.ts import { secp256k1 } from "@noble/curves/secp256k1"; import { sha256 } from "@noble/hashes/sha256"; var Secp256k1Provider = class { async generateKeyPair() { const privateKey = secp256k1.utils.randomPrivateKey(); const publicKey = secp256k1.getPublicKey(privateKey, true); return { publicKey, privateKey }; } async sign(data, privateKey) { if (privateKey instanceof CryptoKey) { throw new Error("CryptoKey is not supported for Secp256k1 signing"); } const msgHash = sha256(data); const signature = secp256k1.sign(msgHash, privateKey); return signature.toCompactRawBytes(); } async verify(data, signature, publicKey) { if (!(publicKey instanceof Uint8Array)) { throw new Error("JsonWebKey is not supported for Secp256k1 verification"); } const msgHash = sha256(data); return secp256k1.verify(signature, msgHash, publicKey); } getKeyType() { return KEY_TYPE.SECP256K1; } async derivePublicKey(privateKey) { return secp256k1.getPublicKey(privateKey, true); } }; // src/crypto/providers/ecdsa_r1.ts import { p256 } from "@noble/curves/p256"; import { bytesToHex, hexToBytes } from "@noble/curves/abstract/utils"; function getCrypto2() { if (typeof globalThis !== "undefined" && globalThis.crypto) { return globalThis.crypto; } throw new Error("Web Crypto API is not available in the current runtime"); } var EcdsaR1Provider = class { constructor() { this.crypto = getCrypto2(); } async generateKeyPair() { const { publicKey, privateKey } = await this.crypto.subtle.generateKey( { name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"] ); const exportedPublic = new Uint8Array(await this.crypto.subtle.exportKey("raw", publicKey)); const exportedPrivate = new Uint8Array(await this.crypto.subtle.exportKey("pkcs8", privateKey)); const compressedPublicKey = this.compressPublicKey(exportedPublic); return { publicKey: compressedPublicKey, privateKey: exportedPrivate }; } compressPublicKey(publicKey) { if (publicKey.length !== 65) { throw new Error(`Invalid public key length. Expected 65 bytes, got ${publicKey.length}`); } if (publicKey[0] !== 4) { throw new Error("Invalid public key format. Expected uncompressed format (0x04)"); } const x = publicKey.slice(1, 33); const y = publicKey.slice(33, 65); const compressedFormat = y[31] % 2 === 0 ? 2 : 3; const compressed = new Uint8Array(33); compressed[0] = compressedFormat; compressed.set(x, 1); return compressed; } decompressPublicKey(compressedKey) { if (compressedKey.length !== 33) { throw new Error( `Invalid compressed public key length. Expected 33 bytes, got ${compressedKey.length}` ); } const format = compressedKey[0]; if (format !== 2 && format !== 3) { throw new Error("Invalid compressed public key format. Expected 0x02 or 0x03"); } try { const point = p256.ProjectivePoint.fromHex(compressedKey); const x = point.x; const y = point.y; const xBytes = hexToBytes(x.toString(16).padStart(64, "0")); const yBytes = hexToBytes(y.toString(16).padStart(64, "0")); const decompressed = new Uint8Array(65); decompressed[0] = 4; decompressed.set(xBytes, 1); decompressed.set(yBytes, 33); return decompressed; } catch (err) { const error = err; throw new Error(`Failed to decompress public key: ${error.message}`); } } async sign(data, privateKey) { let key; if (privateKey instanceof Uint8Array) { key = await this.crypto.subtle.importKey( "pkcs8", privateKey, { name: "ECDSA", namedCurve: "P-256" }, false, ["sign"] ); } else { key = privateKey; } const signature = await this.crypto.subtle.sign( { name: "ECDSA", hash: { name: "SHA-256" } }, key, data ); return new Uint8Array(signature); } convertDERSignatureToRaw(derSignature) { const firstByte = derSignature[0]; if (firstByte === 48) { try { const sig = p256.Signature.fromDER(derSignature); return sig.toCompactRawBytes(); } catch (e) { throw new Error("Invalid DER signature"); } } return derSignature; } convertRawToDER(raw) { if (raw.length !== 64) return raw; const r = bytesToHex(raw.slice(0, 32)); const s = bytesToHex(raw.slice(32, 64)); const der = p256.Signature.fromCompact(hexToBytes(r + s)).toDERHex(); return hexToBytes(der); } async verify(data, signature, publicKey) { let key; if (publicKey instanceof Uint8Array) { const decompressedKey = this.decompressPublicKey(publicKey); key = await this.crypto.subtle.importKey( "raw", decompressedKey, { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"] ); } else { key = await this.crypto.subtle.importKey( "jwk", publicKey, { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"] ); } return await this.crypto.subtle.verify( { name: "ECDSA", hash: { name: "SHA-256" } }, key, signature, data ); } getKeyType() { return KEY_TYPE.ECDSAR1; } async derivePublicKey(privateKey) { const cryptoKey = await this.crypto.subtle.importKey( "pkcs8", privateKey, { name: "ECDSA", namedCurve: "P-256" }, true, ["sign"] ); const jwk = await this.crypto.subtle.exportKey("jwk", cryptoKey); if (!jwk.x || !jwk.y) { throw new Error("Failed to derive public key from private key"); } const x = base64urlToBytes(jwk.x); const y = base64urlToBytes(jwk.y); const uncompressed = new Uint8Array(65); uncompressed[0] = 4; uncompressed.set(x, 1); uncompressed.set(y, 33); return this.compressPublicKey(uncompressed); } }; // src/crypto/factory.ts var DefaultCryptoProviderFactory = class { constructor() { this.providers = /* @__PURE__ */ new Map(); this.providers.set(KEY_TYPE.ED25519, new Ed25519Provider()); this.providers.set(KEY_TYPE.SECP256K1, new Secp256k1Provider()); this.providers.set(KEY_TYPE.ECDSAR1, new EcdsaR1Provider()); } createProvider(keyType) { const provider = this.providers.get(keyType); if (!provider) { throw new Error(`No provider available for key type: ${keyType}`); } return provider; } supports(keyType) { return this.providers.has(keyType); } }; var defaultCryptoProviderFactory = new DefaultCryptoProviderFactory(); // src/crypto/utils.ts var CryptoUtils = class { /** * Generates a key pair based on the specified curve * @param type The key type to generate (Ed25519VerificationKey2020 or EcdsaSecp256k1VerificationKey2019 or EcdsaSecp256r1VerificationKey2019) * @returns A key pair containing public and private keys */ static async generateKeyPair(type = "Ed25519VerificationKey2020" /* ED25519 */) { const keyType = typeof type === "string" ? toKeyType(type) : type; const provider = defaultCryptoProviderFactory.createProvider(keyType); return provider.generateKeyPair(); } /** * Signs data u