@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,533 lines (1,516 loc) • 149 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
AbstractVDR: () => AbstractVDR,
AuthErrorCode: () => AuthErrorCode,
Bytes: () => Bytes,
CadopIdentityKit: () => CadopIdentityKit,
CadopServiceType: () => CadopServiceType,
CryptoUtils: () => CryptoUtils,
DIDAuth: () => DIDAuth,
DebugLogger: () => DebugLogger,
DefaultCryptoProviderFactory: () => DefaultCryptoProviderFactory,
DidAccountSigner: () => DidAccountSigner,
DidKeyCodec: () => DidKeyCodec,
EcdsaR1Provider: () => EcdsaR1Provider,
Ed25519Provider: () => Ed25519Provider,
IdentityEnv: () => IdentityEnv,
IdentityEnvBuilder: () => IdentityEnvBuilder,
IdentityKit: () => IdentityKit,
InMemoryLRUDIDDocumentCache: () => InMemoryLRUDIDDocumentCache,
KEY_TYPE: () => KEY_TYPE,
KeyManager: () => KeyManager,
KeyMultibaseCodec: () => KeyMultibaseCodec,
KeyStoreSigner: () => KeyStoreSigner,
KeyType: () => KeyType,
KeyVDR: () => KeyVDR,
MemoryKeyStore: () => MemoryKeyStore,
MultibaseCodec: () => MultibaseCodec,
NuwaIdentityKit: () => IdentityKit,
RoochTestUtils: () => RoochTestUtils,
RoochVDR: () => RoochVDR,
Secp256k1Provider: () => Secp256k1Provider,
StoredKeyCodec: () => StoredKeyCodec,
TestEnv: () => TestEnv,
VDRRegistry: () => VDRRegistry,
algorithmToKeyType: () => algorithmToKeyType,
base64urlToBytes: () => base64urlToBytes,
bootstrapIdentityEnv: () => bootstrapIdentityEnv,
bootstrapRoochTestEnv: () => bootstrapRoochTestEnv,
buildBaseScopes: () => buildBaseScopes,
buildDid: () => buildDid,
bytesToString: () => bytesToString,
combineScopes: () => combineScopes,
createCadopCustodian: () => createCadopCustodian,
createDefaultVDRs: () => createDefaultVDRs,
createDidViaCadop: () => createDidViaCadop,
createSelfDid: () => createSelfDid,
createVDR: () => createVDR,
defaultCryptoProviderFactory: () => defaultCryptoProviderFactory,
extractFragment: () => extractFragment,
extractFragmentFromId: () => extractFragmentFromId,
extractIdentifier: () => extractIdentifier,
extractMethod: () => extractMethod,
getDidWithoutFragment: () => getDidWithoutFragment,
getSupportedAlgorithms: () => getSupportedAlgorithms,
initRoochVDR: () => initRoochVDR,
isKeyType: () => isKeyType,
isValidAddressFormat: () => isValidAddressFormat,
keyTypeToAlgorithm: () => keyTypeToAlgorithm,
keyTypeToRoochSignatureScheme: () => keyTypeToRoochSignatureScheme,
parseDid: () => parseDid,
roochSignatureSchemeToKeyType: () => roochSignatureSchemeToKeyType,
sameDid: () => sameDid,
scopeObjectToString: () => scopeObjectToString,
scopeObjectsToStrings: () => scopeObjectsToStrings,
stringToBytes: () => stringToBytes,
toKeyType: () => toKeyType,
validateScopeFormat: () => validateScopeFormat,
validateScopes: () => validateScopes
});
module.exports = __toCommonJS(index_exports);
// 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
var import_base58 = require("multiformats/bases/base58");
var import_base64 = require("multiformats/bases/base64");
var import_base16 = require("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: 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 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 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 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
var import_rooch_sdk3 = require("@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
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 new Error("CryptoKey is not supported for Secp256k1 signing");
}
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 new Error("JsonWebKey is not supported for Secp256k1 verification");
}
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 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 = 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 new Error(`Failed to decompress public key: ${error.message}`);
}
}
async