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,557 lines (1,532 loc) 164 kB
#!/usr/bin/env node "use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // package.json var require_package = __commonJS({ "package.json"(exports2, module2) { module2.exports = { name: "@nuwa-ai/identity-kit", version: "0.8.1", description: "SDK for NIP-1 Agent Single DID Multi-Key Model and NIP-3 CADOP (Custodian-Assisted DID Onboarding Protocol)", type: "module", main: "./dist/index.cjs", module: "./dist/index.js", types: "./dist/index.d.ts", bin: { "nuwa-id": "./dist/cli/index.cjs" }, exports: { ".": { types: "./dist/index.d.ts", import: "./dist/index.js", require: "./dist/index.cjs" }, "./web": { types: "./dist/web/index.d.ts", import: "./dist/web/index.js", require: "./dist/web/index.cjs" }, "./testHelpers": { types: "./dist/testHelpers/index.d.ts", import: "./dist/testHelpers/index.js", require: "./dist/testHelpers/index.cjs" } }, scripts: { dev: "tsup --watch", build: "tsup && chmod +x dist/cli/index.cjs dist/cli/index.js", test: "jest --config=jest.config.json", "test:coverage": "jest --config=jest.config.json --coverage", "test:watch": "jest --config=jest.config.json --watch", clean: "rimraf dist", lint: "eslint . --ext .ts", "lint:fix": "eslint . --ext .ts --fix", format: 'prettier --write "src/**/*.ts" "test/**/*.ts"', "format:check": 'prettier --check "src/**/*.ts" "test/**/*.ts"', prepublishOnly: "pnpm run clean && pnpm run build" }, files: [ "dist/**/*" ], keywords: [ "did", "identity", "web3", "nuwa", "nip-1", "nip-3", "cadop", "custodian", "onboarding", "web", "browser" ], peerDependencies: { react: "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, peerDependenciesMeta: { react: { optional: true } }, author: "Nuwa Community", license: "Apache-2.0", repository: { type: "git", url: "git+https://github.com/nuwa-protocol/nuwa.git", directory: "nuwa-kit/typescript/packages/identity-kit" }, bugs: { url: "https://github.com/nuwa-protocol/nuwa/issues" }, homepage: "https://github.com/nuwa-protocol/nuwa/tree/main/nuwa-kit/typescript/packages/identity-kit#readme", devDependencies: { "@jest/globals": "^29.7.0", "@types/jest": "^29.5.14", "@types/node": "^20.10.0", "@types/react": "^18.0.0", "@typescript-eslint/eslint-plugin": "^7.3.1", "@typescript-eslint/parser": "^7.3.1", eslint: "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", jest: "^29.7.0", "jest-environment-jsdom": "^30.0.5", jsdom: "^26.1.0", prettier: "^3.2.5", rimraf: "^5.0.5", "ts-jest": "29.1.1", tsup: "^8.2.3", typescript: "^5.3.2" }, dependencies: { "@noble/curves": "^1.9.1", "@noble/hashes": "^1.8.0", "@roochnetwork/rooch-sdk": "^0.4.0", multiformats: "^9.9.0" }, tsup: { entry: [ "src/index.ts", "src/web/index.ts", "src/testHelpers/index.ts", "src/cli/index.ts" ], format: [ "esm", "cjs" ], dts: true, splitting: false, sourcemap: true, clean: true, external: [ "react" ] } }; } }); // src/cli/index.ts var cli_exports = {}; __export(cli_exports, { parseArgs: () => parseArgs }); module.exports = __toCommonJS(cli_exports); var import_promises2 = require("fs/promises"); var import_path2 = __toESM(require("path"), 1); // src/cli/args.ts function parseArgs(argv) { if (argv.length === 0) return { options: {} }; const [first, ...tail] = argv; if (first === "-h" || first === "--help") { return { command: "help", options: {} }; } if (first === "-v" || first === "--version") { return { command: "version", options: {} }; } if (first.startsWith("-")) { throw new Error(`Unknown flag: ${first}`); } let command = first; let rest = tail; if (first === "profile") { const action = tail[0]; if (!action || action.startsWith("--")) { throw new Error("profile command requires subcommand: list | use | create"); } command = `profile:${action}`; rest = tail.slice(1); } const options = {}; for (let i = 0; i < rest.length; i++) { const token = rest[i]; if (!token.startsWith("-")) continue; if (token.startsWith("-") && !token.startsWith("--")) { const flag = token.slice(1); if (flag === "h") { addOption(options, "help", true); continue; } if (flag === "v") { addOption(options, "version", true); continue; } throw new Error(`Unknown flag: -${flag}`); } const [rawKey, inlineValue] = token.slice(2).split("=", 2); if (inlineValue !== void 0) { addOption(options, rawKey, inlineValue); continue; } const next = rest[i + 1]; if (!next || next.startsWith("--")) { addOption(options, rawKey, true); continue; } addOption(options, rawKey, next); i += 1; } return { command, options }; } function addOption(options, key, value) { const existing = options[key]; if (existing === void 0) { options[key] = value; return; } if (Array.isArray(existing)) { existing.push(String(value)); options[key] = existing; return; } options[key] = [String(existing), String(value)]; } // src/cli-lib/config.ts var import_promises = require("fs/promises"); var import_os = __toESM(require("os"), 1); var import_path = __toESM(require("path"), 1); // src/cli-lib/types.ts var DEFAULT_PROFILE_NAME = "default"; function makeDefaultConfig() { return makeSingleProfileConfig({ name: DEFAULT_PROFILE_NAME, network: "main", cadopDomain: "https://id.nuwa.dev", keyFragment: "agent-auth-1" }); } function makeSingleProfileConfig(input) { return { version: 2, activeProfile: input.name, profiles: { [input.name]: { did: input.did, network: input.network, roochRpcUrl: input.roochRpcUrl, cadopDomain: input.cadopDomain, keyFragment: input.keyFragment, keyFile: `keys/${input.name}.json` } } }; } // src/cli-lib/config.ts function resolveCliDir() { return import_path.default.join(import_os.default.homedir(), ".config", "nuwa-did"); } function getCliPaths() { const dir = resolveCliDir(); return { dir, configFile: import_path.default.join(dir, "config.json"), keysDir: import_path.default.join(dir, "keys") }; } async function ensureCliDir() { const paths = getCliPaths(); await (0, import_promises.mkdir)(paths.dir, { recursive: true, mode: 448 }); await (0, import_promises.mkdir)(paths.keysDir, { recursive: true, mode: 448 }); return paths; } async function loadConfig() { const paths = await ensureCliDir(); try { const raw = await (0, import_promises.readFile)(paths.configFile, "utf8"); try { return normalizeConfig(JSON.parse(raw)); } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error( `invalid nuwa-id config at ${paths.configFile}: ${message}. Delete the file and run \`nuwa-id init\` to recreate it.` ); } } catch (error) { const err = error; if (!err?.code || err.code !== "ENOENT") { throw error; } return makeDefaultConfig(); } } async function saveConfig(config) { const paths = await ensureCliDir(); await (0, import_promises.writeFile)(paths.configFile, JSON.stringify(config, null, 2), { encoding: "utf8", mode: 384 }); } async function keyExists() { const config = await loadConfig(); const keyFile = resolveProfileKeyFile(config, config.activeProfile); try { await (0, import_promises.stat)(keyFile); return true; } catch { return false; } } async function loadKeyMaterial(profileName) { const config = await loadConfig(); const keyFile = resolveProfileKeyFile(config, profileName || config.activeProfile); let raw; try { raw = await (0, import_promises.readFile)(keyFile, "utf8"); } catch (error) { const err = error; if (err?.code === "ENOENT") { throw new Error("agent key not initialized; run `nuwa-id init`"); } throw error; } try { return JSON.parse(raw); } catch { throw new Error(`invalid key file at ${keyFile}`); } } async function saveKeyMaterial(key, profileName) { const config = await loadConfig(); const keyFile = resolveProfileKeyFile(config, profileName || config.activeProfile); await saveKeyMaterialToPath(key, keyFile); } async function saveKeyMaterialWithRelativePath(key, keyFileRelativePath) { const keyFile = resolveSafeKeyPath(keyFileRelativePath); await saveKeyMaterialToPath(key, keyFile); } async function saveKeyMaterialToPath(key, keyFile) { const paths = await ensureCliDir(); await (0, import_promises.mkdir)(import_path.default.dirname(keyFile), { recursive: true, mode: 448 }); await (0, import_promises.writeFile)(keyFile, JSON.stringify(key, null, 2), { encoding: "utf8", mode: 384 }); } function getActiveProfile(config) { const profile = config.profiles[config.activeProfile]; if (!profile) { throw new Error(`active profile not found: ${config.activeProfile}`); } return { name: config.activeProfile, profile }; } function updateActiveProfile(config, updater) { const active = getActiveProfile(config); return { ...config, profiles: { ...config.profiles, [active.name]: updater(active.profile) } }; } function normalizeConfig(input) { if (input.version !== 2) { throw new Error("invalid config version"); } const activeProfile = input.activeProfile; if (!activeProfile || typeof activeProfile !== "string") { throw new Error("invalid config: activeProfile missing"); } const profiles = input.profiles; if (!profiles || typeof profiles !== "object") { throw new Error("invalid config: profiles missing"); } const active = profiles[activeProfile]; if (!active) { throw new Error(`invalid config: active profile "${activeProfile}" missing`); } return { version: 2, activeProfile, profiles }; } function resolveProfileKeyFile(config, profileName) { const profile = config.profiles[profileName]; if (!profile) { throw new Error(`profile not found: ${profileName}`); } return resolveSafeKeyPath(profile.keyFile, profileName); } function resolveSafeKeyPath(keyFile, profileName) { if (import_path.default.isAbsolute(keyFile) || keyFile.includes("..")) { if (profileName) { throw new Error(`invalid keyFile in config for profile "${profileName}"`); } throw new Error("invalid keyFile path"); } return import_path.default.join(getCliPaths().dir, keyFile); } // src/cli-lib/deeplink.ts var import_crypto2 = require("crypto"); // src/multibase/base.ts var import_base58 = require("multiformats/bases/base58"); var import_base64 = require("multiformats/bases/base64"); var import_base16 = require("multiformats/bases/base16"); // 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 createAuthenticationError(code, message, details, cause) { return new IdentityKitError(code, message, { category: "authentication", 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/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; } var Bytes = { stringToBytes, bytesToString, base64urlToBytes }; // src/multibase/base.ts var ENCODER_MAP = { base58btc: import_base58.base58btc, base64pad: import_base64.base64pad, base64: import_base64.base64, base64url: import_base64.base64url, base64urlpad: import_base64.base64urlpad, base16: import_base16.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 import_base58.base58btc.decode(encoded); } /** * Decode base64pad string to bytes * @param encoded The base64pad encoded string * @returns decoded bytes */ static decodeBase64pad(encoded) { return import_base64.base64pad.decode(encoded); } /** * Decode base16 string to bytes * @param encoded The base16 encoded string * @returns decoded bytes */ static decodeBase16(encoded) { return import_base16.base16.decode(encoded); } /** * Decode base64 string to bytes * @param encoded The base64 encoded string * @returns decoded bytes */ static decodeBase64(encoded) { return import_base64.base64.decode(encoded); } /** * Decode base64url string to bytes * @param encoded The base64url encoded string * @returns decoded bytes */ static decodeBase64url(encoded) { return import_base64.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 import_base64.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 import_base58.base58btc.decode(encoded); case "M": return import_base64.base64pad.decode(encoded); case "f": return import_base16.base16.decode(encoded); case "m": return import_base64.base64.decode(encoded); case "u": return import_base64.base64url.decode(encoded); case "U": return import_base64.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 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/deeplink/shared.ts var LOCALHOST_PATTERN = /^(localhost|\d+\.\d+\.\d+\.\d+)(:\d+)?$/; function normalizeCadopDomain(cadopDomain) { const trimmed = cadopDomain.trim().replace(/\/+$/, ""); if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) { return trimmed; } if (LOCALHOST_PATTERN.test(trimmed)) { return `http://${trimmed}`; } return `https://${trimmed}`; } function buildAddKeyPayload(input) { const payload = { version: 1, verificationMethod: { type: input.keyType, publicKeyMultibase: input.publicKeyMultibase, idFragment: input.keyFragment }, verificationRelationships: input.relationships, redirectUri: input.redirectUri, state: input.state }; if (input.agentDid) { payload.agentDid = input.agentDid; } if (input.scopes && input.scopes.length > 0) { payload.scopes = input.scopes; } return payload; } function encodeAddKeyPayload(payload) { return MultibaseCodec.encodeBase64url(JSON.stringify(payload)); } function buildAddKeyDeepLink(input) { const domain = normalizeCadopDomain(input.cadopDomain); const encodedPayload = encodeAddKeyPayload(input.payload); return `${domain}/add-key?payload=${encodedPayload}`; } // src/cli-lib/deeplink.ts function buildAddKeyDeepLink2(input) { const domain = normalizeCadopDomain(input.cadopDomain); const payload = buildAddKeyPayload({ keyType: input.key.keyType, publicKeyMultibase: input.key.publicKeyMultibase, keyFragment: input.keyFragment, relationships: ["authentication"], redirectUri: input.redirectUri || `${domain}/close`, state: (0, import_crypto2.randomUUID)() }); return { url: buildAddKeyDeepLink({ cadopDomain: domain, payload }), payload }; } // src/cli-lib/authHeader.ts var import_crypto6 = require("crypto"); // src/auth/v1/index.ts var v1_exports = {}; __export(v1_exports, { AuthErrorCode: () => AuthErrorCode, createSignature: () => createSignature, default: () => v1_default, toAuthorizationHeader: () => toAuthorizationHeader, verifyAuthHeader: () => verifyAuthHeader, verifySignature: () => verifySignature }); // src/auth/v1/utils.ts function canonicalize(obj) { const keys = Object.keys(obj).sort(); return JSON.stringify(obj, keys); } // 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 var import_secp256k1 = require("@noble/curves/secp256k1"); var import_sha256 = require("@noble/hashes/sha256"); var Secp256k1Provider = class { async generateKeyPair() { const privateKey = import_secp256k1.secp256k1.utils.randomPrivateKey(); const publicKey = import_secp256k1.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 = (0, import_sha256.sha256)(data); const signature = import_secp256k1.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 = (0, import_sha256.sha256)(data); return import_secp256k1.secp256k1.verify(signature, msgHash, publicKey); } getKeyType() { return KEY_TYPE.SECP256K1; } async derivePublicKey(privateKey) { return import_secp256k1.secp256k1.getPublicKey(privateKey, true); } }; // src/crypto/providers/ecdsa_r1.ts var import_p256 = require("@noble/curves/p256"); var import_utils = require("@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 = import_p256.p256.ProjectivePoint.fromHex(compressedKey); const x = point.x; const y = point.y; const xBytes = (0, import_utils.hexToBytes)(x.toString(16).padStart(64, "0")); const yBytes = (0, import_utils.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 = import_p256.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 = (0, import_utils.bytesToHex)(raw.slice(0, 32)); const s = (0, import_utils.bytesToHex)(raw.slice(32, 64)); const der = import_p256.p256.Signature.fromCompact((0, import_utils.hexToBytes)(r + s)).toDERHex(); return (0, import_utils.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; } }