@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,586 lines (1,565 loc) • 130 kB
JavaScript
// 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/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/vdr/roochVDR.ts
import {
RoochClient,
Transaction as Transaction2,
Args,
getRoochNodeUrl as sdkGetRoochNodeUrl,
Signer as Signer2
} 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 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;
}
// src/multibase/base.ts
var ENCODER_MAP = {
base58btc,
base64pad,
base64,
base64url,
base64urlpad,
base16
};
var MultibaseCodec = class {
/**
* Generic encode
* Example: `MultibaseCodec.encode(bytes, 'base64url')`
*/
static encode(data, base) {
const encoder = ENCODER_MAP[base];
if (!encoder) {
throw new Error(`Unsupported multibase: ${base}`);
}
const bytes = typeof data === "string" ? stringToBytes(data) : data;
return encoder.encode(bytes);
}
/**
* Encode bytes to base58btc format
* @param bytes The bytes to encode
* @returns base58btc encoded string with 'z' prefix
*/
static encodeBase58btc(bytes) {
return this.encode(bytes, "base58btc");
}
/**
* Encode bytes to base64pad format
* @param bytes The bytes to encode
* @returns base64pad encoded string with 'M' prefix
*/
static encodeBase64pad(data) {
return this.encode(data, "base64pad");
}
/**
* Encode bytes to base16 (hex) format
* @param bytes The bytes to encode
* @returns base16 encoded string with 'f' prefix
*/
static encodeBase16(bytes) {
return this.encode(bytes, "base16");
}
/**
* Encode bytes to base64 format
* @param bytes The bytes to encode
* @returns base64 encoded string
*/
static encodeBase64(data) {
return this.encode(data, "base64");
}
/**
* Encode bytes to base64url format (RFC4648 URL-safe, no padding)
* @param bytes The bytes to encode
* @returns base64url encoded string with 'u' prefix
*/
static encodeBase64url(data) {
return this.encode(data, "base64url");
}
/**
* Encode bytes to base64urlpad format (URL-safe with padding)
* @param bytes The bytes to encode
* @returns base64urlpad encoded string with 'U' prefix
*/
static encodeBase64urlpad(data) {
return this.encode(data, "base64urlpad");
}
/**
* Decode base58btc string to bytes
* @param encoded The base58btc encoded string
* @returns decoded bytes
*/
static decodeBase58btc(encoded) {
return base58btc.decode(encoded);
}
/**
* Decode base64pad string to bytes
* @param encoded The base64pad encoded string
* @returns decoded bytes
*/
static decodeBase64pad(encoded) {
return base64pad.decode(encoded);
}
/**
* Decode base16 string to bytes
* @param encoded The base16 encoded string
* @returns decoded bytes
*/
static decodeBase16(encoded) {
return base16.decode(encoded);
}
/**
* Decode base64 string to bytes
* @param encoded The base64 encoded string
* @returns decoded bytes
*/
static decodeBase64(encoded) {
return base64.decode(encoded);
}
/**
* Decode base64url string to bytes
* @param encoded The base64url encoded string
* @returns decoded bytes
*/
static decodeBase64url(encoded) {
return base64url.decode(encoded);
}
/**
* Decode base64url string to string
* @param encoded The base64url encoded string
* @returns decoded string
*/
static decodeBase64urlToString(encoded) {
return bytesToString(this.decodeBase64url(encoded));
}
/**
* Decode base64urlpad string to bytes
* @param encoded The base64urlpad encoded string
* @returns decoded bytes
*/
static decodeBase64urlpad(encoded) {
return base64urlpad.decode(encoded);
}
/**
* Decode base64urlpad string to string
* @param encoded The base64urlpad encoded string
* @returns decoded string
*/
static decodeBase64urlpadToString(encoded) {
return bytesToString(this.decodeBase64urlpad(encoded));
}
/**
* Decode multibase encoded string to bytes
* After multiformats v9, there is no longer a single "universal base" object;
* the official recommendation is to manually dispatch prefixes between the few *.decoder objects you use.
* @param encoded The multibase encoded string
* @returns decoded bytes
*/
static decode(encoded) {
const prefix = encoded[0];
switch (prefix) {
case "z":
return base58btc.decode(encoded);
case "M":
return base64pad.decode(encoded);
case "f":
return base16.decode(encoded);
case "m":
return base64.decode(encoded);
case "u":
return base64url.decode(encoded);
case "U":
return base64urlpad.decode(encoded);
default:
throw new Error(`Unsupported multibase prefix: ${prefix}`);
}
}
};
// src/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}`);
}
// 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/crypto/providers/ed25519.ts
function getCrypto() {
if (typeof globalThis !== "undefined" && globalThis.crypto) {
return globalThis.crypto;
}
throw new Error("Web Crypto API is not available in the current runtime");
}
var Ed25519Provider = class {
constructor() {
this.crypto = getCrypto();
}
async generateKeyPair() {
const { publicKey, privateKey } = await this.crypto.subtle.generateKey(
{
name: "Ed25519"
},
true,
["sign", "verify"]
);
const exportedPublic = new Uint8Array(await this.crypto.subtle.exportKey("raw", publicKey));
const exportedPrivate = new Uint8Array(await this.crypto.subtle.exportKey("pkcs8", privateKey));
return {
publicKey: exportedPublic,
privateKey: exportedPrivate
};
}
async sign(data, privateKey) {
let key;
if (privateKey instanceof Uint8Array) {
key = await this.crypto.subtle.importKey(
"pkcs8",
privateKey,
{
name: "Ed25519"
},
false,
["sign"]
);
} else {
key = privateKey;
}
const signature = await this.crypto.subtle.sign("Ed25519", key, data);
return new Uint8Array(signature);
}
async verify(data, signature, publicKey) {
let key;
if (publicKey instanceof Uint8Array) {
key = await this.crypto.subtle.importKey(
"raw",
publicKey,
{
name: "Ed25519"
},
false,
["verify"]
);
} else {
key = await this.crypto.subtle.importKey(
"jwk",
publicKey,
{
name: "Ed25519"
},
false,
["verify"]
);
}
return await this.crypto.subtle.verify("Ed25519", key, signature, data);
}
getKeyType() {
return KEY_TYPE.ED25519;
}
async derivePublicKey(privateKey) {
const cryptoKey = await this.crypto.subtle.importKey(
"pkcs8",
privateKey,
{ name: "Ed25519" },
true,
["sign"]
);
const jwk = await this.crypto.subtle.exportKey("jwk", cryptoKey);
if (!jwk.x) {
throw new Error("Failed to derive public key from private key");
}
const publicKeyBytes = base64urlToBytes(jwk.x);
return publicKeyBytes;
}
};
// src/crypto/providers/secp256k1.ts
import { secp256k1 } from "@noble/curves/secp256k1";
import { sha256 } from "@noble/hashes/sha256";
var Secp256k1Provider = class {
async generateKeyPair() {
const privateKey = secp256k1.utils.randomPrivateKey();
const publicKey = secp256k1.getPublicKey(privateKey, true);
return {
publicKey,
privateKey
};
}
async sign(data, privateKey) {
if (privateKey instanceof CryptoKey) {
throw new Error("CryptoKey is not supported for Secp256k1 signing");
}
const msgHash = sha256(data);
const signature = secp256k1.sign(msgHash, privateKey);
return signature.toCompactRawBytes();
}
async verify(data, signature, publicKey) {
if (!(publicKey instanceof Uint8Array)) {
throw new Error("JsonWebKey is not supported for Secp256k1 verification");
}
const msgHash = sha256(data);
return secp256k1.verify(signature, msgHash, publicKey);
}
getKeyType() {
return KEY_TYPE.SECP256K1;
}
async derivePublicKey(privateKey) {
return secp256k1.getPublicKey(privateKey, true);
}
};
// src/crypto/providers/ecdsa_r1.ts
import { p256 } from "@noble/curves/p256";
import { bytesToHex, hexToBytes } from "@noble/curves/abstract/utils";
function getCrypto2() {
if (typeof globalThis !== "undefined" && globalThis.crypto) {
return globalThis.crypto;
}
throw new Error("Web Crypto API is not available in the current runtime");
}
var EcdsaR1Provider = class {
constructor() {
this.crypto = getCrypto2();
}
async generateKeyPair() {
const { publicKey, privateKey } = await this.crypto.subtle.generateKey(
{
name: "ECDSA",
namedCurve: "P-256"
},
true,
["sign", "verify"]
);
const exportedPublic = new Uint8Array(await this.crypto.subtle.exportKey("raw", publicKey));
const exportedPrivate = new Uint8Array(await this.crypto.subtle.exportKey("pkcs8", privateKey));
const compressedPublicKey = this.compressPublicKey(exportedPublic);
return {
publicKey: compressedPublicKey,
privateKey: exportedPrivate
};
}
compressPublicKey(publicKey) {
if (publicKey.length !== 65) {
throw new Error(`Invalid public key length. Expected 65 bytes, got ${publicKey.length}`);
}
if (publicKey[0] !== 4) {
throw new Error("Invalid public key format. Expected uncompressed format (0x04)");
}
const x = publicKey.slice(1, 33);
const y = publicKey.slice(33, 65);
const compressedFormat = y[31] % 2 === 0 ? 2 : 3;
const compressed = new Uint8Array(33);
compressed[0] = compressedFormat;
compressed.set(x, 1);
return compressed;
}
decompressPublicKey(compressedKey) {
if (compressedKey.length !== 33) {
throw new Error(
`Invalid compressed public key length. Expected 33 bytes, got ${compressedKey.length}`
);
}
const format = compressedKey[0];
if (format !== 2 && format !== 3) {
throw new Error("Invalid compressed public key format. Expected 0x02 or 0x03");
}
try {
const point = p256.ProjectivePoint.fromHex(compressedKey);
const x = point.x;
const y = point.y;
const xBytes = hexToBytes(x.toString(16).padStart(64, "0"));
const yBytes = hexToBytes(y.toString(16).padStart(64, "0"));
const decompressed = new Uint8Array(65);
decompressed[0] = 4;
decompressed.set(xBytes, 1);
decompressed.set(yBytes, 33);
return decompressed;
} catch (err) {
const error = err;
throw new Error(`Failed to decompress public key: ${error.message}`);
}
}
async sign(data, privateKey) {
let key;
if (privateKey instanceof Uint8Array) {
key = await this.crypto.subtle.importKey(
"pkcs8",
privateKey,
{
name: "ECDSA",
namedCurve: "P-256"
},
false,
["sign"]
);
} else {
key = privateKey;
}
const signature = await this.crypto.subtle.sign(
{
name: "ECDSA",
hash: { name: "SHA-256" }
},
key,
data
);
return new Uint8Array(signature);
}
convertDERSignatureToRaw(derSignature) {
const firstByte = derSignature[0];
if (firstByte === 48) {
try {
const sig = p256.Signature.fromDER(derSignature);
return sig.toCompactRawBytes();
} catch (e) {
throw new Error("Invalid DER signature");
}
}
return derSignature;
}
convertRawToDER(raw) {
if (raw.length !== 64) return raw;
const r = bytesToHex(raw.slice(0, 32));
const s = bytesToHex(raw.slice(32, 64));
const der = p256.Signature.fromCompact(hexToBytes(r + s)).toDERHex();
return hexToBytes(der);
}
async verify(data, signature, publicKey) {
let key;
if (publicKey instanceof Uint8Array) {
const decompressedKey = this.decompressPublicKey(publicKey);
key = await this.crypto.subtle.importKey(
"raw",
decompressedKey,
{
name: "ECDSA",
namedCurve: "P-256"
},
false,
["verify"]
);
} else {
key = await this.crypto.subtle.importKey(
"jwk",
publicKey,
{
name: "ECDSA",
namedCurve: "P-256"
},
false,
["verify"]
);
}
return await this.crypto.subtle.verify(
{
name: "ECDSA",
hash: { name: "SHA-256" }
},
key,
signature,
data
);
}
getKeyType() {
return KEY_TYPE.ECDSAR1;
}
async derivePublicKey(privateKey) {
const cryptoKey = await this.crypto.subtle.importKey(
"pkcs8",
privateKey,
{ name: "ECDSA", namedCurve: "P-256" },
true,
["sign"]
);
const jwk = await this.crypto.subtle.exportKey("jwk", cryptoKey);
if (!jwk.x || !jwk.y) {
throw new Error("Failed to derive public key from private key");
}
const x = base64urlToBytes(jwk.x);
const y = base64urlToBytes(jwk.y);
const uncompressed = new Uint8Array(65);
uncompressed[0] = 4;
uncompressed.set(x, 1);
uncompressed.set(y, 33);
return this.compressPublicKey(uncompressed);
}
};
// src/crypto/factory.ts
var DefaultCryptoProviderFactory = class {
constructor() {
this.providers = /* @__PURE__ */ new Map();
this.providers.set(KEY_TYPE.ED25519, new Ed25519Provider());
this.providers.set(KEY_TYPE.SECP256K1, new Secp256k1Provider());
this.providers.set(KEY_TYPE.ECDSAR1, new EcdsaR1Provider());
}
createProvider(keyType) {
const provider = this.providers.get(keyType);
if (!provider) {
throw new Error(`No provider available for key type: ${keyType}`);
}
return provider;
}
supports(keyType) {
return this.providers.has(keyType);
}
};
var defaultCryptoProviderFactory = new DefaultCryptoProviderFactory();
// src/crypto/utils.ts
var CryptoUtils = class {
/**
* Generates a key pair based on the specified curve
* @param type The key type to generate (Ed25519VerificationKey2020 or EcdsaSecp256k1VerificationKey2019 or EcdsaSecp256r1VerificationKey2019)
* @returns A key pair containing public and private keys
*/
static async generateKeyPair(type = "Ed25519VerificationKey2020" /* ED25519 */) {
const keyType = typeof type === "string" ? toKeyType(type) : type;
const provider = defaultCryptoProviderFactory.createProvider(keyType);
return provider.generateKeyPair();
}
/**
* Signs data 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/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 new Error(`Key not found: ${keyId}`);
}
if (!key.privateKeyMultibase) {
throw new Error(`No private key available for ${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/didAccountSigner.ts
import {
RoochAddress,
Signer,
Authenticator,
Ed25519PublicKey,
Secp256k1PublicKey
} from "@roochnetwork/rooch-sdk";
// 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 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 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.keyId = keyId;
this.did = did;
if (!this.did.startsWith("did:rooch:")) {
throw new Error("Signer DID must be a did:rooch DID");
}
const didParts = parseDid(did);
this.didAddress = new RoochAddress(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
*/
static async create(signer, keyId) {
if (signer instanceof _DidAccountSigner) {
return signer;
}
const actualKeyId = keyId || (await signer.listKeyIds())[0];
if (!actualKeyId) {
throw new Error("No available keys in signer");
}
const keyInfo = await signer.getKeyInfo(actualKeyId);
if (!keyInfo) {
throw new Error(`Key info not found for keyId: ${actualKeyId}`);
}
const did = await signer.getDid();
return new _DidAccountSigner(signer, did, actualKeyId, keyInfo.type, keyInfo.publicKey);
}
// Implement Rooch Signer interface
getRoochAddress() {
return this.didAddress;
}
async sign(input) {
return this.wrappedSigner.signWithKeyId(input, this.keyId);
}
async signTransaction(input) {
return Authenticator.rooch(input.hashData(), this);
}
getKeyScheme() {
return keyTypeToRoochSignatureScheme(this.keyType);
}
getPublicKey() {
if (this.keyType === "EcdsaSecp256k1VerificationKey2019" /* SECP256K1 */) {
return new Secp256k1PublicKey(this.publicKey);
} else if (this.keyType === "Ed25519VerificationKey2020" /* ED25519 */) {
return new Ed25519PublicKey(this.publicKey);
} else if (this.keyType === "EcdsaSecp256r1VerificationKey2019" /* ECDSAR1 */) {
throw new Error("ECDSAR1 is not supported for DID account");
} else {
throw new Error(`Unsupported key type: ${this.keyType}`);
}
}
getBitcoinAddress() {
throw new Error("Bitcoin address is not supported for DID account");
}
// Implement SignerInterface
async signWithKeyId(data, keyId) {
if (keyId !== this.keyId) {
throw new Error(`Key ID mismatch. Expected ${this.keyId}, got ${keyId}`);
}
return this.sign(data);
}
async canSignWithKeyId(keyId) {
return keyId === this.keyId;
}
async listKeyIds() {
return [this.keyId];
}
async getDid() {
return this.did;
}
async getKeyInfo(keyId) {
if (keyId !== this.keyId) {
return void 0;
}
return {
type: this.keyType,
publicKey: this.publicKey
};
}
};
// 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/roochVDRTypes.ts
import {
bcs,
sha3_256,
toHEX,
stringToBytes as stringToBytes2,
Serializer
} from "@roochnetwork/rooch-sdk";
function simpleMapSchema(keySchema, valueSchema) {
return bcs.struct("SimpleMap", {
data: bcs.vector(
bcs.struct("Entry", {
key: keySchema,
value: valueSchema
})
)
});
}
function formatDIDString(did) {
return `did:${did.method}:${did.identifier}`;
}
var DIDSchema = bcs.struct("DID", {
method: bcs.string(),
identifier: bcs.string()
});
var DIDIdSchema = bcs.struct("DIDID", {
did: DIDSchema,
fragment: bcs.string()
});
var VerificationMethodSchema = bcs.struct("VerificationMethod", {
id: DIDIdSchema,
type: bcs.string(),
controller: DIDSchema,
public_key_multibase: bcs.string()
});
var ServiceSchema = bcs.struct("Service", {
id: DIDIdSchema,
type: bcs.string(),
service_endpoint: bcs.string(),
properties: simpleMapSchema(bcs.string(), bcs.string())
});
var AccountCapSchema = bcs.struct("AccountCap", {
addr: bcs.Address
});
var DIDDocumentSchema = bcs.struct("DIDDocument", {
id: DIDSchema,
controller: bcs.vector(DIDSchema),
verification_methods: simpleMapSchema(bcs.string(), VerificationMethodSchema),
authentication: bcs.vector(bcs.string()),
assertion_method: bcs.vector(bcs.string()),
capability_invocation: bcs.vector(bcs.string()),
capability_delegation: bcs.vector(bcs.string()),
key_agreement: bcs.vector(bcs.string()),
services: simpleMapSchema(bcs.string(), ServiceSchema),
also_known_as: bcs.vector(bcs.string()),
account_cap: AccountCapSchema
});
var DIDCreatedEventSchema = bcs.struct("DIDCreatedEvent", {
did: bcs.string(),
object_id: bcs.ObjectId,
controller: bcs.vector(bcs.string()),
creator_address: bcs.Address
});
function simpleMapToMap(simpleMap) {
return new Map(simpleMap.data.map((entry) => [entry.key, entry.value]));
}
function convertMoveDIDDocumentToInterface(didDocObject) {
let bcsHex = didDocObject.value;
bcsHex = bcsHex.startsWith("0x") ? bcsHex.slice(2) : bcsHex;
let bcsBytes = new Uint8Array(
bcsHex.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)) || []
);
let didDoc = DIDDocumentSchema.parse(bcsBytes);
const didId = formatDIDString(didDoc.id);
const controllers = didDoc.controller.map((c) => formatDIDString(c));
const verificationMethods = [];
const verificationMethodsMap = simpleMapToMap(didDoc.verification_methods);
verificationMethodsMap.forEach((vm) => {
verificationMethods.push({
id: `${formatDIDString(vm.id.did)}#${vm.id.fragment}`,
type: vm.type,
controller: formatDIDString(vm.controller),
publicKeyMultibase: vm.public_key_multibase
});
});
const convertFragmentToDIDURL = (fragment) => `${didId}#${fragment}`;
const services = [];
const servicesMap = simpleMapToMap(didDoc.services);
servicesMap.forEach((service) => {
const serviceEndpoint = {
id: `${formatDIDString(service.id.did)}#${service.id.fragment}`,
type: service.type,
serviceEndpoint: service.service_endpoint
};
let properties = simpleMapToMap(service.properties);
if (properties.size > 0) {
Object.assign(serviceEndpoint, Object.fromEntries(properties));
}
services.push(serviceEndpoint);
});
return {
"@context": ["https://www.w3.org/ns/did/v1"],
id: didId,
controller: controllers,
verificationMethod: verificationMethods,
authentication: didDoc.authentication.map(convertFragmentToDIDURL),
assertionMethod: didDoc.assertion_method.map(convertFragmentToDIDURL),
capabilityInvocation: didDoc.capability_invocation.map(convertFragmentToDIDURL),
capabilityDelegation: didDoc.capability_delegation.map(convertFragmentToDIDURL),
keyAgreement: didDoc.key_agreement.map(convertFragmentToDIDURL),
service: services,
alsoKnownAs: didDoc.also_known_as
};
}
function parseDIDCreatedEvent(eventData) {
const hexData = eventData.startsWith("0x") ? eventData.slice(2) : eventData;
const bytes = new Uint8Array(
hexData.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)) || []
);
return DIDCreatedEventSchema.parse(bytes);
}
var DIDDocumentStructTag = {
address: "0x3",
module: "did",
name: "DIDDocument",
typeParams: []
};
function resolveDidObjectID(identifier) {
return customObjectID(identifier, DIDDocumentStructTag);
}
function customObjectID(id, str