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,570 lines (1,548 loc) 176 kB
// src/testHelpers/env.ts import { RoochClient as RoochClient2 } from "@roochnetwork/rooch-sdk"; // 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/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/errors/IdentityKitError.ts var IdentityKitError = class extends Error { constructor(code, message, options) { super(message); this.name = "IdentityKitError"; this.code = code; this.category = options?.category || this.inferCategory(code); this.details = options?.details; this.cause = options?.cause; if (Error.captureStackTrace) { Error.captureStackTrace(this, this.constructor); } if (options?.cause) { this.stack = `${this.stack} Caused by: ${options.cause.stack}`; } } /** * Infer error category from error code */ inferCategory(code) { if (code.startsWith("AUTH_")) return "authentication"; if (code.startsWith("DID_")) return "did"; if (code.startsWith("VDR_")) return "vdr"; if (code.startsWith("KEY_")) return "key-management"; if (code.startsWith("WEB_")) return "web"; if (code.startsWith("REACT_")) return "react"; if (code.startsWith("CRYPTO_")) return "crypto"; if (code.startsWith("MULTIBASE_")) return "multibase"; if (code.startsWith("SCOPE_") || code.startsWith("VALIDATION_")) return "validation"; if (code.startsWith("DEEPLINK_")) return "deeplink"; if (code.startsWith("STORAGE_")) return "storage"; if (code.startsWith("SIGNER_")) return "signer"; if (code.includes("SIGNING") || code.includes("SIGNATURE")) return "signing"; if (code.includes("NETWORK")) return "network"; if (code.includes("PERMISSION")) return "permission"; return "general"; } /** * Convert to a plain object for serialization */ toJSON() { return { name: this.name, code: this.code, category: this.category, message: this.message, details: this.details, stack: this.stack }; } /** * Get a user-friendly error message with suggestions */ getUserMessage() { const suggestions = this.getSuggestions(); let message = this.message; if (suggestions.length > 0) { message += "\n\nSuggestions:\n" + suggestions.map((s) => `\u2022 ${s}`).join("\n"); } return message; } /** * Get contextual suggestions based on error code */ getSuggestions() { switch (this.code) { case "WEB_BROWSER_NOT_SUPPORTED" /* WEB_BROWSER_NOT_SUPPORTED */: return [ "Use a modern browser that supports required Web APIs", "Check if you're running in a browser environment" ]; case "WEB_STORAGE_NOT_AVAILABLE" /* WEB_STORAGE_NOT_AVAILABLE */: return [ "Enable localStorage or IndexedDB in your browser", "Check if you're in private/incognito mode", "Consider using memory storage as fallback" ]; case "DID_METHOD_NOT_SUPPORTED" /* DID_METHOD_NOT_SUPPORTED */: return [ "Check if the DID method is registered with VDRRegistry", "Verify the DID format is correct" ]; case "VDR_NETWORK_ERROR" /* VDR_NETWORK_ERROR */: return [ "Check your network connection", "Verify the RPC URL is correct and accessible", "Check if the VDR service is running" ]; case "KEY_STORAGE_ERROR" /* KEY_STORAGE_ERROR */: return [ "Check browser storage permissions", "Verify storage quota is not exceeded", "Try clearing browser storage and retry" ]; case "REACT_NOT_AVAILABLE" /* REACT_NOT_AVAILABLE */: return [ "Ensure React is properly installed and imported", "Check if you're using the hook in a React component" ]; case "CRYPTO_PROVIDER_NOT_FOUND" /* CRYPTO_PROVIDER_NOT_FOUND */: return [ "Check if the key type is supported", "Verify the crypto provider factory configuration" ]; case "MULTIBASE_DECODE_FAILED" /* MULTIBASE_DECODE_FAILED */: return [ "Verify the encoded string format is correct", "Check if the multibase prefix is valid", "Ensure the input is not corrupted" ]; case "SCOPE_VALIDATION_FAILED" /* SCOPE_VALIDATION_FAILED */: return [ "Check the scope format: address::module::function", "Verify the address format is valid", "Ensure module and function names are correct" ]; case "DEEPLINK_INVALID_STATE" /* DEEPLINK_INVALID_STATE */: return [ "Check if the state parameter matches the stored value", "Verify the callback URL parameters are correct", "Ensure the session storage is available" ]; case "STORAGE_QUOTA_EXCEEDED" /* STORAGE_QUOTA_EXCEEDED */: return [ "Clear unused data from browser storage", "Check available storage quota", "Consider using a different storage strategy" ]; case "SIGNER_CONVERSION_FAILED" /* SIGNER_CONVERSION_FAILED */: return [ "Verify the signer implements the required interface", "Check if the key ID is available in the signer", "Ensure the DID account signer is properly configured" ]; default: return []; } } }; function createDIDError(code, message, details, cause) { return new IdentityKitError(code, message, { category: "did", details, cause }); } function createVDRError(code, message, details, cause) { return new IdentityKitError(code, message, { category: "vdr", details, cause }); } function createKeyManagementError(code, message, details, cause) { return new IdentityKitError(code, message, { category: "key-management", details, cause }); } function createCryptoError(code, message, details, cause) { return new IdentityKitError(code, message, { category: "crypto", details, cause }); } function createMultibaseError(code, message, details, cause) { return new IdentityKitError(code, message, { category: "multibase", details, cause }); } function createValidationError(code, message, details, cause) { return new IdentityKitError(code, message, { category: "validation", details, cause }); } function createSignerError(code, message, details, cause) { return new IdentityKitError(code, message, { category: "signer", details, cause }); } // 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 createVDRError( "VDR_NOT_AVAILABLE" /* VDR_NOT_AVAILABLE */, `No VDR available for method: ${method}`, { method, availableMethods: Array.from(this.vdrs.keys()) } ); } 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 createVDRError( "VDR_NOT_AVAILABLE" /* VDR_NOT_AVAILABLE */, `No VDR available for method: ${method}`, { method, availableMethods: Array.from(this.vdrs.keys()) } ); } 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 createVDRError( "VDR_NOT_AVAILABLE" /* VDR_NOT_AVAILABLE */, `No VDR available for method: ${method}`, { method, availableMethods: Array.from(this.vdrs.keys()) } ); } const result = await vdr.createViaCADOP(creationRequest, options); if (result.success && result.didDocument) { this.cache.set(result.didDocument.id, result.didDocument); } return result; } async createDIDViaCADOPWithController(method, creationRequest, options) { const vdr = this.vdrs.get(method); if (!vdr) { throw createVDRError( "VDR_NOT_AVAILABLE" /* VDR_NOT_AVAILABLE */, `No VDR available for method: ${method}`, { method, availableMethods: Array.from(this.vdrs.keys()) } ); } const result = await vdr.createViaCADOPWithController(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 createVDRError( "VDR_NOT_AVAILABLE" /* VDR_NOT_AVAILABLE */, `No VDR available for method: ${method}`, { method, availableMethods: Array.from(this.vdrs.keys()) } ); } if (this.cache.has(did)) { const doc = this.cache.get(did); return doc !== null; } const exists = await vdr.exists(did); return exists; } }; // src/vdr/roochVDR.ts import { RoochClient, Transaction as Transaction2, Args, getRoochNodeUrl as sdkGetRoochNodeUrl } from "@roochnetwork/rooch-sdk"; // 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 createValidationError( "ENVIRONMENT_NOT_SUPPORTED" /* ENVIRONMENT_NOT_SUPPORTED */, "No TextEncoder or Buffer available in this environment.", { environment: typeof globalThis, textEncoderAvailable: typeof TextEncoder !== "undefined", bufferAvailable: typeof Buffer !== "undefined" } ); } 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 createValidationError( "ENVIRONMENT_NOT_SUPPORTED" /* ENVIRONMENT_NOT_SUPPORTED */, "No TextDecoder or Buffer available in this environment.", { environment: typeof globalThis, textDecoderAvailable: typeof TextDecoder !== "undefined", bufferAvailable: typeof Buffer !== "undefined" } ); } 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; } // 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 createMultibaseError( "MULTIBASE_ENCODE_FAILED" /* MULTIBASE_ENCODE_FAILED */, `Unsupported multibase: ${base}`, { base, supportedBases: Object.keys(ENCODER_MAP) } ); } 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 createMultibaseError( "MULTIBASE_DECODE_FAILED" /* MULTIBASE_DECODE_FAILED */, `Unsupported multibase prefix: ${prefix}`, { prefix, supportedPrefixes: ["z", "M", "m", "u", "U", "f"] } ); } } }; // 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 createValidationError( "KEY_TYPE_NOT_SUPPORTED" /* KEY_TYPE_NOT_SUPPORTED */, `Invalid key type: ${value}`, { value, supportedTypes: Object.values(KeyType) } ); } function roochSignatureSchemeToKeyType(scheme) { if (scheme === "Secp256k1") { return "EcdsaSecp256k1VerificationKey2019" /* SECP256K1 */; } else if (scheme === "ED25519") { return "Ed25519VerificationKey2020" /* ED25519 */; } else if (scheme === "EcdsaR1") { return "EcdsaSecp256r1VerificationKey2019" /* ECDSAR1 */; } throw createValidationError( "KEY_TYPE_NOT_SUPPORTED" /* KEY_TYPE_NOT_SUPPORTED */, `Unsupported Rooch signature scheme: ${scheme}`, { scheme, supportedSchemes: ["Secp256k1", "ED25519", "EcdsaR1"] } ); } function keyTypeToRoochSignatureScheme(keyType) { if (keyType === "EcdsaSecp256k1VerificationKey2019" /* SECP256K1 */) { return "Secp256k1"; } else if (keyType === "Ed25519VerificationKey2020" /* ED25519 */) { return "ED25519"; } else if (keyType === "EcdsaSecp256r1VerificationKey2019" /* ECDSAR1 */) { return "EcdsaR1"; } throw createValidationError( "KEY_TYPE_NOT_SUPPORTED" /* KEY_TYPE_NOT_SUPPORTED */, `Unsupported key type: ${keyType}`, { keyType, supportedTypes: Object.values(KeyType) } ); } // 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 createValidationError( "INVALID_INPUT_FORMAT" /* INVALID_INPUT_FORMAT */, `Invalid key length for ${keyType}. Expected ${expectedLength} bytes, got ${bytes.length}`, { keyType, expectedLength, actualLength: 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 createMultibaseError( "MULTIBASE_DECODE_FAILED" /* MULTIBASE_DECODE_FAILED */, "Invalid key format: too short", { encoded, minimumLength: 2, actualLength: decoded.length } ); } const keyType = this.extractKeyType(decoded); const bytes = this.extractBytes(decoded); const expectedLength = this.getExpectedKeyLength(keyType); if (bytes.length !== expectedLength) { throw createValidationError( "INVALID_INPUT_FORMAT" /* INVALID_INPUT_FORMAT */, `Invalid key length for ${keyType}. Expected ${expectedLength} bytes, got ${bytes.length}`, { keyType, expectedLength, actualLength: bytes.length } ); } return { keyType, bytes }; } catch (error) { if (error instanceof Error && error.message === "Non-base58btc character") { throw createMultibaseError( "MULTIBASE_DECODE_FAILED" /* MULTIBASE_DECODE_FAILED */, "Invalid multibase format", { encoded } ); } 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 createValidationError( "KEY_TYPE_NOT_SUPPORTED" /* KEY_TYPE_NOT_SUPPORTED */, `Unsupported key type: ${keyType}`, { keyType, supportedTypes: [KEY_TYPE.ED25519, KEY_TYPE.SECP256K1, KEY_TYPE.ECDSAR1] } ); } } 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 createMultibaseError( "MULTIBASE_DECODE_FAILED" /* MULTIBASE_DECODE_FAILED */, "Unknown key type prefix", { prefix: Array.from(prefixedBytes.slice(0, 2)) } ); } 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 createValidationError( "KEY_TYPE_NOT_SUPPORTED" /* KEY_TYPE_NOT_SUPPORTED */, `Unsupported key type: ${keyType}`, { keyType, supportedTypes: [KEY_TYPE.ED25519, KEY_TYPE.SECP256K1, KEY_TYPE.ECDSAR1] } ); } } }; 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 createDIDError("DID_INVALID_FORMAT" /* DID_INVALID_FORMAT */, "Invalid did:key format", { didKey, expectedPrefix: "did:key:" }); } const multibase = didKey.substring(8); const { keyType, bytes } = KeyMultibaseCodec.decodeWithType(multibase); return { keyType, publicKey: bytes }; } }; // src/crypto/providers/ed25519.ts function getCrypto() { if (typeof globalThis !== "undefined" && globalThis.crypto) { return globalThis.crypto; } throw createCryptoError( "ENVIRONMENT_NOT_SUPPORTED" /* ENVIRONMENT_NOT_SUPPORTED */, "Web Crypto API is not available in the current runtime", { environment: typeof globalThis, cryptoAvailable: false } ); } var Ed25519Provider = class { constructor() { this.crypto = getCrypto(); } async generateKeyPair() { const generated = await this.crypto.subtle.generateKey( { name: "Ed25519" }, true, ["sign", "verify"] ); if (!("publicKey" in generated)) { throw createCryptoError( "CRYPTO_OPERATION_FAILED" /* CRYPTO_OPERATION_FAILED */, "Ed25519 generateKey did not return a key pair", { keyType: "Ed25519", operation: "generateKeyPair" } ); } const { publicKey, privateKey } = generated; 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 createCryptoError( "CRYPTO_KEY_DERIVATION_FAILED" /* CRYPTO_KEY_DERIVATION_FAILED */, "Failed to derive public key from private key", { keyType: "Ed25519", operation: "derivePublicKey" } ); } 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 createCryptoError( "OPERATION_NOT_SUPPORTED" /* OPERATION_NOT_SUPPORTED */, "CryptoKey is not supported for Secp256k1 signing", { keyType: "Secp256k1", operation: "sign", privateKeyType: "CryptoKey" } ); } const msgHash = sha256(data); const signature = secp256k1.sign(msgHash, privateKey); return signature.toCompactRawBytes(); } async verify(data, signature, publicKey) { if (!(publicKey instanceof Uint8Array)) { throw createCryptoError( "OPERATION_NOT_SUPPORTED" /* OPERATION_NOT_SUPPORTED */, "JsonWebKey is not supported for Secp256k1 verification", { keyType: "Secp256k1", operation: "verify", publicKeyType: "JsonWebKey" } ); } 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 createCryptoError( "ENVIRONMENT_NOT_SUPPORTED" /* ENVIRONMENT_NOT_SUPPORTED */, "Web Crypto API is not available in the current runtime", { environment: typeof globalThis, cryptoAvailable: false } ); } var EcdsaR1Provider = class { constructor() { this.crypto = getCrypto2(); } async generateKeyPair() { const generated = await this.crypto.subtle.generateKey( { name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"] ); if (!("publicKey" in generated)) { throw createCryptoError( "CRYPTO_OPERATION_FAILED" /* CRYPTO_OPERATION_FAILED */, "ECDSA P-256 generateKey did not return a key pair", { keyType: "ECDSA-R1", operation: "generateKeyPair" } ); } const { publicKey, privateKey } = generated; 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 createValidationError( "INVALID_INPUT_FORMAT" /* INVALID_INPUT_FORMAT */, `Invalid public key length. Expected 65 bytes, got ${publicKey.length}`, { expectedLength: 65, actualLength: publicKey.length, keyType: "ECDSA-R1" } ); } if (publicKey[0] !== 4) { throw createValidationError( "INVALID_INPUT_FORMAT" /* INVALID_INPUT_FORMAT */, "Invalid public key format. Expected uncompressed format (0x04)", { actualPrefix: publicKey[0], expectedPrefix: 4, keyType: "ECDSA-R1" } ); } 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 createValidationError( "INVALID_INPUT_FORMAT" /* INVALID_INPUT_FORMAT */, `Invalid compressed public key length. Expected 33 bytes, got ${compressedKey.length}`, { expectedLength: 33, actualLength: compressedKey.length, keyType: "ECDSA-R1" } ); } const format = compressedKey[0]; if (format !== 2 && format !== 3) { throw createValidationError( "INVALID_INPUT_FORMAT" /* INVALID_INPUT_FORMAT */, "Invalid compressed public key format. Expected 0x02 or 0x03", { actualPrefix: compressedKey[0], expectedPrefixes: [2, 3], keyType: "ECDSA-R1" } ); } 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 createCryptoError( "CRYPTO_OPERATION_FAILED" /* CRYPTO_OPERATION_FAILED */, `Failed to decompress public key: ${error.message}`, { keyType: "ECDSA-R1", operation: "decompressPublicKey", originalError: 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 createValidationError( "INVALID_INPUT_FORMAT" /* INVALID_INPUT_FORMAT */, "Invalid DER signature", { keyType: "ECDSA-R1", operation: "parseDERSignature" } ); } } 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 createCryptoError( "CRYPTO_KEY_DERIVATION_FAILED" /* CRYPTO_KEY_DERIVATION_FAILED */, "Failed to derive public key from private key", { keyType: "ECDSA-R1", operation: "derivePublicKey" } ); } 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 createCryptoError( "CRYPTO_PROVIDER_NOT_FOUND" /* CRYPTO_PROVIDER_NOT_FOUND */, `No provider available for key type: ${keyType}`, { keyType, availableTypes: Array.from(this.providers.keys()) } ); } 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 using the specified private key * @param data The data to sign * @param privateKey The private key to use for signing (can be Uint8Array or CryptoKey) * @param type The key type (Ed25519VerificationKey2020 or EcdsaSecp256k1VerificationKey2019) * @returns The signature as a Uint8Array */ static async sign(data, privateKey, type) { const keyType = typeof type === "string" ? toKeyType(type) : type; const provider = defaultCryptoProviderFactory.createProvider(keyType); return provider.sign(data, privateKey); } /** * Verifies a signature using the specified public key * @param data The original data * @param signature The signature to verify * @param publicKey The public key to use for verification (can be Uint8Array or JsonWebKey) * @param type The key type (Ed25519VerificationKey2020 or EcdsaSecp256k1VerificationKey2019) * @returns Whether the signature is valid */ static async verify(data, signature, publicKey, type) { const keyType = typeof type === "string" ? toKeyType(type) : type; const provider = defaultCryptoProviderFactory.createProvider(keyType); return provider.verify(data, signature, publicKey); } /** * Derive public key from private key * @param privateKey The private key bytes * @param keyType The key type * @returns The corresponding public key bytes */ static async derivePublicKey(privateKey, keyType) { const type = typeof keyType === "string" ? toKeyType(keyType) : keyType; const provider = defaultCryptoProviderFactory.createProvider(type); return provider.derivePublicKey(privateKey); } /** * Validate the consistency between a private key and public key pair * @param privateKey The private key bytes * @param publicKey The public key bytes * @param keyType The key type * @returns true if the keys are consistent, false otherwise */ static async validateKeyPairConsistency(privateKey, publicKey, keyType) { try { const derivedPublicKey = await this.derivePublicKey(privateKey, keyType); return this.areUint8ArraysEqual(derivedPublicKey, publicKey); } catch (error) { console.warn("Key pair consistency validation failed:", error); return false; } } /** * Compare two Uint8Array for equality * @param a First array * @param b Second array * @returns true if arrays are equal, false otherwise */ static areUint8ArraysEqual(a, b) { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { if (a[i] !== b[i]) return false; } return true; } }; // src/crypto/EcdsaR1PublicKey.ts import { p256 as p2562 } from "@noble/curves/p256"; import { RoochAddress, PublicKey, SIGNATURE_SCHEME_TO_FLAG, blake2b, fromB64 } from "@roochnetwork/rooch-sdk"; var PUBLIC_KEY_SIZE = 33; var EcdsaR1PublicKey = class extends PublicKey { /** * Create a new EcdsaR1PublicKey object * @param value ECDSA R1 public key as buffer or base-64 encoded string */ constructor(value) { super(); if (typeof value === "string") { this.data = fromB64(value); } else if (value instanceof Uint8Array) { this.data = value; } else { this.data = Uint8Array.from(value); } if (this.data.length !== PUBLIC_KEY_SIZE) { throw createValidationError( "INVALID_INPUT_FORMAT" /* INVALID_INPUT_FORMAT */, `Invalid public key input. Expected ${PUBLIC_KEY_SIZE} bytes, got ${this.data.length}`, { expectedSize: PUBLIC_KEY_SIZE, actualSize: this.data.length, keyType: "ECDSA-R1" } ); } } /** * Checks if two EcdsaR1 public keys are equal */ equals(publicKey) { return super.equals(publicKey); } /** * Return the byte array representation of the ECDSA R1 public key */ toBytes() { return this.data; } /** * Return the signature scheme flag for ECDSA R1 */ flag() { return SIGNATURE_SCHEME_TO_FLAG.EcdsaR1; } /** * Verifies that the signature is valid for the provided message */ async verify(message, signature) { try { return p2562.verify(signature, message, this.toBytes()); } catch (error) { return false; } } /** * Return the Rooch address associated with this ECDSA R1 public key */ toAddress() { const tmp = new Uint8Array(PUBLIC_KEY_SIZE + 1); tmp.set([SIGNATURE_SCHEME_TO_FLAG.EcdsaR1]); tmp.set(this.toBytes(), 1); const ROOCH_ADDRESS_LENGTH = 32; return new RoochAddress(blake2b(tmp, { dkLen: 32 }).slice(0, ROOCH_ADDRESS_LENGTH * 2)); } }; EcdsaR1PublicKey.SIZE = PUBLIC_KEY_SIZE; // src/signers/keyStoreUtils.ts async function signWithKeyStore(keyStore, data, keyId) { if (typeof keyStore.sign === "function") { return keyStore.sign(keyId, data); } const key = await keyStore.load(keyId); if (!key) { throw createKeyManagementError("KEY_NOT_FOUND" /* KEY_NOT_FOUND */, `Key not found: ${keyId}`, { keyId }); } if (!key.privateKeyMultibase) { throw createKeyManagementError( "KEY_PRIVATE_KEY_NOT_AVAILABLE" /* KEY_PRIVATE_KEY_NOT_AVAILABLE */, `No private key available for ${keyId}`, { keyId } ); } const privateKeyBytes = MultibaseCodec.decode(key.privateKeyMultibase); return CryptoUtils.sign(data, privateKeyBytes, key.keyType); } async function canSignWithKeyStore(keyStore, keyId) { if (typeof keyStore.sign === "function") { const keyExists = await keyStore.load(keyId); return keyExists !== null; } const key = await keyStore.load(keyId); return !!(key && key.privateKeyMultibase); } async function getKeyInfoFromKeyStore(keyStore, keyId) { const key = await keyStore.load(keyId); if (!key) return void 0; const publicKeyBytes = MultibaseCodec.decode(key.publicKeyMultibase); return { type: key.keyType, publicKey: publicKeyBytes }; } // src/signers/types.ts function isSignerInterface(obj) { return obj && typeof obj === "object" && typeof obj.listKeyIds === "function" && typeof obj.signWithKeyId === "function" && typeof obj.canSignWithKeyId === "function" && typeof obj.getDid === "function" && typeof obj.getKeyInfo === "function" && // Check that it's NOT a Signer (which has different methods) typeof obj.sign !== "function"; } // src/signers/didAccountSigner.ts import { RoochAddress as RoochAddress2, Signer, Authenticator, Ed25519PublicKey, Secp256k1PublicKey } from "@roochnetwork/rooch-sdk"; // src/utils/did.ts function parseDid(did) { if (!did.startsWith("did:")) { throw createDIDError("DID_INVALID_FORMAT" /* DID_INVALID_FORMAT */, `Invalid DID: ${did}`, { did, reason: 'DID must start with "did:"' }); } const afterPrefix = did.slice(4); const methodEnd = afterPrefix.indexOf(":"); if (methodEnd === -1) { throw createDIDError( "DID_INVALID_FORMAT" /* DID_INVALID_FORMAT */, `Invalid DID \u2013 missing method/identifier separator: ${did}`, { did, reason: 'Missing method/identifier separator ":"' } ); } const method = afterPrefix.slice(0, methodEnd); const idPlusFrag = afterPrefix.slice(methodEnd + 1); if (!method || !idPlusFrag) { throw createDIDError("DID_INVALID_FORMAT" /* DID_INVALID_FORMAT */, `Invalid DID: ${did}`, { did, reason: "Empty method or identifier" }); } 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 extractFragment(idOrDid) { const idx = idOrDid.indexOf("#"); if (idx === -1) { throw createDIDError( "DID_INVALID_FORMAT" /* DID_INVALID_FORMAT */, `No fragment found in ${idOrDid}`, { idOrDid, reason: 'Missing fragment separator "#"' } ); } return idOrDid.slice(idx + 1); } var extractFragmentFromId = extractFragment; function buildDid(method, identifier) { return `did:${method}:${identifier}`; } function getDidWithoutFragment(did) { const { method, identifier } = parseDid(did); return buildDid(method, identifier); } // src/signers/didAccountSigner.ts var DidAccountSigner = class _DidAccountSigner extends Signer { constructor(wrappedSigner, did, keyId, keyType, publicKey) { super(); this.wrappedSigner = wrappedSigner; this.__brandDidAccountSigner = true; this.keyId = keyId; this.did = did; if (!this.did.startsWith("did:rooch:")) { throw createSignerError( "SIGNER_INVALID_DID" /* SIGNER_INVALID_DID */, "Signer DID must be a did:rooch DID", { did: this.did, expectedMethod: "rooch" } ); } const didParts = parseDid(did); this.didAddress = new RoochAddress2(didParts.identifier); this.keyType = keyType; this.publicKey = publicKey; } /** * Create a DidAccountSigner instance from a SignerInterface * @param signer The signer to wrap * @param keyId Optional specific keyId to use * @returns A new DidAccountSigner instance */ /** * Check if an object is a DidAccountSigner instance * Safe to use instanceof within the same module */ static isDidAccountSigner(obj) { return obj i