"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 __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 = (mod2, isNodeMode, target) => (target = mod2 != null ? __create(__getProtoOf(mod2)) : {}, __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 || !mod2 || !mod2.__esModule ? __defProp(target, "default", { value: mod2, enumerable: true }) : target, mod2 )); var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: true }), mod2); // src/index.ts var src_exports = {}; __export(src_exports, { AgentCryptoApi: () => AgentCryptoApi, AgentDidApi: () => AgentDidApi, AgentDwnApi: () => AgentDwnApi, AgentIdentityApi: () => AgentIdentityApi, AgentSyncApi: () => AgentSyncApi, BearerIdentity: () => BearerIdentity, DidInterface: () => DidInterface, DidRpcMethod: () => DidRpcMethod, DwnConstant: () => import_dwn_sdk_js2.DwnConstant, DwnDataStore: () => DwnDataStore, DwnDateSort: () => import_dwn_sdk_js2.DateSort, DwnDidStore: () => DwnDidStore, DwnEncryptionAlgorithm: () => import_dwn_sdk_js2.EncryptionAlgorithm, DwnEventSubscriptionHandler: () => import_dwn_sdk_js2.EventSubscriptionHandler, DwnIdentityStore: () => DwnIdentityStore, DwnInterface: () => DwnInterface, DwnKeyDerivationScheme: () => import_dwn_sdk_js2.KeyDerivationScheme, DwnKeyStore: () => DwnKeyStore, DwnMessageSubscription: () => import_dwn_sdk_js2.MessageSubscription, DwnPaginationCursor: () => import_dwn_sdk_js2.PaginationCursor, DwnPublicKeyJwk: () => import_dwn_sdk_js2.PublicJwk, DwnRecordSubscriptionHandler: () => import_dwn_sdk_js2.RecordSubscriptionHandler, DwnSigner: () => import_dwn_sdk_js2.Signer, HdIdentityVault: () => HdIdentityVault, HttpWeb5RpcClient: () => HttpWeb5RpcClient, InMemoryDataStore: () => InMemoryDataStore, InMemoryDidStore: () => InMemoryDidStore, InMemoryIdentityStore: () => InMemoryIdentityStore, InMemoryKeyStore: () => InMemoryKeyStore, LocalKeyManager: () => LocalKeyManager2, PlatformAgentTestHarness: () => PlatformAgentTestHarness, SyncEngineLevel: () => SyncEngineLevel, Web5RpcClient: () => Web5RpcClient, WebSocketWeb5RpcClient: () => WebSocketWeb5RpcClient, blobToIsomorphicNodeReadable: () => blobToIsomorphicNodeReadable, dwnMessageConstructors: () => dwnMessageConstructors, getDwnServiceEndpointUrls: () => getDwnServiceEndpointUrls, getPaginationCursor: () => getPaginationCursor, getRecordAuthor: () => getRecordAuthor, getRecordMessageCid: () => getRecordMessageCid, isDidRequest: () => isDidRequest, isDwnMessage: () => isDwnMessage, isDwnRequest: () => isDwnRequest, isIdentityMetadata: () => isIdentityMetadata, isPortableIdentity: () => isPortableIdentity, isRecordsWrite: () => isRecordsWrite, webReadableToIsomorphicNodeReadable: () => webReadableToIsomorphicNodeReadable }); module.exports = __toCommonJS(src_exports); // src/types/dwn.ts var import_dwn_sdk_js = require("@tbd54566975/dwn-sdk-js"); var import_dwn_sdk_js2 = require("@tbd54566975/dwn-sdk-js"); var DwnInterface = ((DwnInterface2) => { DwnInterface2[DwnInterface2["EventsGet"] = import_dwn_sdk_js.DwnInterfaceName.Events + import_dwn_sdk_js.DwnMethodName.Get] = "EventsGet"; DwnInterface2[DwnInterface2["EventsQuery"] = import_dwn_sdk_js.DwnInterfaceName.Events + import_dwn_sdk_js.DwnMethodName.Query] = "EventsQuery"; DwnInterface2[DwnInterface2["EventsSubscribe"] = import_dwn_sdk_js.DwnInterfaceName.Events + import_dwn_sdk_js.DwnMethodName.Subscribe] = "EventsSubscribe"; DwnInterface2[DwnInterface2["MessagesGet"] = import_dwn_sdk_js.DwnInterfaceName.Messages + import_dwn_sdk_js.DwnMethodName.Get] = "MessagesGet"; DwnInterface2[DwnInterface2["ProtocolsConfigure"] = import_dwn_sdk_js.DwnInterfaceName.Protocols + import_dwn_sdk_js.DwnMethodName.Configure] = "ProtocolsConfigure"; DwnInterface2[DwnInterface2["ProtocolsQuery"] = import_dwn_sdk_js.DwnInterfaceName.Protocols + import_dwn_sdk_js.DwnMethodName.Query] = "ProtocolsQuery"; DwnInterface2[DwnInterface2["RecordsDelete"] = import_dwn_sdk_js.DwnInterfaceName.Records + import_dwn_sdk_js.DwnMethodName.Delete] = "RecordsDelete"; DwnInterface2[DwnInterface2["RecordsQuery"] = import_dwn_sdk_js.DwnInterfaceName.Records + import_dwn_sdk_js.DwnMethodName.Query] = "RecordsQuery"; DwnInterface2[DwnInterface2["RecordsRead"] = import_dwn_sdk_js.DwnInterfaceName.Records + import_dwn_sdk_js.DwnMethodName.Read] = "RecordsRead"; DwnInterface2[DwnInterface2["RecordsSubscribe"] = import_dwn_sdk_js.DwnInterfaceName.Records + import_dwn_sdk_js.DwnMethodName.Subscribe] = "RecordsSubscribe"; DwnInterface2[DwnInterface2["RecordsWrite"] = import_dwn_sdk_js.DwnInterfaceName.Records + import_dwn_sdk_js.DwnMethodName.Write] = "RecordsWrite"; return DwnInterface2; })(DwnInterface || {}); var dwnMessageConstructors = { [DwnInterface.EventsGet]: import_dwn_sdk_js.EventsGet, [DwnInterface.EventsQuery]: import_dwn_sdk_js.EventsQuery, [DwnInterface.EventsSubscribe]: import_dwn_sdk_js.EventsSubscribe, [DwnInterface.MessagesGet]: import_dwn_sdk_js.MessagesGet, [DwnInterface.ProtocolsConfigure]: import_dwn_sdk_js.ProtocolsConfigure, [DwnInterface.ProtocolsQuery]: import_dwn_sdk_js.ProtocolsQuery, [DwnInterface.RecordsDelete]: import_dwn_sdk_js.RecordsDelete, [DwnInterface.RecordsQuery]: import_dwn_sdk_js.RecordsQuery, [DwnInterface.RecordsRead]: import_dwn_sdk_js.RecordsRead, [DwnInterface.RecordsSubscribe]: import_dwn_sdk_js.RecordsSubscribe, [DwnInterface.RecordsWrite]: import_dwn_sdk_js.RecordsWrite }; // src/bearer-identity.ts var BearerIdentity = class { constructor({ did, metadata }) { this.did = did; this.metadata = metadata; } /** * Converts a `BearerIdentity` object to a portable format containing the DID and metadata * associated with the Identity. * * @example * ```ts * // Assuming `identity` is an instance of BearerIdentity. * const portableIdentity = await identity.export(); * // portableIdentity now contains the and metadata. * ``` * * @returns A `PortableIdentity` containing the DID and metadata associated with the * `BearerIdentity`. */ async export() { return { portableDid: await this.did.export(), metadata: this.metadata }; } }; // src/crypto-api.ts var import_crypto8 = require("@web5/crypto"); // src/prototyping/crypto/algorithms/hkdf.ts var import_crypto = require("@web5/crypto"); // src/prototyping/crypto/primitives/hkdf.ts var import_utils = require("@noble/ciphers/webcrypto/utils"); var import_common = require("@web5/common"); var Hkdf = class { /** * Derives a key using the HMAC-based Extract-and-Expand Key Derivation Function (HKDF). * * This method generates a derived key using a hash function from input keying material given as * `baseKeyBytes`. The length of the derived key can be specified. Optionally, it can also use a salt * and info for the derivation process. * * HKDF is useful in various cryptographic applications and protocols, especially when * there's a need to derive multiple keys from a single source of key material. * * Note: The `baseKeyBytes` that will be the input key material for HKDF should be a high-entropy * secret value, such as a cryptographic key. It should be kept confidential and not be derived * from a low-entropy value, such as a password. * * @example * ```ts * const info = new Uint8Array([...]); * const derivedKeyBytes = await Hkdf.deriveKeyBytes({ * baseKeyBytes: new Uint8Array([...]), // Input keying material * hash: 'SHA-256', // The hash function to use ('SHA-256', 'SHA-384', 'SHA-512') * salt: new Uint8Array([...]), // The salt value * info: new Uint8Array([...]), // Optional application-specific information * length: 256 // The length of the derived key in bits * }); * ``` * * @param params - The parameters for key derivation. * @returns A Promise that resolves to the derived key as a byte array. */ static async deriveKeyBytes({ baseKeyBytes, length, hash: hash2, salt, info = new Uint8Array() }) { const webCrypto = (0, import_utils.getWebcryptoSubtle)(); const webCryptoKey = await webCrypto.importKey("raw", baseKeyBytes, { name: "HKDF" }, false, ["deriveBits"]); salt = typeof salt === "string" ? import_common.Convert.string(salt).toUint8Array() : salt; info = typeof info === "string" ? import_common.Convert.string(info).toUint8Array() : info; const derivedKeyBuffer = await crypto.subtle.deriveBits( { name: "HKDF", hash: hash2, salt, info }, webCryptoKey, length ); const derivedKeyBytes = new Uint8Array(derivedKeyBuffer); return derivedKeyBytes; } }; // src/prototyping/crypto/algorithms/hkdf.ts var HkdfAlgorithm = class extends import_crypto.CryptoAlgorithm { async deriveKeyBytes({ algorithm, ...params }) { const hash2 = { "HKDF-256": "SHA-256", "HKDF-384": "SHA-384", "HKDF-512": "SHA-512" }[algorithm]; const derivedKeyBytes = await Hkdf.deriveKeyBytes({ ...params, hash: hash2 }); return derivedKeyBytes; } }; // src/prototyping/crypto/algorithms/ecdsa.ts var import_crypto2 = require("@web5/crypto"); // src/prototyping/crypto/crypto-error.ts var CryptoError = class _CryptoError extends Error { /** * Constructs an instance of CryptoError, a custom error class for handling Crypto-related errors. * * @param code - A {@link CryptoErrorCode} representing the specific type of error encountered. * @param message - A human-readable description of the error. */ constructor(code, message) { super(message); this.code = code; this.name = "CryptoError"; Object.setPrototypeOf(this, new.target.prototype); if (Error.captureStackTrace) { Error.captureStackTrace(this, _CryptoError); } } }; // src/prototyping/crypto/algorithms/ecdsa.ts var EcdsaAlgorithm = class extends import_crypto2.CryptoAlgorithm { async bytesToPrivateKey({ algorithm, privateKeyBytes }) { switch (algorithm) { case "ES256K": case "secp256k1": { const privateKey = await import_crypto2.Secp256k1.bytesToPrivateKey({ privateKeyBytes }); privateKey.alg = "EdDSA"; return privateKey; } case "ES256": case "secp256r1": { const privateKey = await import_crypto2.Secp256r1.bytesToPrivateKey({ privateKeyBytes }); privateKey.alg = "EdDSA"; return privateKey; } default: { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `Algorithm not supported: ${algorithm}`); } } } async bytesToPublicKey({ algorithm, publicKeyBytes }) { switch (algorithm) { case "ES256K": case "secp256k1": { const publicKey = await import_crypto2.Secp256k1.bytesToPublicKey({ publicKeyBytes }); publicKey.alg = "EdDSA"; return publicKey; } case "ES256": case "secp256r1": { const publicKey = await import_crypto2.Secp256r1.bytesToPublicKey({ publicKeyBytes }); publicKey.alg = "EdDSA"; return publicKey; } default: { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `Algorithm not supported: ${algorithm}`); } } } /** * Derives the public key in JWK format from a given private key. * * @remarks * This method takes a private key in JWK format and derives its corresponding public key, * also in JWK format. The process ensures that the derived public key correctly corresponds to * the given private key. * * @example * ```ts * const ecdsa = new EcdsaAlgorithm(); * const privateKey = { ... }; // A Jwk object representing a private key * const publicKey = await ecdsa.computePublicKey({ key: privateKey }); * ``` * * @param params - The parameters for the public key derivation. * @param params.key - The private key in JWK format from which to derive the public key. * * @returns A Promise that resolves to the derived public key in JWK format. */ async computePublicKey({ key }) { if (!(0, import_crypto2.isEcPrivateJwk)(key)) throw new TypeError("Invalid key provided. Must be an elliptic curve (EC) private key."); switch (key.crv) { case "secp256k1": { const publicKey = await import_crypto2.Secp256k1.computePublicKey({ key }); publicKey.alg = "ES256K"; return publicKey; } case "P-256": { const publicKey = await import_crypto2.Secp256r1.computePublicKey({ key }); publicKey.alg = "ES256"; return publicKey; } default: { throw new Error(`Unsupported curve: ${key.crv}`); } } } /** * Generates a new private key with the specified algorithm in JSON Web Key (JWK) format. * * @example * ```ts * const ecdsa = new EcdsaAlgorithm(); * const privateKey = await ecdsa.generateKey({ algorithm: 'ES256K' }); * ``` * * @param params - The parameters for key generation. * @param params.algorithm - The algorithm to use for key generation. * * @returns A Promise that resolves to the generated private key in JWK format. */ async generateKey({ algorithm }) { switch (algorithm) { case "ES256K": case "secp256k1": { const privateKey = await import_crypto2.Secp256k1.generateKey(); privateKey.alg = "ES256K"; return privateKey; } case "ES256": case "secp256r1": { const privateKey = await import_crypto2.Secp256r1.generateKey(); privateKey.alg = "ES256"; return privateKey; } } } /** * Retrieves the public key properties from a given private key in JWK format. * * @remarks * This method extracts the public key portion from an ECDSA private key in JWK format. It does * so by removing the private key property 'd' and making a shallow copy, effectively yielding the * public key. * * Note: This method offers a significant performance advantage, being about 200 times faster * than `computePublicKey()`. However, it does not mathematically validate the private key, nor * does it derive the public key from the private key. It simply extracts existing public key * properties from the private key object. This makes it suitable for scenarios where speed is * critical and the private key's integrity is already assured. * * @example * ```ts * const ecdsa = new EcdsaAlgorithm(); * const privateKey = { ... }; // A Jwk object representing a private key * const publicKey = await ecdsa.getPublicKey({ key: privateKey }); * ``` * * @param params - The parameters for retrieving the public key properties. * @param params.key - The private key in JWK format. * * @returns A Promise that resolves to the public key in JWK format. */ async getPublicKey({ key }) { if (!(0, import_crypto2.isEcPrivateJwk)(key)) throw new TypeError("Invalid key provided. Must be an elliptic curve (EC) private key."); switch (key.crv) { case "secp256k1": { const publicKey = await import_crypto2.Secp256k1.getPublicKey({ key }); publicKey.alg = "ES256K"; return publicKey; } case "P-256": { const publicKey = await import_crypto2.Secp256r1.getPublicKey({ key }); publicKey.alg = "ES256"; return publicKey; } default: { throw new Error(`Unsupported curve: ${key.crv}`); } } } async privateKeyToBytes({ privateKey }) { switch (privateKey.crv) { case "secp256k1": { return await import_crypto2.Secp256k1.privateKeyToBytes({ privateKey }); } case "P-256": { return await import_crypto2.Secp256r1.privateKeyToBytes({ privateKey }); } default: { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `Curve not supported: ${privateKey.crv}`); } } } async publicKeyToBytes({ publicKey }) { switch (publicKey.crv) { case "secp256k1": { return await import_crypto2.Secp256k1.publicKeyToBytes({ publicKey }); } case "P-256": { return await import_crypto2.Secp256r1.publicKeyToBytes({ publicKey }); } default: { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `Curve not supported: ${publicKey.crv}`); } } } /** * Generates an ECDSA signature of given data using a private key. * * @remarks * This method uses the signature algorithm determined by the given `algorithm` to sign the * provided data. * * The signature can later be verified by parties with access to the corresponding * public key, ensuring that the data has not been tampered with and was indeed signed by the * holder of the private key. * * @example * ```ts * const ecdsa = new EcdsaAlgorithm(); * const data = new TextEncoder().encode('Message'); * const privateKey = { ... }; // A Jwk object representing a private key * const signature = await ecdsa.sign({ * key: privateKey, * data * }); * ``` * * @param params - The parameters for the signing operation. * @param params.key - The private key to use for signing, represented in JWK format. * @param params.data - The data to sign. * * @returns A Promise resolving to the digital signature as a `Uint8Array`. */ async sign({ key, data }) { if (!(0, import_crypto2.isEcPrivateJwk)(key)) throw new TypeError("Invalid key provided. Must be an elliptic curve (EC) private key."); switch (key.crv) { case "secp256k1": { return await import_crypto2.Secp256k1.sign({ key, data }); } case "P-256": { return await import_crypto2.Secp256r1.sign({ key, data }); } default: { throw new Error(`Unsupported curve: ${key.crv}`); } } } /** * Verifies an ECDSA signature associated with the provided data using the provided key. * * @remarks * This method uses the signature algorithm determined by the `crv` property of the provided key * to check the validity of a digital signature against the original data. It confirms whether the * signature was created by the holder of the corresponding private key and that the data has not * been tampered with. *s * @example * ```ts * const ecdsa = new EcdsaAlgorithm(); * const publicKey = { ... }; // Public key in JWK format corresponding to the private key that signed the data * const signature = new Uint8Array([...]); // Signature to verify * const data = new TextEncoder().encode('Message'); * const isValid = await ecdsa.verify({ * key: publicKey, * signature, * data * }); * ``` * * @param params - The parameters for the verification operation. * @param params.key - The key to use for verification. * @param params.signature - The signature to verify. * @param params.data - The data to verify. * * @returns A Promise resolving to a boolean indicating whether the signature is valid. */ async verify({ key, signature, data }) { if (!(0, import_crypto2.isEcPublicJwk)(key)) throw new TypeError("Invalid key provided. Must be an elliptic curve (EC) public key."); switch (key.crv) { case "secp256k1": { return await import_crypto2.Secp256k1.verify({ key, signature, data }); } case "P-256": { return await import_crypto2.Secp256r1.verify({ key, signature, data }); } default: { throw new Error(`Unsupported curve: ${key.crv}`); } } } }; // src/prototyping/crypto/algorithms/eddsa.ts var import_crypto3 = require("@web5/crypto"); var EdDsaAlgorithm = class extends import_crypto3.CryptoAlgorithm { async bytesToPrivateKey({ algorithm, privateKeyBytes }) { switch (algorithm) { case "Ed25519": { const privateKey = await import_crypto3.Ed25519.bytesToPrivateKey({ privateKeyBytes }); privateKey.alg = "EdDSA"; return privateKey; } default: { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `Algorithm not supported: ${algorithm}`); } } } async bytesToPublicKey({ algorithm, publicKeyBytes }) { switch (algorithm) { case "Ed25519": { const publicKey = await import_crypto3.Ed25519.bytesToPublicKey({ publicKeyBytes }); publicKey.alg = "EdDSA"; return publicKey; } default: { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `Algorithm not supported: ${algorithm}`); } } } /** * Derives the public key in JWK format from a given private key. * * @remarks * This method takes a private key in JWK format and derives its corresponding public key, * also in JWK format. The process ensures that the derived public key correctly corresponds to * the given private key. * * @example * ```ts * const eddsa = new EdDsaAlgorithm(); * const privateKey = { ... }; // A Jwk object representing a private key * const publicKey = await eddsa.computePublicKey({ key: privateKey }); * ``` * * @param params - The parameters for the public key derivation. * @param params.key - The private key in JWK format from which to derive the public key. * * @returns A Promise that resolves to the derived public key in JWK format. */ async computePublicKey({ key }) { if (!(0, import_crypto3.isOkpPrivateJwk)(key)) throw new TypeError("Invalid key provided. Must be an octet key pair (OKP) private key."); switch (key.crv) { case "Ed25519": { const publicKey = await import_crypto3.Ed25519.computePublicKey({ key }); publicKey.alg = "EdDSA"; return publicKey; } default: { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `Curve not supported: ${key.crv}`); } } } /** * Generates a new private key with the specified algorithm in JSON Web Key (JWK) format. * * @example * ```ts * const eddsa = new EdDsaAlgorithm(); * const privateKey = await eddsa.generateKey({ algorithm: 'Ed25519' }); * ``` * * @param params - The parameters for key generation. * @param params.algorithm - The algorithm to use for key generation. * * @returns A Promise that resolves to the generated private key in JWK format. */ async generateKey({ algorithm }) { switch (algorithm) { case "Ed25519": { const privateKey = await import_crypto3.Ed25519.generateKey(); privateKey.alg = "EdDSA"; return privateKey; } } } /** * Retrieves the public key properties from a given private key in JWK format. * * @remarks * This method extracts the public key portion from an EdDSA private key in JWK format. It does * so by removing the private key property 'd' and making a shallow copy, effectively yielding the * public key. * * Note: This method offers a significant performance advantage, being about 100 times faster * than `computePublicKey()`. However, it does not mathematically validate the private key, nor * does it derive the public key from the private key. It simply extracts existing public key * properties from the private key object. This makes it suitable for scenarios where speed is * critical and the private key's integrity is already assured. * * @example * ```ts * const eddsa = new EdDsaAlgorithm(); * const privateKey = { ... }; // A Jwk object representing a private key * const publicKey = await eddsa.getPublicKey({ key: privateKey }); * ``` * * @param params - The parameters for retrieving the public key properties. * @param params.key - The private key in JWK format. * * @returns A Promise that resolves to the public key in JWK format. */ async getPublicKey({ key }) { if (!(0, import_crypto3.isOkpPrivateJwk)(key)) throw new TypeError("Invalid key provided. Must be an octet key pair (OKP) private key."); switch (key.crv) { case "Ed25519": { const publicKey = await import_crypto3.Ed25519.getPublicKey({ key }); publicKey.alg = "EdDSA"; return publicKey; } default: { throw new Error(`Unsupported curve: ${key.crv}`); } } } async privateKeyToBytes({ privateKey }) { switch (privateKey.crv) { case "Ed25519": { return await import_crypto3.Ed25519.privateKeyToBytes({ privateKey }); } default: { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `Curve not supported: ${privateKey.crv}`); } } } async publicKeyToBytes({ publicKey }) { switch (publicKey.crv) { case "Ed25519": { return await import_crypto3.Ed25519.publicKeyToBytes({ publicKey }); } default: { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `Curve not supported: ${publicKey.crv}`); } } } /** * Generates an EdDSA signature of given data using a private key. * * @remarks * This method uses the signature algorithm determined by the given `algorithm` to sign the * provided data. * * The signature can later be verified by parties with access to the corresponding * public key, ensuring that the data has not been tampered with and was indeed signed by the * holder of the private key. * * @example * ```ts * const eddsa = new EdDsaAlgorithm(); * const data = new TextEncoder().encode('Message'); * const privateKey = { ... }; // A Jwk object representing a private key * const signature = await eddsa.sign({ * key: privateKey, * data * }); * ``` * * @param params - The parameters for the signing operation. * @param params.key - The private key to use for signing, represented in JWK format. * @param params.data - The data to sign. * * @returns A Promise resolving to the digital signature as a `Uint8Array`. */ async sign({ key, data }) { if (!(0, import_crypto3.isOkpPrivateJwk)(key)) throw new TypeError("Invalid key provided. Must be an octet key pair (OKP) private key."); switch (key.crv) { case "Ed25519": { return await import_crypto3.Ed25519.sign({ key, data }); } default: { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `Curve not supported: ${key.crv}`); } } } /** * Verifies an EdDSA signature associated with the provided data using the provided key. * * @remarks * This method uses the signature algorithm determined by the `crv` property of the provided key * to check the validity of a digital signature against the original data. It confirms whether the * signature was created by the holder of the corresponding private key and that the data has not * been tampered with. *s * @example * ```ts * const eddsa = new EdDsaAlgorithm(); * const publicKey = { ... }; // Public key in JWK format corresponding to the private key that signed the data * const signature = new Uint8Array([...]); // Signature to verify * const data = new TextEncoder().encode('Message'); * const isValid = await eddsa.verify({ * key: publicKey, * signature, * data * }); * ``` * * @param params - The parameters for the verification operation. * @param params.key - The key to use for verification. * @param params.signature - The signature to verify. * @param params.data - The data to verify. * * @returns A Promise resolving to a boolean indicating whether the signature is valid. */ async verify({ key, signature, data }) { if (!(0, import_crypto3.isOkpPublicJwk)(key)) throw new TypeError("Invalid key provided. Must be an octet key pair (OKP) public key."); switch (key.crv) { case "Ed25519": { return await import_crypto3.Ed25519.verify({ key, signature, data }); } default: { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `Curve not supported: ${key.crv}`); } } } }; // src/prototyping/crypto/algorithms/aes-kw.ts var import_crypto5 = require("@web5/crypto"); // src/prototyping/crypto/primitives/aes-kw.ts var import_utils2 = require("@noble/ciphers/webcrypto/utils"); var import_common2 = require("@web5/common"); var import_crypto4 = require("@web5/crypto"); var AES_KEY_LENGTHS = [128, 192, 256]; var AesKw = class { /** * Converts a raw private key in bytes to its corresponding JSON Web Key (JWK) format. * * @remarks * This method takes a symmetric key represented as a byte array (Uint8Array) and * converts it into a JWK object for use with AES (Advanced Encryption Standard) * for key wrapping. The conversion process involves encoding the key into * base64url format and setting the appropriate JWK parameters. * * The resulting JWK object includes the following properties: * - `kty`: Key Type, set to 'oct' for Octet Sequence (representing a symmetric key). * - `k`: The symmetric key, base64url-encoded. * - `kid`: Key ID, generated based on the JWK thumbprint. * * @example * ```ts * const privateKeyBytes = new Uint8Array([...]); // Replace with actual symmetric key bytes * const privateKey = await AesKw.bytesToPrivateKey({ privateKeyBytes }); * ``` * * @param params - The parameters for the symmetric key conversion. * @param params.privateKeyBytes - The raw symmetric key as a Uint8Array. * * @returns A Promise that resolves to the symmetric key in JWK format. */ static async bytesToPrivateKey({ privateKeyBytes }) { const privateKey = { k: import_common2.Convert.uint8Array(privateKeyBytes).toBase64Url(), kty: "oct" }; privateKey.kid = await (0, import_crypto4.computeJwkThumbprint)({ jwk: privateKey }); const lengthInBits = privateKeyBytes.length * 8; privateKey.alg = { 128: "A128KW", 192: "A192KW", 256: "A256KW" }[lengthInBits]; return privateKey; } /** * Generates a symmetric key for AES for key wrapping in JSON Web Key (JWK) format. * * @remarks * This method creates a new symmetric key of a specified length suitable for use with * AES key wrapping. It uses cryptographically secure random number generation to * ensure the uniqueness and security of the key. The generated key adheres to the JWK * format, making it compatible with common cryptographic standards and easy to use in * various cryptographic processes. * * The generated key includes the following components: * - `kty`: Key Type, set to 'oct' for Octet Sequence. * - `k`: The symmetric key component, base64url-encoded. * - `kid`: Key ID, generated based on the JWK thumbprint. * - `alg`: Algorithm, set to 'A128KW', 'A192KW', or 'A256KW' for AES Key Wrap with the * specified key length. * * @example * ```ts * const length = 256; // Length of the key in bits (e.g., 128, 192, 256) * const privateKey = await AesKw.generateKey({ length }); * ``` * * @param params - The parameters for the key generation. * @param params.length - The length of the key in bits. Common lengths are 128, 192, and 256 bits. * * @returns A Promise that resolves to the generated symmetric key in JWK format. */ static async generateKey({ length }) { if (!AES_KEY_LENGTHS.includes(length)) { throw new RangeError(`The key length is invalid: Must be ${AES_KEY_LENGTHS.join(", ")} bits`); } const webCrypto = (0, import_utils2.getWebcryptoSubtle)(); const webCryptoKey = await webCrypto.generateKey({ name: "AES-KW", length }, true, ["wrapKey", "unwrapKey"]); const { ext, key_ops, ...privateKey } = await webCrypto.exportKey("jwk", webCryptoKey); privateKey.kid = await (0, import_crypto4.computeJwkThumbprint)({ jwk: privateKey }); return privateKey; } /** * Converts a private key from JSON Web Key (JWK) format to a raw byte array (Uint8Array). * * @remarks * This method takes a symmetric key in JWK format and extracts its raw byte representation. * It decodes the 'k' parameter of the JWK value, which represents the symmetric key in base64url * encoding, into a byte array. * * @example * ```ts * const privateKey = { ... }; // A symmetric key in JWK format * const privateKeyBytes = await AesKw.privateKeyToBytes({ privateKey }); * ``` * * @param params - The parameters for the symmetric key conversion. * @param params.privateKey - The symmetric key in JWK format. * * @returns A Promise that resolves to the symmetric key as a Uint8Array. */ static async privateKeyToBytes({ privateKey }) { if (!(0, import_crypto4.isOctPrivateJwk)(privateKey)) { throw new Error(`AesKw: The provided key is not a valid oct private key.`); } const privateKeyBytes = import_common2.Convert.base64Url(privateKey.k).toUint8Array(); return privateKeyBytes; } static async unwrapKey({ wrappedKeyBytes, wrappedKeyAlgorithm, decryptionKey }) { if (!("alg" in decryptionKey && decryptionKey.alg)) { throw new CryptoError("invalidJwk" /* InvalidJwk */, `The decryption key is missing the 'alg' property.`); } if (!["A128KW", "A192KW", "A256KW"].includes(decryptionKey.alg)) { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `The 'decryptionKey' algorithm is not supported: ${decryptionKey.alg}`); } const webCrypto = (0, import_utils2.getWebcryptoSubtle)(); const decryptionCryptoKey = await webCrypto.importKey( "jwk", // key format decryptionKey, // key data { name: "AES-KW" }, // algorithm identifier true, // key is extractable ["unwrapKey"] // key usages ); const webCryptoAlgorithm = { A128KW: "AES-KW", A192KW: "AES-KW", A256KW: "AES-KW", A128GCM: "AES-GCM", A192GCM: "AES-GCM", A256GCM: "AES-GCM" }[wrappedKeyAlgorithm]; if (!webCryptoAlgorithm) { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `The 'wrappedKeyAlgorithm' is not supported: ${wrappedKeyAlgorithm}`); } const unwrappedCryptoKey = await webCrypto.unwrapKey( "raw", // output format wrappedKeyBytes.buffer, // key to unwrap decryptionCryptoKey, // unwrapping key "AES-KW", // algorithm identifier { name: webCryptoAlgorithm }, // unwrapped key algorithm identifier true, // key is extractable ["unwrapKey"] // key usages ); const { ext, key_ops, ...unwrappedJsonWebKey } = await webCrypto.exportKey("jwk", unwrappedCryptoKey); const unwrappedKey = unwrappedJsonWebKey; unwrappedKey.kid = await (0, import_crypto4.computeJwkThumbprint)({ jwk: unwrappedKey }); return unwrappedKey; } static async wrapKey({ unwrappedKey, encryptionKey }) { if (!("alg" in encryptionKey && encryptionKey.alg)) { throw new CryptoError("invalidJwk" /* InvalidJwk */, `The encryption key is missing the 'alg' property.`); } if (!["A128KW", "A192KW", "A256KW"].includes(encryptionKey.alg)) { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `The 'encryptionKey' algorithm is not supported: ${encryptionKey.alg}`); } if (!("alg" in unwrappedKey && unwrappedKey.alg)) { throw new CryptoError("invalidJwk" /* InvalidJwk */, `The private key to wrap is missing the 'alg' property.`); } const webCrypto = (0, import_utils2.getWebcryptoSubtle)(); const encryptionCryptoKey = await webCrypto.importKey( "jwk", // key format encryptionKey, // key data { name: "AES-KW" }, // algorithm identifier true, // key is extractable ["wrapKey"] // key usages ); const webCryptoAlgorithm = { A128KW: "AES-KW", A192KW: "AES-KW", A256KW: "AES-KW", A128GCM: "AES-GCM", A192GCM: "AES-GCM", A256GCM: "AES-GCM" }[unwrappedKey.alg]; if (!webCryptoAlgorithm) { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `The 'unwrappedKey' algorithm is not supported: ${unwrappedKey.alg}`); } const unwrappedCryptoKey = await webCrypto.importKey( "jwk", // key format unwrappedKey, // key data { name: webCryptoAlgorithm }, // algorithm identifier true, // key is extractable ["unwrapKey"] // key usages ); const wrappedKeyBuffer = await webCrypto.wrapKey( "raw", // output format unwrappedCryptoKey, // key to wrap encryptionCryptoKey, // wrapping key "AES-KW" // algorithm identifier ); const wrappedKeyBytes = new Uint8Array(wrappedKeyBuffer); return wrappedKeyBytes; } }; // src/prototyping/crypto/algorithms/aes-kw.ts var AesKwAlgorithm = class extends import_crypto5.CryptoAlgorithm { async bytesToPrivateKey({ privateKeyBytes }) { const privateKey = await AesKw.bytesToPrivateKey({ privateKeyBytes }); privateKey.alg = { 16: "A128KW", 24: "A192KW", 32: "A256KW" }[privateKeyBytes.length]; return privateKey; } /** * Generates a symmetric key for AES for key wrapping in JSON Web Key (JWK) format. * * @remarks * This method generates a symmetric AES key for use in key wrapping mode, based on the specified * `algorithm` parameter which determines the key length. It uses cryptographically secure random * number generation to ensure the uniqueness and security of the key. The key is returned in JWK * format. * * The generated key includes the following components: * - `kty`: Key Type, set to 'oct' for Octet Sequence. * - `k`: The symmetric key component, base64url-encoded. * - `kid`: Key ID, generated based on the JWK thumbprint. * - `alg`: Algorithm, set to 'A128KW', 'A192KW', or 'A256KW' for AES Key Wrap with the * specified key length. * * @example * ```ts * const aesKw = new AesKwAlgorithm(); * const privateKey = await aesKw.generateKey({ algorithm: 'A256KW' }); * ``` * * @param params - The parameters for the key generation. * * @returns A Promise that resolves to the generated symmetric key in JWK format. */ async generateKey({ algorithm }) { const length = { A128KW: 128, A192KW: 192, A256KW: 256 }[algorithm]; const privateKey = await AesKw.generateKey({ length }); privateKey.alg = algorithm; return privateKey; } async privateKeyToBytes({ privateKey }) { const privateKeyBytes = await AesKw.privateKeyToBytes({ privateKey }); return privateKeyBytes; } /** * Decrypts a wrapped key using the AES Key Wrap algorithm. * * @remarks * This method unwraps a previously wrapped cryptographic key using the AES Key Wrap algorithm. * The wrapped key, provided as a byte array, is unwrapped using the decryption key specified in * the parameters. * * This operation is useful for securely receiving keys transmitted over untrusted mediums. The * method returns the unwrapped key as a JSON Web Key (JWK). * * @example * ```ts * const aesKw = new AesKwAlgorithm(); * const wrappedKeyBytes = new Uint8Array([...]); // Byte array of a wrapped AES-256 GCM key * const decryptionKey = { ... }; // A Jwk object representing the AES unwrapping key * const unwrappedKey = await aesKw.unwrapKey({ * wrappedKeyBytes, * wrappedKeyAlgorithm: 'A256GCM', * decryptionKey * }); * ``` * * @param params - The parameters for the key unwrapping operation. * * @returns A Promise that resolves to the unwrapped key in JWK format. */ async unwrapKey(params) { const unwrappedKey = await AesKw.unwrapKey(params); return unwrappedKey; } /** * Encrypts a given key using the AES Key Wrap algorithm. * * @remarks * This method wraps a given cryptographic key using the AES Key Wrap algorithm. The private key * to be wrapped is provided in the form of a JSON Web Key (JWK). * * This operation is useful for securely transmitting keys over untrusted mediums. The method * returns the wrapped key as a byte array. * * @example * ```ts * const aesKw = new AesKwAlgorithm(); * const unwrappedKey = { ... }; // A Jwk object representing the key to be wrapped * const encryptionKey = { ... }; // A Jwk object representing the AES wrapping key * const wrappedKeyBytes = await aesKw.wrapKey({ unwrappedKey, encryptionKey }); * ``` * * @param params - The parameters for the key wrapping operation. * * @returns A Promise that resolves to the wrapped key as a Uint8Array. */ async wrapKey(params) { const wrappedKeyBytes = AesKw.wrapKey(params); return wrappedKeyBytes; } }; // src/prototyping/crypto/algorithms/pbkdf2.ts var import_crypto6 = require("@web5/crypto"); // src/prototyping/crypto/primitives/pbkdf2.ts var import_utils3 = require("@noble/ciphers/webcrypto/utils"); var Pbkdf2 = class { /** * Derives a cryptographic key from a password using the PBKDF2 algorithm. * * @remarks * This method applies the PBKDF2 algorithm to the provided password along with * a salt value and iterates the process a specified number of times. It uses * a cryptographic hash function to enhance security and produce a key of the * desired length. The method is capable of utilizing either the Web Crypto API * or the Node.js Crypto module, depending on the environment's support. * * @example * ```ts * const derivedKeyBytes = await Pbkdf2.deriveKeyBytes({ * baseKeyBytes: new TextEncoder().encode('password'), // The password as a Uint8Array * hash: 'SHA-256', // The hash function to use ('SHA-256', 'SHA-384', 'SHA-512') * salt: new Uint8Array([...]), // The salt value * iterations: 600_000, // The number of iterations * length: 256 // The length of the derived key in bits * }); * ``` * * @param params - The parameters for key derivation. * @returns A Promise that resolves to the derived key as a byte array. */ static async deriveKeyBytes({ baseKeyBytes, hash: hash2, salt, iterations, length }) { const webCrypto = (0, import_utils3.getWebcryptoSubtle)(); const webCryptoKey = await webCrypto.importKey( "raw", // key format is raw bytes baseKeyBytes, // key data to import { name: "PBKDF2" }, // algorithm identifier false, // key is not extractable ["deriveBits"] // key usages ); const derivedKeyBuffer = await webCrypto.deriveBits( { name: "PBKDF2", hash: hash2, salt, iterations }, webCryptoKey, length ); const derivedKeyBytes = new Uint8Array(derivedKeyBuffer); return derivedKeyBytes; } }; // src/prototyping/crypto/algorithms/pbkdf2.ts var Pbkdf2Algorithm = class extends import_crypto6.CryptoAlgorithm { async deriveKeyBytes({ algorithm, ...params }) { const [, hashFunction] = algorithm.split(/[-+]/); const hash2 = { "HS256": "SHA-256", "HS384": "SHA-384", "HS512": "SHA-512" }[hashFunction]; const derivedKeyBytes = await Pbkdf2.deriveKeyBytes({ ...params, hash: hash2 }); return derivedKeyBytes; } }; // src/prototyping/crypto/algorithms/aes-gcm.ts var import_crypto7 = require("@web5/crypto"); var AesGcmAlgorithm = class extends import_crypto7.CryptoAlgorithm { async bytesToPrivateKey({ privateKeyBytes }) { const privateKey = await import_crypto7.AesGcm.bytesToPrivateKey({ privateKeyBytes }); privateKey.alg = { 16: "A128GCM", 24: "A192GCM", 32: "A256GCM" }[privateKeyBytes.length]; return privateKey; } /** * Decrypts the provided data using AES-GCM. * * @remarks * This method performs AES-GCM decryption on the given encrypted data using the specified key. * It requires an initialization vector (IV), the encrypted data along with the decryption key, * and optionally, additional authenticated data (AAD). The method returns the decrypted data as a * Uint8Array. The optional `tagLength` parameter specifies the size in bits of the authentication * tag used when encrypting the data. If not specified, the default tag length of 128 bits is * used. * * @example * ```ts * const aesGcm = new AesGcmAlgorithm(); * const encryptedData = new Uint8Array([...]); // Encrypted data * const iv = new Uint8Array([...]); // Initialization vector used during encryption * const additionalData = new Uint8Array([...]); // Optional additional authenticated data * const key = { ... }; // A Jwk object representing the AES key * const decryptedData = await aesGcm.decrypt({ * data: encryptedData, * iv, * additionalData, * key, * tagLength: 128 // Optional tag length in bits * }); * ``` * * @param params - The parameters for the decryption operation. * * @returns A Promise that resolves to the decrypted data as a Uint8Array. */ async decrypt(params) { const plaintext = import_crypto7.AesGcm.decrypt(params); return plaintext; } /** * Encrypts the provided data using AES-GCM. * * @remarks * This method performs AES-GCM encryption on the given data using the specified key. * It requires an initialization vector (IV), the encrypted data along with the decryption key, * and optionally, additional authenticated data (AAD). The method returns the encrypted data as a * Uint8Array. The optional `tagLength` parameter specifies the size in bits of the authentication * tag generated in the encryption operation and used for authentication in the corresponding * decryption. If not specified, the default tag length of 128 bits is used. * * @example * ```ts * const aesGcm = new AesGcmAlgorithm(); * const data = new TextEncoder().encode('Messsage'); * const iv = new Uint8Array([...]); // Initialization vector * const additionalData = new Uint8Array([...]); // Optional additional authenticated data * const key = { ... }; // A Jwk object representing an AES key * const encryptedData = await aesGcm.encrypt({ * data, * iv, * additionalData, * key, * tagLength: 128 // Optional tag length in bits * }); * ``` * * @param params - The parameters for the encryption operation. * * @returns A Promise that resolves to the encrypted data as a Uint8Array. */ async encrypt(params) { const ciphertext = import_crypto7.AesGcm.encrypt(params); return ciphertext; } /** * Generates a symmetric key for AES in Galois/Counter Mode (GCM) in JSON Web Key (JWK) format. * * @remarks * This method generates a symmetric AES key for use in GCM mode, based on the specified * `algorithm` parameter which determines the key length. It uses cryptographically secure random * number generation to ensure the uniqueness and security of the key. The key is returned in JWK * format. * * The generated key includes the following components: * - `kty`: Key Type, set to 'oct' for Octet Sequence. * - `k`: The symmetric key component, base64url-encoded. * - `kid`: Key ID, generated based on the JWK thumbprint. * * @example * ```ts * const aesGcm = new AesGcmAlgorithm(); * const privateKey = await aesGcm.generateKey({ algorithm: 'A256GCM' }); * ``` * * @param params - The parameters for the key generation. * * @returns A Promise that resolves to the generated symmetric key in JWK format. */ async generateKey({ algorithm }) { const length = { A128GCM: 128, A192GCM: 192, A256GCM: 256 }[algorithm]; const privateKey = await import_crypto7.AesGcm.generateKey({ length }); privateKey.alg = algorithm; return privateKey; } async privateKeyToBytes({ privateKey }) { const privateKeyBytes = await import_crypto7.AesGcm.privateKeyToBytes({ privateKey }); return privateKeyBytes; } }; // src/crypto-api.ts var supportedAlgorithms = { "AES-GCM": { implementation: AesGcmAlgorithm, names: ["A128GCM", "A192GCM", "A256GCM"], operations: ["bytesToPrivateKey", "decrypt", "encrypt", "generateKey"] }, "AES-KW": { implementation: AesKwAlgorithm, names: ["A128KW", "A192KW", "A256KW"], operations: ["bytesToPrivateKey", "generateKey", "privateKeyToBytes", "wrapKey", "unwrapKey"] }, "Ed25519": { implementation: EdDsaAlgorithm, names: ["Ed25519"], operations: ["bytesToPrivateKey", "bytesToPublicKey", "generateKey", "sign", "verify"] }, "HKDF": { implementation: HkdfAlgorithm, names: ["HKDF-256", "HKDF-384", "HKDF-512"], operations: ["deriveKey", "deriveKeyBytes"] }, "PBKDF2": { implementation: Pbkdf2Algorithm, names: ["PBES2-HS256+A128KW", "PBES2-HS384+A192KW", "PBES2-HS512+A256KW"], operations: ["deriveKey", "deriveKeyBytes"] }, "secp256k1": { implementation: EcdsaAlgorithm, names: ["ES256K", "secp256k1"], operations: ["bytesToPrivateKey", "bytesToPublicKey", "generateKey", "sign", "verify"] }, "secp256r1": { implementation: EcdsaAlgorithm, names: ["ES256", "secp256r1"], operations: ["bytesToPrivateKey", "bytesToPublicKey", "generateKey", "sign", "verify"] }, "SHA-256": { implementation: import_crypto8.Sha2Algorithm, names: ["SHA-256"], operations: ["digest"] } }; var AgentCryptoApi = class { constructor() { /** * A private map that stores instances of cryptographic algorithm implementations. Each key in * this map is an `AlgorithmConstructor`, and its corresponding value is an instance of a class * that implements a specific cryptographic algorithm. This map is used to cache and reuse * instances for performance optimization, ensuring that each algorithm is instantiated only once. */ this._algorithmInstances = /* @__PURE__ */ new Map(); } async bytesToPrivateKey({ algorithm: algorithmIdentifier, privateKeyBytes }) { const algorithm = this.getAlgorithmName({ algorithm: algorithmIdentifier }); const keyConverter = this.getAlgorithm({ algorithm }); const privateKey = await keyConverter.bytesToPrivateKey({ algorithm: algorithmIdentifier, privateKeyBytes }); return privateKey; } async bytesToPublicKey({ algorithm: algorithmIdentifier, publicKeyBytes }) { const algorithm = this.getAlgorithmName({ algorithm: algorithmIdentifier }); const keyConverter = this.getAlgorithm({ algorithm }); const publicKey = await keyConverter.bytesToPublicKey({ algorithm: algorithmIdentifier, publicKeyBytes }); return publicKey; } async decrypt(params) { const algorithm = this.getAlgorithmName({ key: params.key }); const cipher = this.getAlgorithm({ algorithm }); return await cipher.decrypt(params); } async deriveKey(params) { const algorithm = this.getAlgorithmName({ algorithm: params.algorithm }); const kdf = this.getAlgorithm({ algorithm }); let derivedKeyAlgorithm; switch (params.algorithm) { case "HKDF-256": case "HKDF-384": case "HKDF-512": { derivedKeyAlgorithm = params.derivedKeyAlgorithm; break; } case "PBES2-HS256+A128KW": case "PBES2-HS384+A192KW": case "PBES2-HS512+A256KW": { derivedKeyAlgorithm = params.algorithm.split(/[-+]/)[2]; break; } default: throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `The specified "algorithm" is not supported: ${params.algorithm}`); } const length = +(derivedKeyAlgorithm.match(/\d+/)?.[0] ?? -1); if (length === -1) { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `The derived key algorithm" is not supported: ${derivedKeyAlgorithm}`); } const privateKeyBytes = await kdf.deriveKeyBytes({ ...params, length }); return await this.bytesToPrivateKey({ algorithm: derivedKeyAlgorithm, privateKeyBytes }); } async deriveKeyBytes(params) { const algorithm = this.getAlgorithmName({ algorithm: params.algorithm }); const kdf = this.getAlgorithm({ algorithm }); const derivedKeyBytes = await kdf.deriveKeyBytes(params); return derivedKeyBytes; } /** * Generates a hash digest of the provided data. * * @remarks * A digest is the output of the hash function. It's a fixed-size string of bytes that uniquely * represents the data input into the hash function. The digest is often used for data integrity * checks, as any alteration in the input data results in a significantly different digest. * * It takes the algorithm identifier of the hash function and data to digest as input and returns * the digest of the data. * * @example * ```ts * const cryptoApi = new AgentCryptoApi(); * const data = new Uint8Array([...]); * const digest = await cryptoApi.digest({ algorithm: 'SHA-256', data }); * ``` * * @param params - The parameters for the digest operation. * @param params.algorithm - The name of hash function to use. * @param params.data - The data to digest. * * @returns A Promise which will be fulfilled with the hash digest. */ async digest({ algorithm, data }) { const hasher = this.getAlgorithm({ algorithm }); const hash2 = await hasher.digest({ algorithm, data }); return hash2; } async encrypt(params) { const algorithm = this.getAlgorithmName({ key: params.key }); const cipher = this.getAlgorithm({ algorithm }); return await cipher.encrypt(params); } async generateKey(params) { const algorithm = this.getAlgorithmName({ algorithm: params.algorithm }); const keyGenerator = this.getAlgorithm({ algorithm }); const privateKey = await keyGenerator.generateKey({ algorithm: params.algorithm }); privateKey.kid ??= await (0, import_crypto8.computeJwkThumbprint)({ jwk: privateKey }); return privateKey; } // ! TODO: Remove this once the `Dsa` interface is updated in @web5/crypto to remove KMS-specific methods. async getKeyUri(_params) { throw new Error("Method not implemented."); } async getPublicKey({ key }) { const algorithm = this.getAlgorithmName({ key }); const keyGenerator = this.getAlgorithm({ algorithm }); const publicKey = await keyGenerator.getPublicKey({ key }); return publicKey; } async privateKeyToBytes({ privateKey }) { const algorithm = this.getAlgorithmName({ key: privateKey }); const keyConverter = this.getAlgorithm({ algorithm }); const privateKeyBytes = await keyConverter.privateKeyToBytes({ privateKey }); return privateKeyBytes; } async publicKeyToBytes({ publicKey }) { const algorithm = this.getAlgorithmName({ key: publicKey }); const keyConverter = this.getAlgorithm({ algorithm }); const publicKeyBytes = await keyConverter.publicKeyToBytes({ publicKey }); return publicKeyBytes; } async sign({ key, data }) { const algorithm = this.getAlgorithmName({ key }); const signer = this.getAlgorithm({ algorithm }); const signature = signer.sign({ data, key }); return signature; } async unwrapKey(params) { const algorithm = this.getAlgorithmName({ key: params.decryptionKey }); const keyWrapper = this.getAlgorithm({ algorithm }); return await keyWrapper.unwrapKey(params); } async verify({ key, signature, data }) { const algorithm = this.getAlgorithmName({ key }); const signer = this.getAlgorithm({ algorithm }); const isSignatureValid = signer.verify({ key, signature, data }); return isSignatureValid; } async wrapKey(params) { const algorithm = this.getAlgorithmName({ key: params.encryptionKey }); const keyWrapper = this.getAlgorithm({ algorithm }); return await keyWrapper.wrapKey(params); } /** * Retrieves an algorithm implementation instance based on the provided algorithm name. * * @remarks * This method checks if the requested algorithm is supported and returns a cached instance * if available. If an instance does not exist, it creates and caches a new one. This approach * optimizes performance by reusing algorithm instances across cryptographic operations. * * @example * ```ts * const signer = this.getAlgorithm({ algorithm: 'Ed25519' }); * ``` * * @param params - The parameters for retrieving the algorithm implementation. * @param params.algorithm - The name of the algorithm to retrieve. * * @returns An instance of the requested algorithm implementation. * * @throws Error if the requested algorithm is not supported. */ getAlgorithm({ algorithm }) { const AlgorithmImplementation = supportedAlgorithms[algorithm]?.["implementation"]; if (!AlgorithmImplementation) { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `Algorithm not supported: ${algorithm}`); } if (!this._algorithmInstances.has(AlgorithmImplementation)) { this._algorithmInstances.set(AlgorithmImplementation, new AlgorithmImplementation()); } return this._algorithmInstances.get(AlgorithmImplementation); } getAlgorithmName({ algorithm, key }) { const algProperty = key?.alg ?? algorithm; const crvProperty = key?.crv; for (const algorithmIdentifier of Object.keys(supportedAlgorithms)) { const algorithmNames = supportedAlgorithms[algorithmIdentifier].names; if (algProperty && algorithmNames.includes(algProperty)) { return algorithmIdentifier; } else if (crvProperty && algorithmNames.includes(crvProperty)) { return algorithmIdentifier; } } throw new CryptoError( "algorithmNotSupported" /* AlgorithmNotSupported */, `Algorithm not supported based on provided input: alg=${algProperty}, crv=${crvProperty}. Please check the documentation for the list of supported algorithms.` ); } }; // src/did-api.ts var import_dids = require("@web5/dids"); // src/store-did.ts var import_common4 = require("@web5/common"); // src/utils-internal.ts var import_crypto9 = require("@web5/crypto"); var TENANT_SEPARATOR = "^"; var DeterministicKeyGenerator = class extends import_crypto9.LocalKeyManager { constructor() { super(); this._predefinedKeys = /* @__PURE__ */ new Map(); this._keyGenerator = this._predefinedKeys.keys(); } async addPredefinedKeys({ privateKeys }) { const predefinedKeys = {}; for (const key of privateKeys) { key.kid ??= await (0, import_crypto9.computeJwkThumbprint)({ jwk: key }); const keyUri = await this.getKeyUri({ key }); predefinedKeys[keyUri] = key; } this._predefinedKeys = new Map(Object.entries(predefinedKeys)); this._keyGenerator = this._predefinedKeys.keys(); } async exportKey({ keyUri }) { const privateKey = this._predefinedKeys.get(keyUri); if (!privateKey) { throw new Error(`DeterministicKeyGenerator.exportKey: Key not found: ${keyUri}`); } return privateKey; } async generateKey(_params) { const { value: keyUri, done } = this._keyGenerator.next(); if (done) { throw new Error("Ran out of predefined keys"); } return keyUri; } async getPublicKey({ keyUri }) { const privateKey = this._predefinedKeys.get(keyUri); if (!privateKey) { throw new Error(`DeterministicKeyGenerator.getPublicKey: Key not found: ${keyUri}`); } const { d, ...publicKey } = privateKey; return publicKey; } async sign({ keyUri, data }) { const privateKey = this._predefinedKeys.get(keyUri); if (!privateKey) { throw new Error(`DeterministicKeyGenerator.sign: Key not found: ${keyUri}`); } const signature = await import_crypto9.Ed25519.sign({ data, key: privateKey }); return signature; } }; async function getDataStoreTenant({ agent, tenant, didUri }) { if (tenant) return tenant; if (agent.agentDid) return agent.agentDid.uri; if (!didUri) { throw new Error(`Failed to determine tenant DID: 'agent.agentDid', 'tenant', and 'didUri' are undefined`); } return didUri; } // src/prototyping/dids/utils.ts function isPortableDid(obj) { return !(!obj || typeof obj !== "object" || obj === null) && "uri" in obj && "document" in obj && "metadata" in obj && (!("keyManager" in obj) || obj.keyManager === void 0); } // src/store-data.ts var import_ms = __toESM(require("ms"), 1); var import_common3 = require("@web5/common"); var DwnDataStore = class { constructor() { this.name = "DwnDataStore"; /** * Cache of Store Objects referenced by DWN record ID to Store Objects. * * Up to 100 entries are retained for 15 minutes. */ this._cache = new import_common3.TtlCache({ ttl: (0, import_ms.default)("15 minutes"), max: 100 }); /** * Index for mappings from Store Identifier to DWN record ID. * * Up to 1,000 entries are retained for 2 hours. */ this._index = new import_common3.TtlCache({ ttl: (0, import_ms.default)("2 hours"), max: 1e3 }); /** * Properties to use when writing and querying records with the DWN store. */ this._recordProperties = { dataFormat: "application/json", schema: "https://identity.foundation/schemas/web5/private-jwk" }; } async delete({ id, agent, tenant }) { const tenantDid = await getDataStoreTenant({ agent, tenant, didUri: id }); let matchingRecordId = await this.lookupRecordId({ id, tenantDid, agent }); if (!matchingRecordId) return false; const { reply: { status } } = await agent.dwn.processRequest({ author: tenantDid, target: tenantDid, messageType: DwnInterface.RecordsDelete, messageParams: { recordId: matchingRecordId } }); if (status.code === 202) { this._index.delete(`${tenantDid}${TENANT_SEPARATOR}${id}`); this._cache.delete(matchingRecordId); return true; } throw new Error(`${this.name}: Failed to delete '${id}' from store: (${status.code}) ${status.detail}`); } async get({ id, agent, tenant, useCache = false }) { const tenantDid = await getDataStoreTenant({ agent, tenant, didUri: id }); let matchingRecordId = await this.lookupRecordId({ id, tenantDid, agent }); if (!matchingRecordId) return void 0; return await this.getRecord({ recordId: matchingRecordId, tenantDid, agent, useCache }); } async list({ agent, tenant }) { const tenantDid = await getDataStoreTenant({ tenant, agent }); const storedRecords = await this.getAllRecords({ agent, tenantDid }); return storedRecords; } async set({ id, data, tenant, agent, preventDuplicates = true, useCache = false }) { const tenantDid = await getDataStoreTenant({ agent, tenant, didUri: id }); if (preventDuplicates) { const matchingRecordId = await this.lookupRecordId({ id, tenantDid, agent }); if (matchingRecordId) { throw new Error(`${this.name}: Import failed due to duplicate entry for: ${id}`); } } const dataBytes = import_common3.Convert.object(data).toUint8Array(); const { message, reply: { status } } = await agent.dwn.processRequest({ author: tenantDid, target: tenantDid, messageType: DwnInterface.RecordsWrite, messageParams: { ...this._recordProperties }, dataStream: new Blob([dataBytes], { type: "application/json" }) }); if (!(message && status.code === 202)) { throw new Error(`${this.name}: Failed to write data to store for: ${id}`); } this._index.set(`${tenantDid}${TENANT_SEPARATOR}${id}`, message.recordId); if (useCache) { this._cache.set(message.recordId, data); } } async getAllRecords(_params) { throw new Error("Not implemented: Classes extending DwnDataStore must implement getAllRecords()"); } async getRecord({ recordId, tenantDid, agent, useCache }) { if (useCache) { const record = this._cache.get(recordId); if (record) return record; } const { reply: readReply } = await agent.dwn.processRequest({ author: tenantDid, target: tenantDid, messageType: DwnInterface.RecordsRead, messageParams: { filter: { recordId } } }); if (!readReply.record?.data) { throw new Error(`${this.name}: Failed to read data from DWN for: ${recordId}`); } const storeObject = await import_common3.NodeStream.consumeToJson({ readable: readReply.record.data }); if (useCache) { this._cache.set(recordId, storeObject); } return storeObject; } async lookupRecordId({ id, tenantDid, agent }) { let recordId = this._index.get(`${tenantDid}${TENANT_SEPARATOR}${id}`, { updateAgeOnGet: true }); if (!recordId) { await this.getAllRecords({ agent, tenantDid }); recordId = this._index.get(`${tenantDid}${TENANT_SEPARATOR}${id}`); } return recordId; } }; var InMemoryDataStore = class { constructor() { this.name = "InMemoryDataStore"; /** * A private field that contains the Map used as the in-memory data store. */ this.store = /* @__PURE__ */ new Map(); } async delete({ id, agent, tenant }) { const tenantDid = await getDataStoreTenant({ agent, tenant, didUri: id }); if (this.store.has(`${tenantDid}${TENANT_SEPARATOR}${id}`)) { this.store.delete(`${tenantDid}${TENANT_SEPARATOR}${id}`); return true; } return false; } async get({ id, agent, tenant }) { const tenantDid = await getDataStoreTenant({ agent, tenant, didUri: id }); return this.store.get(`${tenantDid}${TENANT_SEPARATOR}${id}`); } async list({ agent, tenant }) { const tenantDid = await getDataStoreTenant({ tenant, agent }); const result = []; for (const [key, storedRecord] of this.store.entries()) { if (key.startsWith(`${tenantDid}${TENANT_SEPARATOR}`)) { result.push(storedRecord); } } return result; } async set({ id, data, tenant, agent, preventDuplicates }) { const tenantDid = await getDataStoreTenant({ agent, tenant, didUri: id }); if (preventDuplicates) { const duplicateFound = this.store.has(`${tenantDid}${TENANT_SEPARATOR}${id}`); if (duplicateFound) { throw new Error(`${this.name}: Import failed due to duplicate entry for: ${id}`); } } const clonedData = structuredClone(data); this.store.set(`${tenantDid}${TENANT_SEPARATOR}${id}`, clonedData); } }; // src/store-did.ts var DwnDidStore = class extends DwnDataStore { constructor() { super(...arguments); this.name = "DwnDidStore"; /** * Properties to use when writing and querying DID records with the DWN store. */ this._recordProperties = { dataFormat: "application/json", schema: "https://identity.foundation/schemas/web5/portable-did" }; } async delete(params) { return await super.delete(params); } async get(params) { return await super.get(params); } async list(params) { return await super.list(params); } async set(params) { return await super.set(params); } async getAllRecords({ agent, tenantDid }) { this._index.clear(); const { reply: queryReply } = await agent.dwn.processRequest({ author: tenantDid, target: tenantDid, messageType: DwnInterface.RecordsQuery, messageParams: { filter: { ...this._recordProperties } } }); let storedDids = []; for (const record of queryReply.entries ?? []) { if (!record.encodedData) { throw new Error(`${this.name}: Expected 'encodedData' to be present in the DWN query result entry`); } const storedDid = import_common4.Convert.base64Url(record.encodedData).toObject(); if (isPortableDid(storedDid)) { const indexKey = `${tenantDid}${TENANT_SEPARATOR}${storedDid.uri}`; this._index.set(indexKey, record.recordId); this._cache.set(record.recordId, storedDid); storedDids.push(storedDid); } } return storedDids; } }; var InMemoryDidStore = class extends InMemoryDataStore { constructor() { super(...arguments); this.name = "InMemoryDidStore"; } async delete(params) { return await super.delete(params); } async get(params) { return await super.get(params); } async list(params) { return await super.list(params); } async set(params) { return await super.set(params); } }; // src/prototyping/dids/resolver-cache-memory.ts var import_ms2 = __toESM(require("ms"), 1); var import_common5 = require("@web5/common"); var DidResolverCacheMemory = class { constructor({ ttl = "15m" } = {}) { this.cache = new import_common5.TtlCache({ ttl: (0, import_ms2.default)(ttl) }); } /** * Retrieves a DID resolution result from the cache. * * If the cached item has exceeded its TTL, it's scheduled for deletion and undefined is returned. * * @param didUri - The DID string used as the key for retrieving the cached result. * @returns The cached DID resolution result or undefined if not found or expired. */ async get(didUri) { if (!didUri) { throw new Error("Key cannot be null or undefined"); } return this.cache.get(didUri); } /** * Stores a DID resolution result in the cache with a TTL. * * @param didUri - The DID string used as the key for storing the result. * @param resolutionResult - The DID resolution result to be cached. * @returns A promise that resolves when the operation is complete. */ async set(didUri, resolutionResult) { this.cache.set(didUri, resolutionResult); } /** * Deletes a DID resolution result from the cache. * * @param didUri - The DID string used as the key for deletion. * @returns A promise that resolves when the operation is complete. */ async delete(didUri) { this.cache.delete(didUri); } /** * Clears all entries from the cache. * * @returns A promise that resolves when the operation is complete. */ async clear() { this.cache.clear(); } /** * This method is a no-op but exists to be consistent with other DID Resolver Cache * implementations. * * @returns A promise that resolves immediately. */ async close() { } }; // src/did-api.ts var DidInterface = /* @__PURE__ */ ((DidInterface2) => { DidInterface2["Create"] = "Create"; DidInterface2["Resolve"] = "Resolve"; return DidInterface2; })(DidInterface || {}); function isDidRequest(didRequest, messageType) { return didRequest.messageType === messageType; } var AgentDidApi = class extends import_dids.UniversalResolver { constructor({ agent, didMethods, resolverCache, store }) { if (!didMethods) { throw new TypeError(`AgentDidApi: Required parameter missing: 'didMethods'`); } super({ didResolvers: didMethods, cache: resolverCache ?? new DidResolverCacheMemory() }); this._didMethods = /* @__PURE__ */ new Map(); this._agent = agent; this._store = store ?? new InMemoryDidStore(); for (const didMethod of didMethods) { this._didMethods.set(didMethod.methodName, didMethod); } } /** * Retrieves the `Web5PlatformAgent` execution context. * * @returns The `Web5PlatformAgent` instance that represents the current execution context. * @throws Will throw an error if the `agent` instance property is undefined. */ get agent() { if (this._agent === void 0) { throw new Error("AgentDidApi: Unable to determine agent execution context."); } return this._agent; } set agent(agent) { this._agent = agent; } async create({ method, tenant, options, store }) { const didMethod = this.getMethod(method); const bearerDid = await didMethod.create({ keyManager: this.agent.keyManager, options }); if (store ?? true) { const { uri, document, metadata } = bearerDid; const portableDid = { uri, document, metadata }; await this._store.set({ id: portableDid.uri, data: portableDid, agent: this.agent, tenant: tenant ?? portableDid.uri, preventDuplicates: false, useCache: true }); } return bearerDid; } async export({ didUri, tenant }) { const bearerDid = await this.get({ didUri, tenant }); if (!bearerDid) { throw new Error(`AgentDidApi: Failed to export due to DID not found: ${didUri}`); } const portableDid = await bearerDid.export(); return portableDid; } async get({ didUri, tenant }) { const portableDid = await this._store.get({ id: didUri, agent: this.agent, tenant, useCache: true }); if (!portableDid) return void 0; const bearerDid = await import_dids.BearerDid.import({ portableDid, keyManager: this.agent.keyManager }); return bearerDid; } async getSigningMethod({ didUri, methodId }) { const parsedDid = import_dids.Did.parse(didUri); if (!parsedDid) { throw new Error(`Invalid DID URI: ${didUri}`); } const didMethod = this.getMethod(parsedDid.method); const { didDocument, didResolutionMetadata } = await this.resolve(didUri); if (!didDocument) { throw new Error(`DID resolution failed for '${didUri}': ${JSON.stringify(didResolutionMetadata)}`); } const verificationMethod = await didMethod.getSigningMethod({ didDocument, methodId }); return verificationMethod; } async import({ portableDid, tenant }) { const bearerDid = await import_dids.BearerDid.import({ keyManager: this.agent.keyManager, portableDid }); const { uri, document, metadata } = bearerDid; const portableDidWithoutKeys = { uri, document, metadata }; await this._store.set({ id: portableDidWithoutKeys.uri, data: portableDidWithoutKeys, agent: this.agent, tenant: tenant ?? portableDidWithoutKeys.uri, preventDuplicates: true, useCache: true }); return bearerDid; } async processRequest(request) { if (isDidRequest(request, "Create" /* Create */)) { try { const bearerDid = await this.create({ ...request.messageParams }); const response = { result: { uri: bearerDid.uri, document: bearerDid.document, metadata: bearerDid.metadata }, ok: true, status: { code: 201, message: "Created" } }; return response; } catch (error) { return { ok: false, status: { code: 500, message: error.message ?? "Unknown error occurred" } }; } } if (isDidRequest(request, "Resolve" /* Resolve */)) { const { didUri, options } = request.messageParams; const resolutionResult = await this.resolve(didUri, options); const response = { result: resolutionResult, ok: true, status: { code: 200, message: "OK" } }; return response; } throw new Error(`AgentDidApi: Unsupported request type: ${request.messageType}`); } getMethod(methodName) { const didMethodApi = this._didMethods.get(methodName); if (didMethodApi === void 0) { throw new Error(`DID Method not supported: ${methodName}`); } return didMethodApi; } }; // src/dwn-api.ts var import_common6 = require("@web5/common"); var import_crypto10 = require("@web5/crypto"); var import_dids3 = require("@web5/dids"); var import_dwn_sdk_js4 = require("@tbd54566975/dwn-sdk-js"); // src/utils.ts var import_dids2 = require("@web5/dids"); var import_readable_web_to_node_stream = require("readable-web-to-node-stream"); var import_dwn_sdk_js3 = require("@tbd54566975/dwn-sdk-js"); function blobToIsomorphicNodeReadable(blob) { return webReadableToIsomorphicNodeReadable(blob.stream()); } async function getDwnServiceEndpointUrls(didUri, dereferencer) { const dereferencingResult = await dereferencer.dereference(`${didUri}#dwn`); if (dereferencingResult.dereferencingMetadata.error) { throw new Error(`Failed to dereference '${didUri}#dwn': ${dereferencingResult.dereferencingMetadata.error}`); } if (import_dids2.utils.isDwnDidService(dereferencingResult.contentStream)) { const { serviceEndpoint } = dereferencingResult.contentStream; const serviceEndpointUrls = typeof serviceEndpoint === "string" ? [serviceEndpoint] : Array.isArray(serviceEndpoint) && serviceEndpoint.every((endpoint) => typeof endpoint === "string") ? serviceEndpoint : []; if (serviceEndpointUrls.length > 0) { return serviceEndpointUrls; } } return []; } function getRecordAuthor(record) { return import_dwn_sdk_js3.Records.getAuthor(record); } function isRecordsWrite(obj) { if (!obj || typeof obj !== "object" || obj === null) return false; return "message" in obj && typeof obj.message === "object" && obj.message !== null && "descriptor" in obj.message && typeof obj.message.descriptor === "object" && obj.message.descriptor !== null && "interface" in obj.message.descriptor && obj.message.descriptor.interface === import_dwn_sdk_js3.DwnInterfaceName.Records && "method" in obj.message.descriptor && obj.message.descriptor.method === import_dwn_sdk_js3.DwnMethodName.Write; } function getRecordMessageCid(message) { return import_dwn_sdk_js3.Message.getCid(message); } async function getPaginationCursor(message, dateSort) { const value = dateSort === import_dwn_sdk_js3.DateSort.CreatedAscending || dateSort === import_dwn_sdk_js3.DateSort.CreatedDescending ? message.descriptor.dateCreated : message.descriptor.datePublished; if (value === void 0) { throw new Error("The dateCreated or datePublished property is missing from the record descriptor."); } return { messageCid: await getRecordMessageCid(message), value }; } function webReadableToIsomorphicNodeReadable(webReadable) { return new import_readable_web_to_node_stream.ReadableWebToNodeStream(webReadable); } // src/dwn-api.ts function isDwnRequest(dwnRequest, messageType) { return dwnRequest.messageType === messageType; } function isDwnMessage(messageType, message) { const incomingMessageInterfaceName = message.descriptor.interface + message.descriptor.method; return incomingMessageInterfaceName === messageType; } var AgentDwnApi = class { constructor({ agent, dwn }) { this._agent = agent; this._dwn = dwn; } /** * Retrieves the `Web5PlatformAgent` execution context. * * @returns The `Web5PlatformAgent` instance that represents the current execution context. * @throws Will throw an error if the `agent` instance property is undefined. */ get agent() { if (this._agent === void 0) { throw new Error("AgentDwnApi: Unable to determine agent execution context."); } return this._agent; } set agent(agent) { this._agent = agent; } /** * Public getter for the DWN instance used by this API. * * Notes: * - This getter is public to allow advanced developers to access the DWN instance directly. * However, it is recommended to use the `processRequest` method to interact with the DWN * instance to ensure that the DWN message is constructed correctly. * - The getter is named `node` to avoid confusion with the `dwn` property of the * `Web5PlatformAgent`. In other words, so that a developer can call `agent.dwn.node` to access * the DWN instance and not `agent.dwn.dwn`. */ get node() { return this._dwn; } static async createDwn({ dataPath, dataStore, didResolver, eventLog, eventStream, messageStore, tenantGate }) { dataStore ??= new import_dwn_sdk_js4.DataStoreLevel({ blockstoreLocation: `${dataPath}/DWN_DATASTORE` }); didResolver ??= new import_dids3.UniversalResolver({ didResolvers: [import_dids3.DidDht, import_dids3.DidJwk], cache: new import_dids3.DidResolverCacheLevel({ location: `${dataPath}/DID_RESOLVERCACHE` }) }); eventLog ??= new import_dwn_sdk_js4.EventLogLevel({ location: `${dataPath}/DWN_EVENTLOG` }); messageStore ??= new import_dwn_sdk_js4.MessageStoreLevel({ blockstoreLocation: `${dataPath}/DWN_MESSAGESTORE`, indexLocation: `${dataPath}/DWN_MESSAGEINDEX` }); return await import_dwn_sdk_js4.Dwn.create({ dataStore, didResolver, eventLog, eventStream, messageStore, tenantGate }); } async processRequest(request) { const { message, dataStream } = await this.constructDwnMessage({ request }); const { subscriptionHandler } = request; const reply = request.store !== false ? await this._dwn.processMessage(request.target, message, { dataStream, subscriptionHandler }) : { status: { code: 202, detail: "Accepted" } }; return { reply, message, messageCid: await import_dwn_sdk_js4.Message.getCid(message) }; } async sendRequest(request) { const dwnEndpointUrls = await getDwnServiceEndpointUrls(request.target, this.agent.did); if (dwnEndpointUrls.length === 0) { throw new Error(`AgentDwnApi: DID Service is missing or malformed: ${request.target}#dwn`); } let messageCid; let message; let data; let subscriptionHandler; if ("messageCid" in request) { ({ message, data } = await this.getDwnMessage({ author: request.author, messageCid: request.messageCid, messageType: request.messageType })); messageCid = request.messageCid; } else { ({ message } = await this.constructDwnMessage({ request })); if (request.dataStream && !(request.dataStream instanceof Blob)) { throw new Error("AgentDwnApi: DataStream must be provided as a Blob"); } data = request.dataStream; subscriptionHandler = request.subscriptionHandler; } const reply = await this.sendDwnRpcRequest({ targetDid: request.target, dwnEndpointUrls, message, data, subscriptionHandler }); messageCid ??= await import_dwn_sdk_js4.Message.getCid(message); return { reply, message, messageCid }; } async sendDwnRpcRequest({ targetDid, dwnEndpointUrls, message, data, subscriptionHandler }) { const errorMessages = []; if (message.descriptor.method === import_dwn_sdk_js4.DwnMethodName.Subscribe && subscriptionHandler === void 0) { throw new Error("AgentDwnApi: Subscription handler is required for subscription requests."); } for (let dwnUrl of dwnEndpointUrls) { try { if (subscriptionHandler !== void 0) { const serverInfo = await this.agent.rpc.getServerInfo(dwnUrl); if (!serverInfo.webSocketSupport) { errorMessages.push({ url: dwnUrl, message: "WebSocket support is not enabled on the server." }); continue; } const parsedUrl = new URL(dwnUrl); parsedUrl.protocol = parsedUrl.protocol === "http:" ? "ws:" : "wss:"; dwnUrl = parsedUrl.toString(); } const dwnReply = await this.agent.rpc.sendDwnRequest({ dwnUrl, targetDid, message, data, subscriptionHandler }); return dwnReply; } catch (error) { errorMessages.push({ url: dwnUrl, message: error instanceof Error ? error.message : "Unknown error" }); } } throw new Error(`Failed to send DWN RPC request: ${JSON.stringify(errorMessages)}`); } async constructDwnMessage({ request }) { const rawMessage = request.rawMessage; let readableStream; if (isDwnRequest(request, DwnInterface.RecordsWrite)) { const messageParams = request.messageParams; if (request.dataStream && !messageParams?.data) { const { dataStream } = request; let isomorphicNodeReadable; if (dataStream instanceof Blob) { isomorphicNodeReadable = blobToIsomorphicNodeReadable(dataStream); readableStream = blobToIsomorphicNodeReadable(dataStream); } else if (dataStream instanceof ReadableStream) { const [forCid, forProcessMessage] = dataStream.tee(); isomorphicNodeReadable = webReadableToIsomorphicNodeReadable(forCid); readableStream = webReadableToIsomorphicNodeReadable(forProcessMessage); } if (!rawMessage) { messageParams.dataCid = await import_dwn_sdk_js4.Cid.computeDagPbCidFromStream(isomorphicNodeReadable); messageParams.dataSize ??= isomorphicNodeReadable["bytesRead"]; } } } const signer = await this.getSigner(request.author); const dwnMessageConstructor = dwnMessageConstructors[request.messageType]; const dwnMessage = rawMessage ? await dwnMessageConstructor.parse(rawMessage) : await dwnMessageConstructor.create({ // TODO: Implement alternative to type assertion. ...request.messageParams, signer }); if (isRecordsWrite(dwnMessage) && request.signAsOwner) { await dwnMessage.signAsOwner(signer); } return { message: dwnMessage.message, dataStream: readableStream }; } async getSigner(author) { if (author === this.agent.agentDid.uri) { const signer = await this.agent.agentDid.getSigner(); return { algorithm: signer.algorithm, keyId: signer.keyId, sign: async (data) => { return await signer.sign({ data }); } }; } else { try { const signingMethod = await this.agent.did.getSigningMethod({ didUri: author }); if (!signingMethod.publicKeyJwk) { throw new Error(`Verification method '${signingMethod.id}' does not contain a public key in JWK format`); } const keyUri = await this.agent.keyManager.getKeyUri({ key: signingMethod.publicKeyJwk }); const publicKey = await this.agent.keyManager.getPublicKey({ keyUri }); const keyManager = this.agent.keyManager; return { algorithm: import_crypto10.utils.getJoseSignatureAlgorithmFromPublicKey(publicKey), keyId: signingMethod.id, sign: async (data) => { return await keyManager.sign({ data, keyUri }); } }; } catch (error) { throw new Error(`AgentDwnApi: Unable to get signer for author '${author}': ${error.message}`); } } } /** * FURTHER REFACTORING NEEDED BELOW THIS LINE */ async getDwnMessage({ author, messageCid }) { const signer = await this.getSigner(author); const messagesGet = await dwnMessageConstructors[DwnInterface.MessagesGet].create({ messageCids: [messageCid], signer }); const result = await this._dwn.processMessage(author, messagesGet.message); if (!(result.entries && result.entries.length === 1)) { throw new Error("AgentDwnApi: Expected 1 message entry in the MessagesGet response but received none or more than one."); } const [messageEntry] = result.entries; const message = messageEntry.message; if (!message) { throw new Error(`AgentDwnApi: Message not found with CID: ${messageCid}`); } let dwnMessageWithBlob = { message }; if (isRecordsWrite(messageEntry)) { if (messageEntry.encodedData) { const dataBytes = import_common6.Convert.base64Url(messageEntry.encodedData).toUint8Array(); dwnMessageWithBlob.data = new Blob([dataBytes]); } else { const recordsRead = await dwnMessageConstructors[DwnInterface.RecordsRead].create({ filter: { recordId: messageEntry.message.recordId }, signer }); const reply = await this._dwn.processMessage(author, recordsRead.message); if (reply.status.code >= 400) { const { status: { code, detail } } = reply; throw new Error(`AgentDwnApi: (${code}) Failed to read data associated with record ${messageEntry.message.recordId}. ${detail}}`); } else if (reply.record) { const dataBytes = await import_common6.NodeStream.consumeToBytes({ readable: reply.record.data }); dwnMessageWithBlob.data = new Blob([dataBytes]); } } } return dwnMessageWithBlob; } /** * TODO: Refactor this to consolidate logic in AgentDwnApi and SyncEngineLevel. * ADDED TO GET SYNC WORKING * - createMessage() * - processMessage() */ async createMessage({ author, messageParams, messageType }) { const signer = await this.getSigner(author); const dwnMessageConstructor = dwnMessageConstructors[messageType]; const dwnMessage = await dwnMessageConstructor.create({ // TODO: Explore whether 'messageParams' should be required in the ProcessDwnRequest type. ...messageParams, signer }); return dwnMessage; } async processMessage({ dataStream, message, targetDid }) { return await this._dwn.processMessage(targetDid, message, { dataStream }); } }; // ../../node_modules/.pnpm/@noble+hashes@1.3.3/node_modules/@noble/hashes/esm/_assert.js function number(n) { if (!Number.isSafeInteger(n) || n < 0) throw new Error(`Wrong positive integer: ${n}`); } function isBytes(a) { return a instanceof Uint8Array || a != null && typeof a === "object" && a.constructor.name === "Uint8Array"; } function bytes(b, ...lengths) { if (!isBytes(b)) throw new Error("Expected Uint8Array"); if (lengths.length > 0 && !lengths.includes(b.length)) throw new Error(`Expected Uint8Array of length ${lengths}, not of length=${b.length}`); } function hash(hash2) { if (typeof hash2 !== "function" || typeof hash2.create !== "function") throw new Error("Hash should be wrapped by utils.wrapConstructor"); number(hash2.outputLen); number(hash2.blockLen); } function exists(instance, checkFinished = true) { if (instance.destroyed) throw new Error("Hash instance has been destroyed"); if (checkFinished && instance.finished) throw new Error("Hash#digest() has already been called"); } function output(out, instance) { bytes(out); const min = instance.outputLen; if (out.length < min) { throw new Error(`digestInto() expects output buffer of length at least ${min}`); } } // ../../node_modules/.pnpm/@noble+hashes@1.3.3/node_modules/@noble/hashes/esm/cryptoNode.js var nc = __toESM(require("node:crypto"), 1); var crypto2 = nc && typeof nc === "object" && "webcrypto" in nc ? nc.webcrypto : void 0; // ../../node_modules/.pnpm/@noble+hashes@1.3.3/node_modules/@noble/hashes/esm/utils.js function isBytes2(a) { return a instanceof Uint8Array || a != null && typeof a === "object" && a.constructor.name === "Uint8Array"; } var createView = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength); var rotr = (word, shift) => word << 32 - shift | word >>> shift; var isLE = new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68; if (!isLE) throw new Error("Non little-endian hardware is not supported"); var hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0")); function bytesToHex(bytes2) { if (!isBytes2(bytes2)) throw new Error("Uint8Array expected"); let hex = ""; for (let i = 0; i < bytes2.length; i++) { hex += hexes[bytes2[i]]; } return hex; } var asciis = { _0: 48, _9: 57, _A: 65, _F: 70, _a: 97, _f: 102 }; function asciiToBase16(char) { if (char >= asciis._0 && char <= asciis._9) return char - asciis._0; if (char >= asciis._A && char <= asciis._F) return char - (asciis._A - 10); if (char >= asciis._a && char <= asciis._f) return char - (asciis._a - 10); return; } function hexToBytes(hex) { if (typeof hex !== "string") throw new Error("hex string expected, got " + typeof hex); const hl = hex.length; const al = hl / 2; if (hl % 2) throw new Error("padded hex string expected, got unpadded hex of length " + hl); const array = new Uint8Array(al); for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) { const n1 = asciiToBase16(hex.charCodeAt(hi)); const n2 = asciiToBase16(hex.charCodeAt(hi + 1)); if (n1 === void 0 || n2 === void 0) { const char = hex[hi] + hex[hi + 1]; throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi); } array[ai] = n1 * 16 + n2; } return array; } function utf8ToBytes(str) { if (typeof str !== "string") throw new Error(`utf8ToBytes expected string, got ${typeof str}`); return new Uint8Array(new TextEncoder().encode(str)); } function toBytes(data) { if (typeof data === "string") data = utf8ToBytes(data); if (!isBytes2(data)) throw new Error(`expected Uint8Array, got ${typeof data}`); return data; } function concatBytes(...arrays) { let sum = 0; for (let i = 0; i < arrays.length; i++) { const a = arrays[i]; if (!isBytes2(a)) throw new Error("Uint8Array expected"); sum += a.length; } const res = new Uint8Array(sum); for (let i = 0, pad = 0; i < arrays.length; i++) { const a = arrays[i]; res.set(a, pad); pad += a.length; } return res; } var Hash = class { // Safe version that clones internal state clone() { return this._cloneInto(); } }; var toStr = {}.toString; function wrapConstructor(hashCons) { const hashC = (msg) => hashCons().update(toBytes(msg)).digest(); const tmp = hashCons(); hashC.outputLen = tmp.outputLen; hashC.blockLen = tmp.blockLen; hashC.create = () => hashCons(); return hashC; } function randomBytes(bytesLength = 32) { if (crypto2 && typeof crypto2.getRandomValues === "function") { return crypto2.getRandomValues(new Uint8Array(bytesLength)); } throw new Error("crypto.getRandomValues must be defined"); } // ../../node_modules/.pnpm/@noble+hashes@1.3.3/node_modules/@noble/hashes/esm/_sha2.js function setBigUint64(view, byteOffset, value, isLE2) { if (typeof view.setBigUint64 === "function") return view.setBigUint64(byteOffset, value, isLE2); const _32n2 = BigInt(32); const _u32_max = BigInt(4294967295); const wh = Number(value >> _32n2 & _u32_max); const wl = Number(value & _u32_max); const h = isLE2 ? 4 : 0; const l = isLE2 ? 0 : 4; view.setUint32(byteOffset + h, wh, isLE2); view.setUint32(byteOffset + l, wl, isLE2); } var SHA2 = class extends Hash { constructor(blockLen, outputLen, padOffset, isLE2) { super(); this.blockLen = blockLen; this.outputLen = outputLen; this.padOffset = padOffset; this.isLE = isLE2; this.finished = false; this.length = 0; this.pos = 0; this.destroyed = false; this.buffer = new Uint8Array(blockLen); this.view = createView(this.buffer); } update(data) { exists(this); const { view, buffer, blockLen } = this; data = toBytes(data); const len = data.length; for (let pos = 0; pos < len; ) { const take = Math.min(blockLen - this.pos, len - pos); if (take === blockLen) { const dataView = createView(data); for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos); continue; } buffer.set(data.subarray(pos, pos + take), this.pos); this.pos += take; pos += take; if (this.pos === blockLen) { this.process(view, 0); this.pos = 0; } } this.length += data.length; this.roundClean(); return this; } digestInto(out) { exists(this); output(out, this); this.finished = true; const { buffer, view, blockLen, isLE: isLE2 } = this; let { pos } = this; buffer[pos++] = 128; this.buffer.subarray(pos).fill(0); if (this.padOffset > blockLen - pos) { this.process(view, 0); pos = 0; } for (let i = pos; i < blockLen; i++) buffer[i] = 0; setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE2); this.process(view, 0); const oview = createView(out); const len = this.outputLen; if (len % 4) throw new Error("_sha2: outputLen should be aligned to 32bit"); const outLen = len / 4; const state = this.get(); if (outLen > state.length) throw new Error("_sha2: outputLen bigger than state"); for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE2); } digest() { const { buffer, outputLen } = this; this.digestInto(buffer); const res = buffer.slice(0, outputLen); this.destroy(); return res; } _cloneInto(to) { to || (to = new this.constructor()); to.set(...this.get()); const { blockLen, buffer, length, finished, destroyed, pos } = this; to.length = length; to.pos = pos; to.finished = finished; to.destroyed = destroyed; if (length % blockLen) to.buffer.set(buffer); return to; } }; // ../../node_modules/.pnpm/@noble+hashes@1.3.3/node_modules/@noble/hashes/esm/_u64.js var U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1); var _32n = /* @__PURE__ */ BigInt(32); function fromBig(n, le = false) { if (le) return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) }; return { h: Number(n >> _32n & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 }; } function split(lst, le = false) { let Ah = new Uint32Array(lst.length); let Al = new Uint32Array(lst.length); for (let i = 0; i < lst.length; i++) { const { h, l } = fromBig(lst[i], le); [Ah[i], Al[i]] = [h, l]; } return [Ah, Al]; } var toBig = (h, l) => BigInt(h >>> 0) << _32n | BigInt(l >>> 0); var shrSH = (h, _l, s) => h >>> s; var shrSL = (h, l, s) => h << 32 - s | l >>> s; var rotrSH = (h, l, s) => h >>> s | l << 32 - s; var rotrSL = (h, l, s) => h << 32 - s | l >>> s; var rotrBH = (h, l, s) => h << 64 - s | l >>> s - 32; var rotrBL = (h, l, s) => h >>> s - 32 | l << 64 - s; var rotr32H = (_h, l) => l; var rotr32L = (h, _l) => h; var rotlSH = (h, l, s) => h << s | l >>> 32 - s; var rotlSL = (h, l, s) => l << s | h >>> 32 - s; var rotlBH = (h, l, s) => l << s - 32 | h >>> 64 - s; var rotlBL = (h, l, s) => h << s - 32 | l >>> 64 - s; function add(Ah, Al, Bh, Bl) { const l = (Al >>> 0) + (Bl >>> 0); return { h: Ah + Bh + (l / 2 ** 32 | 0) | 0, l: l | 0 }; } var add3L = (Al, Bl, Cl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0); var add3H = (low, Ah, Bh, Ch) => Ah + Bh + Ch + (low / 2 ** 32 | 0) | 0; var add4L = (Al, Bl, Cl, Dl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0); var add4H = (low, Ah, Bh, Ch, Dh) => Ah + Bh + Ch + Dh + (low / 2 ** 32 | 0) | 0; var add5L = (Al, Bl, Cl, Dl, El) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0); var add5H = (low, Ah, Bh, Ch, Dh, Eh) => Ah + Bh + Ch + Dh + Eh + (low / 2 ** 32 | 0) | 0; var u64 = { fromBig, split, toBig, shrSH, shrSL, rotrSH, rotrSL, rotrBH, rotrBL, rotr32H, rotr32L, rotlSH, rotlSL, rotlBH, rotlBL, add, add3L, add3H, add4L, add4H, add5H, add5L }; var u64_default = u64; // ../../node_modules/.pnpm/@noble+hashes@1.3.3/node_modules/@noble/hashes/esm/sha512.js var [SHA512_Kh, SHA512_Kl] = /* @__PURE__ */ (() => u64_default.split([ "0x428a2f98d728ae22", "0x7137449123ef65cd", "0xb5c0fbcfec4d3b2f", "0xe9b5dba58189dbbc", "0x3956c25bf348b538", "0x59f111f1b605d019", "0x923f82a4af194f9b", "0xab1c5ed5da6d8118", "0xd807aa98a3030242", "0x12835b0145706fbe", "0x243185be4ee4b28c", "0x550c7dc3d5ffb4e2", "0x72be5d74f27b896f", "0x80deb1fe3b1696b1", "0x9bdc06a725c71235", "0xc19bf174cf692694", "0xe49b69c19ef14ad2", "0xefbe4786384f25e3", "0x0fc19dc68b8cd5b5", "0x240ca1cc77ac9c65", "0x2de92c6f592b0275", "0x4a7484aa6ea6e483", "0x5cb0a9dcbd41fbd4", "0x76f988da831153b5", "0x983e5152ee66dfab", "0xa831c66d2db43210", "0xb00327c898fb213f", "0xbf597fc7beef0ee4", "0xc6e00bf33da88fc2", "0xd5a79147930aa725", "0x06ca6351e003826f", "0x142929670a0e6e70", "0x27b70a8546d22ffc", "0x2e1b21385c26c926", "0x4d2c6dfc5ac42aed", "0x53380d139d95b3df", "0x650a73548baf63de", "0x766a0abb3c77b2a8", "0x81c2c92e47edaee6", "0x92722c851482353b", "0xa2bfe8a14cf10364", "0xa81a664bbc423001", "0xc24b8b70d0f89791", "0xc76c51a30654be30", "0xd192e819d6ef5218", "0xd69906245565a910", "0xf40e35855771202a", "0x106aa07032bbd1b8", "0x19a4c116b8d2d0c8", "0x1e376c085141ab53", "0x2748774cdf8eeb99", "0x34b0bcb5e19b48a8", "0x391c0cb3c5c95a63", "0x4ed8aa4ae3418acb", "0x5b9cca4f7763e373", "0x682e6ff3d6b2b8a3", "0x748f82ee5defb2fc", "0x78a5636f43172f60", "0x84c87814a1f0ab72", "0x8cc702081a6439ec", "0x90befffa23631e28", "0xa4506cebde82bde9", "0xbef9a3f7b2c67915", "0xc67178f2e372532b", "0xca273eceea26619c", "0xd186b8c721c0c207", "0xeada7dd6cde0eb1e", "0xf57d4f7fee6ed178", "0x06f067aa72176fba", "0x0a637dc5a2c898a6", "0x113f9804bef90dae", "0x1b710b35131c471b", "0x28db77f523047d84", "0x32caab7b40c72493", "0x3c9ebe0a15c9bebc", "0x431d67c49c100d4c", "0x4cc5d4becb3e42b6", "0x597f299cfc657e2a", "0x5fcb6fab3ad6faec", "0x6c44198c4a475817" ].map((n) => BigInt(n))))(); var SHA512_W_H = /* @__PURE__ */ new Uint32Array(80); var SHA512_W_L = /* @__PURE__ */ new Uint32Array(80); var SHA512 = class extends SHA2 { constructor() { super(128, 64, 16, false); this.Ah = 1779033703 | 0; this.Al = 4089235720 | 0; this.Bh = 3144134277 | 0; this.Bl = 2227873595 | 0; this.Ch = 1013904242 | 0; this.Cl = 4271175723 | 0; this.Dh = 2773480762 | 0; this.Dl = 1595750129 | 0; this.Eh = 1359893119 | 0; this.El = 2917565137 | 0; this.Fh = 2600822924 | 0; this.Fl = 725511199 | 0; this.Gh = 528734635 | 0; this.Gl = 4215389547 | 0; this.Hh = 1541459225 | 0; this.Hl = 327033209 | 0; } // prettier-ignore get() { const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this; return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl]; } // prettier-ignore set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl) { this.Ah = Ah | 0; this.Al = Al | 0; this.Bh = Bh | 0; this.Bl = Bl | 0; this.Ch = Ch | 0; this.Cl = Cl | 0; this.Dh = Dh | 0; this.Dl = Dl | 0; this.Eh = Eh | 0; this.El = El | 0; this.Fh = Fh | 0; this.Fl = Fl | 0; this.Gh = Gh | 0; this.Gl = Gl | 0; this.Hh = Hh | 0; this.Hl = Hl | 0; } process(view, offset) { for (let i = 0; i < 16; i++, offset += 4) { SHA512_W_H[i] = view.getUint32(offset); SHA512_W_L[i] = view.getUint32(offset += 4); } for (let i = 16; i < 80; i++) { const W15h = SHA512_W_H[i - 15] | 0; const W15l = SHA512_W_L[i - 15] | 0; const s0h = u64_default.rotrSH(W15h, W15l, 1) ^ u64_default.rotrSH(W15h, W15l, 8) ^ u64_default.shrSH(W15h, W15l, 7); const s0l = u64_default.rotrSL(W15h, W15l, 1) ^ u64_default.rotrSL(W15h, W15l, 8) ^ u64_default.shrSL(W15h, W15l, 7); const W2h = SHA512_W_H[i - 2] | 0; const W2l = SHA512_W_L[i - 2] | 0; const s1h = u64_default.rotrSH(W2h, W2l, 19) ^ u64_default.rotrBH(W2h, W2l, 61) ^ u64_default.shrSH(W2h, W2l, 6); const s1l = u64_default.rotrSL(W2h, W2l, 19) ^ u64_default.rotrBL(W2h, W2l, 61) ^ u64_default.shrSL(W2h, W2l, 6); const SUMl = u64_default.add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]); const SUMh = u64_default.add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]); SHA512_W_H[i] = SUMh | 0; SHA512_W_L[i] = SUMl | 0; } let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this; for (let i = 0; i < 80; i++) { const sigma1h = u64_default.rotrSH(Eh, El, 14) ^ u64_default.rotrSH(Eh, El, 18) ^ u64_default.rotrBH(Eh, El, 41); const sigma1l = u64_default.rotrSL(Eh, El, 14) ^ u64_default.rotrSL(Eh, El, 18) ^ u64_default.rotrBL(Eh, El, 41); const CHIh = Eh & Fh ^ ~Eh & Gh; const CHIl = El & Fl ^ ~El & Gl; const T1ll = u64_default.add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]); const T1h = u64_default.add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]); const T1l = T1ll | 0; const sigma0h = u64_default.rotrSH(Ah, Al, 28) ^ u64_default.rotrBH(Ah, Al, 34) ^ u64_default.rotrBH(Ah, Al, 39); const sigma0l = u64_default.rotrSL(Ah, Al, 28) ^ u64_default.rotrBL(Ah, Al, 34) ^ u64_default.rotrBL(Ah, Al, 39); const MAJh = Ah & Bh ^ Ah & Ch ^ Bh & Ch; const MAJl = Al & Bl ^ Al & Cl ^ Bl & Cl; Hh = Gh | 0; Hl = Gl | 0; Gh = Fh | 0; Gl = Fl | 0; Fh = Eh | 0; Fl = El | 0; ({ h: Eh, l: El } = u64_default.add(Dh | 0, Dl | 0, T1h | 0, T1l | 0)); Dh = Ch | 0; Dl = Cl | 0; Ch = Bh | 0; Cl = Bl | 0; Bh = Ah | 0; Bl = Al | 0; const All = u64_default.add3L(T1l, sigma0l, MAJl); Ah = u64_default.add3H(All, T1h, sigma0h, MAJh); Al = All | 0; } ({ h: Ah, l: Al } = u64_default.add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0)); ({ h: Bh, l: Bl } = u64_default.add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0)); ({ h: Ch, l: Cl } = u64_default.add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0)); ({ h: Dh, l: Dl } = u64_default.add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0)); ({ h: Eh, l: El } = u64_default.add(this.Eh | 0, this.El | 0, Eh | 0, El | 0)); ({ h: Fh, l: Fl } = u64_default.add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0)); ({ h: Gh, l: Gl } = u64_default.add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0)); ({ h: Hh, l: Hl } = u64_default.add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0)); this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl); } roundClean() { SHA512_W_H.fill(0); SHA512_W_L.fill(0); } destroy() { this.buffer.fill(0); this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } }; var sha512 = /* @__PURE__ */ wrapConstructor(() => new SHA512()); // ../../node_modules/.pnpm/@noble+curves@1.3.0/node_modules/@noble/curves/esm/abstract/utils.js var _0n = BigInt(0); var _1n = BigInt(1); var _2n = BigInt(2); function isBytes3(a) { return a instanceof Uint8Array || a != null && typeof a === "object" && a.constructor.name === "Uint8Array"; } var hexes2 = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0")); function bytesToHex2(bytes2) { if (!isBytes3(bytes2)) throw new Error("Uint8Array expected"); let hex = ""; for (let i = 0; i < bytes2.length; i++) { hex += hexes2[bytes2[i]]; } return hex; } function hexToNumber(hex) { if (typeof hex !== "string") throw new Error("hex string expected, got " + typeof hex); return BigInt(hex === "" ? "0" : `0x${hex}`); } var asciis2 = { _0: 48, _9: 57, _A: 65, _F: 70, _a: 97, _f: 102 }; function asciiToBase162(char) { if (char >= asciis2._0 && char <= asciis2._9) return char - asciis2._0; if (char >= asciis2._A && char <= asciis2._F) return char - (asciis2._A - 10); if (char >= asciis2._a && char <= asciis2._f) return char - (asciis2._a - 10); return; } function hexToBytes2(hex) { if (typeof hex !== "string") throw new Error("hex string expected, got " + typeof hex); const hl = hex.length; const al = hl / 2; if (hl % 2) throw new Error("padded hex string expected, got unpadded hex of length " + hl); const array = new Uint8Array(al); for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) { const n1 = asciiToBase162(hex.charCodeAt(hi)); const n2 = asciiToBase162(hex.charCodeAt(hi + 1)); if (n1 === void 0 || n2 === void 0) { const char = hex[hi] + hex[hi + 1]; throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi); } array[ai] = n1 * 16 + n2; } return array; } function bytesToNumberBE(bytes2) { return hexToNumber(bytesToHex2(bytes2)); } function bytesToNumberLE(bytes2) { if (!isBytes3(bytes2)) throw new Error("Uint8Array expected"); return hexToNumber(bytesToHex2(Uint8Array.from(bytes2).reverse())); } function numberToBytesBE(n, len) { return hexToBytes2(n.toString(16).padStart(len * 2, "0")); } function numberToBytesLE(n, len) { return numberToBytesBE(n, len).reverse(); } function ensureBytes(title, hex, expectedLength) { let res; if (typeof hex === "string") { try { res = hexToBytes2(hex); } catch (e) { throw new Error(`${title} must be valid hex string, got "${hex}". Cause: ${e}`); } } else if (isBytes3(hex)) { res = Uint8Array.from(hex); } else { throw new Error(`${title} must be hex string or Uint8Array`); } const len = res.length; if (typeof expectedLength === "number" && len !== expectedLength) throw new Error(`${title} expected ${expectedLength} bytes, got ${len}`); return res; } function concatBytes2(...arrays) { let sum = 0; for (let i = 0; i < arrays.length; i++) { const a = arrays[i]; if (!isBytes3(a)) throw new Error("Uint8Array expected"); sum += a.length; } let res = new Uint8Array(sum); let pad = 0; for (let i = 0; i < arrays.length; i++) { const a = arrays[i]; res.set(a, pad); pad += a.length; } return res; } var bitMask = (n) => (_2n << BigInt(n - 1)) - _1n; var validatorFns = { bigint: (val) => typeof val === "bigint", function: (val) => typeof val === "function", boolean: (val) => typeof val === "boolean", string: (val) => typeof val === "string", stringOrUint8Array: (val) => typeof val === "string" || isBytes3(val), isSafeInteger: (val) => Number.isSafeInteger(val), array: (val) => Array.isArray(val), field: (val, object) => object.Fp.isValid(val), hash: (val) => typeof val === "function" && Number.isSafeInteger(val.outputLen) }; function validateObject(object, validators, optValidators = {}) { const checkField = (fieldName, type, isOptional) => { const checkVal = validatorFns[type]; if (typeof checkVal !== "function") throw new Error(`Invalid validator "${type}", expected function`); const val = object[fieldName]; if (isOptional && val === void 0) return; if (!checkVal(val, object)) { throw new Error(`Invalid param ${String(fieldName)}=${val} (${typeof val}), expected ${type}`); } }; for (const [fieldName, type] of Object.entries(validators)) checkField(fieldName, type, false); for (const [fieldName, type] of Object.entries(optValidators)) checkField(fieldName, type, true); return object; } // ../../node_modules/.pnpm/@noble+curves@1.3.0/node_modules/@noble/curves/esm/abstract/modular.js var _0n2 = BigInt(0); var _1n2 = BigInt(1); var _2n2 = BigInt(2); var _3n = BigInt(3); var _4n = BigInt(4); var _5n = BigInt(5); var _8n = BigInt(8); var _9n = BigInt(9); var _16n = BigInt(16); function mod(a, b) { const result = a % b; return result >= _0n2 ? result : b + result; } function pow(num, power, modulo) { if (modulo <= _0n2 || power < _0n2) throw new Error("Expected power/modulo > 0"); if (modulo === _1n2) return _0n2; let res = _1n2; while (power > _0n2) { if (power & _1n2) res = res * num % modulo; num = num * num % modulo; power >>= _1n2; } return res; } function pow2(x, power, modulo) { let res = x; while (power-- > _0n2) { res *= res; res %= modulo; } return res; } function invert(number2, modulo) { if (number2 === _0n2 || modulo <= _0n2) { throw new Error(`invert: expected positive integers, got n=${number2} mod=${modulo}`); } let a = mod(number2, modulo); let b = modulo; let x = _0n2, y = _1n2, u = _1n2, v = _0n2; while (a !== _0n2) { const q = b / a; const r = b % a; const m = x - u * q; const n = y - v * q; b = a, a = r, x = u, y = v, u = m, v = n; } const gcd = b; if (gcd !== _1n2) throw new Error("invert: does not exist"); return mod(x, modulo); } function tonelliShanks(P) { const legendreC = (P - _1n2) / _2n2; let Q, S, Z; for (Q = P - _1n2, S = 0; Q % _2n2 === _0n2; Q /= _2n2, S++) ; for (Z = _2n2; Z < P && pow(Z, legendreC, P) !== P - _1n2; Z++) ; if (S === 1) { const p1div4 = (P + _1n2) / _4n; return function tonelliFast(Fp2, n) { const root = Fp2.pow(n, p1div4); if (!Fp2.eql(Fp2.sqr(root), n)) throw new Error("Cannot find square root"); return root; }; } const Q1div2 = (Q + _1n2) / _2n2; return function tonelliSlow(Fp2, n) { if (Fp2.pow(n, legendreC) === Fp2.neg(Fp2.ONE)) throw new Error("Cannot find square root"); let r = S; let g = Fp2.pow(Fp2.mul(Fp2.ONE, Z), Q); let x = Fp2.pow(n, Q1div2); let b = Fp2.pow(n, Q); while (!Fp2.eql(b, Fp2.ONE)) { if (Fp2.eql(b, Fp2.ZERO)) return Fp2.ZERO; let m = 1; for (let t2 = Fp2.sqr(b); m < r; m++) { if (Fp2.eql(t2, Fp2.ONE)) break; t2 = Fp2.sqr(t2); } const ge = Fp2.pow(g, _1n2 << BigInt(r - m - 1)); g = Fp2.sqr(ge); x = Fp2.mul(x, ge); b = Fp2.mul(b, g); r = m; } return x; }; } function FpSqrt(P) { if (P % _4n === _3n) { const p1div4 = (P + _1n2) / _4n; return function sqrt3mod4(Fp2, n) { const root = Fp2.pow(n, p1div4); if (!Fp2.eql(Fp2.sqr(root), n)) throw new Error("Cannot find square root"); return root; }; } if (P % _8n === _5n) { const c1 = (P - _5n) / _8n; return function sqrt5mod8(Fp2, n) { const n2 = Fp2.mul(n, _2n2); const v = Fp2.pow(n2, c1); const nv = Fp2.mul(n, v); const i = Fp2.mul(Fp2.mul(nv, _2n2), v); const root = Fp2.mul(nv, Fp2.sub(i, Fp2.ONE)); if (!Fp2.eql(Fp2.sqr(root), n)) throw new Error("Cannot find square root"); return root; }; } if (P % _16n === _9n) { } return tonelliShanks(P); } var isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n2) === _1n2; var FIELD_FIELDS = [ "create", "isValid", "is0", "neg", "inv", "sqrt", "sqr", "eql", "add", "sub", "mul", "pow", "div", "addN", "subN", "mulN", "sqrN" ]; function validateField(field) { const initial = { ORDER: "bigint", MASK: "bigint", BYTES: "isSafeInteger", BITS: "isSafeInteger" }; const opts = FIELD_FIELDS.reduce((map, val) => { map[val] = "function"; return map; }, initial); return validateObject(field, opts); } function FpPow(f2, num, power) { if (power < _0n2) throw new Error("Expected power > 0"); if (power === _0n2) return f2.ONE; if (power === _1n2) return num; let p = f2.ONE; let d = num; while (power > _0n2) { if (power & _1n2) p = f2.mul(p, d); d = f2.sqr(d); power >>= _1n2; } return p; } function FpInvertBatch(f2, nums) { const tmp = new Array(nums.length); const lastMultiplied = nums.reduce((acc, num, i) => { if (f2.is0(num)) return acc; tmp[i] = acc; return f2.mul(acc, num); }, f2.ONE); const inverted = f2.inv(lastMultiplied); nums.reduceRight((acc, num, i) => { if (f2.is0(num)) return acc; tmp[i] = f2.mul(acc, tmp[i]); return f2.mul(acc, num); }, inverted); return tmp; } function nLength(n, nBitLength) { const _nBitLength = nBitLength !== void 0 ? nBitLength : n.toString(2).length; const nByteLength = Math.ceil(_nBitLength / 8); return { nBitLength: _nBitLength, nByteLength }; } function Field(ORDER, bitLen, isLE2 = false, redef = {}) { if (ORDER <= _0n2) throw new Error(`Expected Field ORDER > 0, got ${ORDER}`); const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen); if (BYTES > 2048) throw new Error("Field lengths over 2048 bytes are not supported"); const sqrtP = FpSqrt(ORDER); const f2 = Object.freeze({ ORDER, BITS, BYTES, MASK: bitMask(BITS), ZERO: _0n2, ONE: _1n2, create: (num) => mod(num, ORDER), isValid: (num) => { if (typeof num !== "bigint") throw new Error(`Invalid field element: expected bigint, got ${typeof num}`); return _0n2 <= num && num < ORDER; }, is0: (num) => num === _0n2, isOdd: (num) => (num & _1n2) === _1n2, neg: (num) => mod(-num, ORDER), eql: (lhs, rhs) => lhs === rhs, sqr: (num) => mod(num * num, ORDER), add: (lhs, rhs) => mod(lhs + rhs, ORDER), sub: (lhs, rhs) => mod(lhs - rhs, ORDER), mul: (lhs, rhs) => mod(lhs * rhs, ORDER), pow: (num, power) => FpPow(f2, num, power), div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER), // Same as above, but doesn't normalize sqrN: (num) => num * num, addN: (lhs, rhs) => lhs + rhs, subN: (lhs, rhs) => lhs - rhs, mulN: (lhs, rhs) => lhs * rhs, inv: (num) => invert(num, ORDER), sqrt: redef.sqrt || ((n) => sqrtP(f2, n)), invertBatch: (lst) => FpInvertBatch(f2, lst), // TODO: do we really need constant cmov? // We don't have const-time bigints anyway, so probably will be not very useful cmov: (a, b, c) => c ? b : a, toBytes: (num) => isLE2 ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES), fromBytes: (bytes2) => { if (bytes2.length !== BYTES) throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes2.length}`); return isLE2 ? bytesToNumberLE(bytes2) : bytesToNumberBE(bytes2); } }); return Object.freeze(f2); } function FpSqrtEven(Fp2, elm) { if (!Fp2.isOdd) throw new Error(`Field doesn't have isOdd`); const root = Fp2.sqrt(elm); return Fp2.isOdd(root) ? Fp2.neg(root) : root; } // ../../node_modules/.pnpm/@noble+curves@1.3.0/node_modules/@noble/curves/esm/abstract/curve.js var _0n3 = BigInt(0); var _1n3 = BigInt(1); function wNAF(c, bits) { const constTimeNegate = (condition, item) => { const neg = item.negate(); return condition ? neg : item; }; const opts = (W) => { const windows = Math.ceil(bits / W) + 1; const windowSize = 2 ** (W - 1); return { windows, windowSize }; }; return { constTimeNegate, // non-const time multiplication ladder unsafeLadder(elm, n) { let p = c.ZERO; let d = elm; while (n > _0n3) { if (n & _1n3) p = p.add(d); d = d.double(); n >>= _1n3; } return p; }, /** * Creates a wNAF precomputation window. Used for caching. * Default window size is set by `utils.precompute()` and is equal to 8. * Number of precomputed points depends on the curve size: * 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where: * - 𝑊 is the window size * - 𝑛 is the bitlength of the curve order. * For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224. * @returns precomputed point tables flattened to a single array */ precomputeWindow(elm, W) { const { windows, windowSize } = opts(W); const points = []; let p = elm; let base = p; for (let window = 0; window < windows; window++) { base = p; points.push(base); for (let i = 1; i < windowSize; i++) { base = base.add(p); points.push(base); } p = base.double(); } return points; }, /** * Implements ec multiplication using precomputed tables and w-ary non-adjacent form. * @param W window size * @param precomputes precomputed tables * @param n scalar (we don't check here, but should be less than curve order) * @returns real and fake (for const-time) points */ wNAF(W, precomputes, n) { const { windows, windowSize } = opts(W); let p = c.ZERO; let f2 = c.BASE; const mask = BigInt(2 ** W - 1); const maxNumber = 2 ** W; const shiftBy = BigInt(W); for (let window = 0; window < windows; window++) { const offset = window * windowSize; let wbits = Number(n & mask); n >>= shiftBy; if (wbits > windowSize) { wbits -= maxNumber; n += _1n3; } const offset1 = offset; const offset2 = offset + Math.abs(wbits) - 1; const cond1 = window % 2 !== 0; const cond2 = wbits < 0; if (wbits === 0) { f2 = f2.add(constTimeNegate(cond1, precomputes[offset1])); } else { p = p.add(constTimeNegate(cond2, precomputes[offset2])); } } return { p, f: f2 }; }, wNAFCached(P, precomputesMap, n, transform) { const W = P._WINDOW_SIZE || 1; let comp = precomputesMap.get(P); if (!comp) { comp = this.precomputeWindow(P, W); if (W !== 1) { precomputesMap.set(P, transform(comp)); } } return this.wNAF(W, comp, n); } }; } function validateBasic(curve) { validateField(curve.Fp); validateObject(curve, { n: "bigint", h: "bigint", Gx: "field", Gy: "field" }, { nBitLength: "isSafeInteger", nByteLength: "isSafeInteger" }); return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve, ...{ p: curve.Fp.ORDER } }); } // ../../node_modules/.pnpm/@noble+curves@1.3.0/node_modules/@noble/curves/esm/abstract/edwards.js var _0n4 = BigInt(0); var _1n4 = BigInt(1); var _2n3 = BigInt(2); var _8n2 = BigInt(8); var VERIFY_DEFAULT = { zip215: true }; function validateOpts(curve) { const opts = validateBasic(curve); validateObject(curve, { hash: "function", a: "bigint", d: "bigint", randomBytes: "function" }, { adjustScalarBytes: "function", domain: "function", uvRatio: "function", mapToCurve: "function" }); return Object.freeze({ ...opts }); } function twistedEdwards(curveDef) { const CURVE = validateOpts(curveDef); const { Fp: Fp2, n: CURVE_ORDER, prehash, hash: cHash, randomBytes: randomBytes2, nByteLength, h: cofactor } = CURVE; const MASK = _2n3 << BigInt(nByteLength * 8) - _1n4; const modP = Fp2.create; const uvRatio2 = CURVE.uvRatio || ((u, v) => { try { return { isValid: true, value: Fp2.sqrt(u * Fp2.inv(v)) }; } catch (e) { return { isValid: false, value: _0n4 }; } }); const adjustScalarBytes2 = CURVE.adjustScalarBytes || ((bytes2) => bytes2); const domain = CURVE.domain || ((data, ctx, phflag) => { if (ctx.length || phflag) throw new Error("Contexts/pre-hash are not supported"); return data; }); const inBig = (n) => typeof n === "bigint" && _0n4 < n; const inRange = (n, max) => inBig(n) && inBig(max) && n < max; const in0MaskRange = (n) => n === _0n4 || inRange(n, MASK); function assertInRange(n, max) { if (inRange(n, max)) return n; throw new Error(`Expected valid scalar < ${max}, got ${typeof n} ${n}`); } function assertGE0(n) { return n === _0n4 ? n : assertInRange(n, CURVE_ORDER); } const pointPrecomputes = /* @__PURE__ */ new Map(); function isPoint(other) { if (!(other instanceof Point)) throw new Error("ExtendedPoint expected"); } class Point { constructor(ex, ey, ez, et) { this.ex = ex; this.ey = ey; this.ez = ez; this.et = et; if (!in0MaskRange(ex)) throw new Error("x required"); if (!in0MaskRange(ey)) throw new Error("y required"); if (!in0MaskRange(ez)) throw new Error("z required"); if (!in0MaskRange(et)) throw new Error("t required"); } get x() { return this.toAffine().x; } get y() { return this.toAffine().y; } static fromAffine(p) { if (p instanceof Point) throw new Error("extended point not allowed"); const { x, y } = p || {}; if (!in0MaskRange(x) || !in0MaskRange(y)) throw new Error("invalid affine point"); return new Point(x, y, _1n4, modP(x * y)); } static normalizeZ(points) { const toInv = Fp2.invertBatch(points.map((p) => p.ez)); return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine); } // "Private method", don't use it directly _setWindowSize(windowSize) { this._WINDOW_SIZE = windowSize; pointPrecomputes.delete(this); } // Not required for fromHex(), which always creates valid points. // Could be useful for fromAffine(). assertValidity() { const { a, d } = CURVE; if (this.is0()) throw new Error("bad point: ZERO"); const { ex: X, ey: Y, ez: Z, et: T } = this; const X2 = modP(X * X); const Y2 = modP(Y * Y); const Z2 = modP(Z * Z); const Z4 = modP(Z2 * Z2); const aX2 = modP(X2 * a); const left = modP(Z2 * modP(aX2 + Y2)); const right = modP(Z4 + modP(d * modP(X2 * Y2))); if (left !== right) throw new Error("bad point: equation left != right (1)"); const XY = modP(X * Y); const ZT = modP(Z * T); if (XY !== ZT) throw new Error("bad point: equation left != right (2)"); } // Compare one point to another. equals(other) { isPoint(other); const { ex: X1, ey: Y1, ez: Z1 } = this; const { ex: X2, ey: Y2, ez: Z2 } = other; const X1Z2 = modP(X1 * Z2); const X2Z1 = modP(X2 * Z1); const Y1Z2 = modP(Y1 * Z2); const Y2Z1 = modP(Y2 * Z1); return X1Z2 === X2Z1 && Y1Z2 === Y2Z1; } is0() { return this.equals(Point.ZERO); } negate() { return new Point(modP(-this.ex), this.ey, this.ez, modP(-this.et)); } // Fast algo for doubling Extended Point. // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd // Cost: 4M + 4S + 1*a + 6add + 1*2. double() { const { a } = CURVE; const { ex: X1, ey: Y1, ez: Z1 } = this; const A = modP(X1 * X1); const B = modP(Y1 * Y1); const C = modP(_2n3 * modP(Z1 * Z1)); const D = modP(a * A); const x1y1 = X1 + Y1; const E = modP(modP(x1y1 * x1y1) - A - B); const G2 = D + B; const F = G2 - C; const H = D - B; const X3 = modP(E * F); const Y3 = modP(G2 * H); const T3 = modP(E * H); const Z3 = modP(F * G2); return new Point(X3, Y3, Z3, T3); } // Fast algo for adding 2 Extended Points. // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd // Cost: 9M + 1*a + 1*d + 7add. add(other) { isPoint(other); const { a, d } = CURVE; const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this; const { ex: X2, ey: Y2, ez: Z2, et: T2 } = other; if (a === BigInt(-1)) { const A2 = modP((Y1 - X1) * (Y2 + X2)); const B2 = modP((Y1 + X1) * (Y2 - X2)); const F2 = modP(B2 - A2); if (F2 === _0n4) return this.double(); const C2 = modP(Z1 * _2n3 * T2); const D2 = modP(T1 * _2n3 * Z2); const E2 = D2 + C2; const G3 = B2 + A2; const H2 = D2 - C2; const X32 = modP(E2 * F2); const Y32 = modP(G3 * H2); const T32 = modP(E2 * H2); const Z32 = modP(F2 * G3); return new Point(X32, Y32, Z32, T32); } const A = modP(X1 * X2); const B = modP(Y1 * Y2); const C = modP(T1 * d * T2); const D = modP(Z1 * Z2); const E = modP((X1 + Y1) * (X2 + Y2) - A - B); const F = D - C; const G2 = D + C; const H = modP(B - a * A); const X3 = modP(E * F); const Y3 = modP(G2 * H); const T3 = modP(E * H); const Z3 = modP(F * G2); return new Point(X3, Y3, Z3, T3); } subtract(other) { return this.add(other.negate()); } wNAF(n) { return wnaf.wNAFCached(this, pointPrecomputes, n, Point.normalizeZ); } // Constant-time multiplication. multiply(scalar) { const { p, f: f2 } = this.wNAF(assertInRange(scalar, CURVE_ORDER)); return Point.normalizeZ([p, f2])[0]; } // Non-constant-time multiplication. Uses double-and-add algorithm. // It's faster, but should only be used when you don't care about // an exposed private key e.g. sig verification. // Does NOT allow scalars higher than CURVE.n. multiplyUnsafe(scalar) { let n = assertGE0(scalar); if (n === _0n4) return I; if (this.equals(I) || n === _1n4) return this; if (this.equals(G)) return this.wNAF(n).p; return wnaf.unsafeLadder(this, n); } // Checks if point is of small order. // If you add something to small order point, you will have "dirty" // point with torsion component. // Multiplies point by cofactor and checks if the result is 0. isSmallOrder() { return this.multiplyUnsafe(cofactor).is0(); } // Multiplies point by curve order and checks if the result is 0. // Returns `false` is the point is dirty. isTorsionFree() { return wnaf.unsafeLadder(this, CURVE_ORDER).is0(); } // Converts Extended point to default (x, y) coordinates. // Can accept precomputed Z^-1 - for example, from invertBatch. toAffine(iz) { const { ex: x, ey: y, ez: z } = this; const is0 = this.is0(); if (iz == null) iz = is0 ? _8n2 : Fp2.inv(z); const ax = modP(x * iz); const ay = modP(y * iz); const zz = modP(z * iz); if (is0) return { x: _0n4, y: _1n4 }; if (zz !== _1n4) throw new Error("invZ was invalid"); return { x: ax, y: ay }; } clearCofactor() { const { h: cofactor2 } = CURVE; if (cofactor2 === _1n4) return this; return this.multiplyUnsafe(cofactor2); } // Converts hash string or Uint8Array to Point. // Uses algo from RFC8032 5.1.3. static fromHex(hex, zip215 = false) { const { d, a } = CURVE; const len = Fp2.BYTES; hex = ensureBytes("pointHex", hex, len); const normed = hex.slice(); const lastByte = hex[len - 1]; normed[len - 1] = lastByte & ~128; const y = bytesToNumberLE(normed); if (y === _0n4) { } else { if (zip215) assertInRange(y, MASK); else assertInRange(y, Fp2.ORDER); } const y2 = modP(y * y); const u = modP(y2 - _1n4); const v = modP(d * y2 - a); let { isValid, value: x } = uvRatio2(u, v); if (!isValid) throw new Error("Point.fromHex: invalid y coordinate"); const isXOdd = (x & _1n4) === _1n4; const isLastByteOdd = (lastByte & 128) !== 0; if (!zip215 && x === _0n4 && isLastByteOdd) throw new Error("Point.fromHex: x=0 and x_0=1"); if (isLastByteOdd !== isXOdd) x = modP(-x); return Point.fromAffine({ x, y }); } static fromPrivateKey(privKey) { return getExtendedPublicKey(privKey).point; } toRawBytes() { const { x, y } = this.toAffine(); const bytes2 = numberToBytesLE(y, Fp2.BYTES); bytes2[bytes2.length - 1] |= x & _1n4 ? 128 : 0; return bytes2; } toHex() { return bytesToHex2(this.toRawBytes()); } } Point.BASE = new Point(CURVE.Gx, CURVE.Gy, _1n4, modP(CURVE.Gx * CURVE.Gy)); Point.ZERO = new Point(_0n4, _1n4, _1n4, _0n4); const { BASE: G, ZERO: I } = Point; const wnaf = wNAF(Point, nByteLength * 8); function modN(a) { return mod(a, CURVE_ORDER); } function modN_LE(hash2) { return modN(bytesToNumberLE(hash2)); } function getExtendedPublicKey(key) { const len = nByteLength; key = ensureBytes("private key", key, len); const hashed = ensureBytes("hashed private key", cHash(key), 2 * len); const head = adjustScalarBytes2(hashed.slice(0, len)); const prefix = hashed.slice(len, 2 * len); const scalar = modN_LE(head); const point = G.multiply(scalar); const pointBytes = point.toRawBytes(); return { head, prefix, scalar, point, pointBytes }; } function getPublicKey(privKey) { return getExtendedPublicKey(privKey).pointBytes; } function hashDomainToScalar(context = new Uint8Array(), ...msgs) { const msg = concatBytes2(...msgs); return modN_LE(cHash(domain(msg, ensureBytes("context", context), !!prehash))); } function sign(msg, privKey, options = {}) { msg = ensureBytes("message", msg); if (prehash) msg = prehash(msg); const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey); const r = hashDomainToScalar(options.context, prefix, msg); const R = G.multiply(r).toRawBytes(); const k = hashDomainToScalar(options.context, R, pointBytes, msg); const s = modN(r + k * scalar); assertGE0(s); const res = concatBytes2(R, numberToBytesLE(s, Fp2.BYTES)); return ensureBytes("result", res, nByteLength * 2); } const verifyOpts = VERIFY_DEFAULT; function verify(sig, msg, publicKey, options = verifyOpts) { const { context, zip215 } = options; const len = Fp2.BYTES; sig = ensureBytes("signature", sig, 2 * len); msg = ensureBytes("message", msg); if (prehash) msg = prehash(msg); const s = bytesToNumberLE(sig.slice(len, 2 * len)); let A, R, SB; try { A = Point.fromHex(publicKey, zip215); R = Point.fromHex(sig.slice(0, len), zip215); SB = G.multiplyUnsafe(s); } catch (error) { return false; } if (!zip215 && A.isSmallOrder()) return false; const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg); const RkA = R.add(A.multiplyUnsafe(k)); return RkA.subtract(SB).clearCofactor().equals(Point.ZERO); } G._setWindowSize(8); const utils = { getExtendedPublicKey, // ed25519 private keys are uniform 32b. No need to check for modulo bias, like in secp256k1. randomPrivateKey: () => randomBytes2(Fp2.BYTES), /** * We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT * values. This slows down first getPublicKey() by milliseconds (see Speed section), * but allows to speed-up subsequent getPublicKey() calls up to 20x. * @param windowSize 2, 4, 8, 16 */ precompute(windowSize = 8, point = Point.BASE) { point._setWindowSize(windowSize); point.multiply(BigInt(3)); return point; } }; return { CURVE, getPublicKey, sign, verify, ExtendedPoint: Point, utils }; } // ../../node_modules/.pnpm/@noble+curves@1.3.0/node_modules/@noble/curves/esm/ed25519.js var ED25519_P = BigInt("57896044618658097711785492504343953926634992332820282019728792003956564819949"); var ED25519_SQRT_M1 = BigInt("19681161376707505956807079304988542015446066515923890162744021073123829784752"); var _0n5 = BigInt(0); var _1n5 = BigInt(1); var _2n4 = BigInt(2); var _5n2 = BigInt(5); var _10n = BigInt(10); var _20n = BigInt(20); var _40n = BigInt(40); var _80n = BigInt(80); function ed25519_pow_2_252_3(x) { const P = ED25519_P; const x2 = x * x % P; const b2 = x2 * x % P; const b4 = pow2(b2, _2n4, P) * b2 % P; const b5 = pow2(b4, _1n5, P) * x % P; const b10 = pow2(b5, _5n2, P) * b5 % P; const b20 = pow2(b10, _10n, P) * b10 % P; const b40 = pow2(b20, _20n, P) * b20 % P; const b80 = pow2(b40, _40n, P) * b40 % P; const b160 = pow2(b80, _80n, P) * b80 % P; const b240 = pow2(b160, _80n, P) * b80 % P; const b250 = pow2(b240, _10n, P) * b10 % P; const pow_p_5_8 = pow2(b250, _2n4, P) * x % P; return { pow_p_5_8, b2 }; } function adjustScalarBytes(bytes2) { bytes2[0] &= 248; bytes2[31] &= 127; bytes2[31] |= 64; return bytes2; } function uvRatio(u, v) { const P = ED25519_P; const v3 = mod(v * v * v, P); const v7 = mod(v3 * v3 * v, P); const pow3 = ed25519_pow_2_252_3(u * v7).pow_p_5_8; let x = mod(u * v3 * pow3, P); const vx2 = mod(v * x * x, P); const root1 = x; const root2 = mod(x * ED25519_SQRT_M1, P); const useRoot1 = vx2 === u; const useRoot2 = vx2 === mod(-u, P); const noRoot = vx2 === mod(-u * ED25519_SQRT_M1, P); if (useRoot1) x = root1; if (useRoot2 || noRoot) x = root2; if (isNegativeLE(x, P)) x = mod(-x, P); return { isValid: useRoot1 || useRoot2, value: x }; } var Fp = Field(ED25519_P, void 0, true); var ed25519Defaults = { // Param: a a: BigInt(-1), // Fp.create(-1) is proper; our way still works and is faster // d is equal to -121665/121666 over finite field. // Negative number is P - number, and division is invert(number, P) d: BigInt("37095705934669439343138083508754565189542113879843219016388785533085940283555"), // Finite field 𝔽p over which we'll do calculations; 2n**255n - 19n Fp, // Subgroup order: how many points curve has // 2n**252n + 27742317777372353535851937790883648493n; n: BigInt("7237005577332262213973186563042994240857116359379907606001950938285454250989"), // Cofactor h: BigInt(8), // Base point (x, y) aka generator point Gx: BigInt("15112221349535400772501151409588531511454012693041857206046113283949847762202"), Gy: BigInt("46316835694926478169428394003475163141307993866256225615783033603165251855960"), hash: sha512, randomBytes, adjustScalarBytes, // dom2 // Ratio of u to v. Allows us to combine inversion and square root. Uses algo from RFC8032 5.1.3. // Constant-time, u/√v uvRatio }; var ed25519 = /* @__PURE__ */ twistedEdwards(ed25519Defaults); function ed25519_domain(data, ctx, phflag) { if (ctx.length > 255) throw new Error("Context is too big"); return concatBytes(utf8ToBytes("SigEd25519 no Ed25519 collisions"), new Uint8Array([phflag ? 1 : 0, ctx.length]), ctx, data); } var ed25519ctx = /* @__PURE__ */ twistedEdwards({ ...ed25519Defaults, domain: ed25519_domain }); var ed25519ph = /* @__PURE__ */ twistedEdwards({ ...ed25519Defaults, domain: ed25519_domain, prehash: sha512 }); var ELL2_C1 = (Fp.ORDER + BigInt(3)) / BigInt(8); var ELL2_C2 = Fp.pow(_2n4, ELL2_C1); var ELL2_C3 = Fp.sqrt(Fp.neg(Fp.ONE)); var ELL2_C4 = (Fp.ORDER - BigInt(5)) / BigInt(8); var ELL2_J = BigInt(486662); var ELL2_C1_EDWARDS = FpSqrtEven(Fp, Fp.neg(BigInt(486664))); var SQRT_AD_MINUS_ONE = BigInt("25063068953384623474111414158702152701244531502492656460079210482610430750235"); var INVSQRT_A_MINUS_D = BigInt("54469307008909316920995813868745141605393597292927456921205312896311721017578"); var ONE_MINUS_D_SQ = BigInt("1159843021668779879193775521855586647937357759715417654439879720876111806838"); var D_MINUS_ONE_SQ = BigInt("40440834346308536858101042469323190826248399146238708352240133220865137265952"); var MAX_255B = BigInt("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ../../node_modules/.pnpm/@noble+hashes@1.3.3/node_modules/@noble/hashes/esm/hmac.js var HMAC = class extends Hash { constructor(hash2, _key) { super(); this.finished = false; this.destroyed = false; hash(hash2); const key = toBytes(_key); this.iHash = hash2.create(); if (typeof this.iHash.update !== "function") throw new Error("Expected instance of class which extends utils.Hash"); this.blockLen = this.iHash.blockLen; this.outputLen = this.iHash.outputLen; const blockLen = this.blockLen; const pad = new Uint8Array(blockLen); pad.set(key.length > blockLen ? hash2.create().update(key).digest() : key); for (let i = 0; i < pad.length; i++) pad[i] ^= 54; this.iHash.update(pad); this.oHash = hash2.create(); for (let i = 0; i < pad.length; i++) pad[i] ^= 54 ^ 92; this.oHash.update(pad); pad.fill(0); } update(buf) { exists(this); this.iHash.update(buf); return this; } digestInto(out) { exists(this); bytes(out, this.outputLen); this.finished = true; this.iHash.digestInto(out); this.oHash.update(out); this.oHash.digestInto(out); this.destroy(); } digest() { const out = new Uint8Array(this.oHash.outputLen); this.digestInto(out); return out; } _cloneInto(to) { to || (to = Object.create(Object.getPrototypeOf(this), {})); const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this; to = to; to.finished = finished; to.destroyed = destroyed; to.blockLen = blockLen; to.outputLen = outputLen; to.oHash = oHash._cloneInto(to.oHash); to.iHash = iHash._cloneInto(to.iHash); return to; } destroy() { this.destroyed = true; this.oHash.destroy(); this.iHash.destroy(); } }; var hmac = (hash2, key, message) => new HMAC(hash2, key).update(message).digest(); hmac.create = (hash2, key) => new HMAC(hash2, key); // ../../node_modules/.pnpm/@noble+hashes@1.3.3/node_modules/@noble/hashes/esm/ripemd160.js var Rho = /* @__PURE__ */ new Uint8Array([7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]); var Id = /* @__PURE__ */ Uint8Array.from({ length: 16 }, (_, i) => i); var Pi = /* @__PURE__ */ Id.map((i) => (9 * i + 5) % 16); var idxL = [Id]; var idxR = [Pi]; for (let i = 0; i < 4; i++) for (let j of [idxL, idxR]) j.push(j[i].map((k) => Rho[k])); var shifts = /* @__PURE__ */ [ [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8], [12, 13, 11, 15, 6, 9, 9, 7, 12, 15, 11, 13, 7, 8, 7, 7], [13, 15, 14, 11, 7, 7, 6, 8, 13, 14, 13, 12, 5, 5, 6, 9], [14, 11, 12, 14, 8, 6, 5, 5, 15, 12, 15, 14, 9, 9, 8, 6], [15, 12, 13, 13, 9, 5, 8, 6, 14, 11, 12, 11, 8, 6, 5, 5] ].map((i) => new Uint8Array(i)); var shiftsL = /* @__PURE__ */ idxL.map((idx, i) => idx.map((j) => shifts[i][j])); var shiftsR = /* @__PURE__ */ idxR.map((idx, i) => idx.map((j) => shifts[i][j])); var Kl = /* @__PURE__ */ new Uint32Array([ 0, 1518500249, 1859775393, 2400959708, 2840853838 ]); var Kr = /* @__PURE__ */ new Uint32Array([ 1352829926, 1548603684, 1836072691, 2053994217, 0 ]); var rotl = (word, shift) => word << shift | word >>> 32 - shift; function f(group, x, y, z) { if (group === 0) return x ^ y ^ z; else if (group === 1) return x & y | ~x & z; else if (group === 2) return (x | ~y) ^ z; else if (group === 3) return x & z | y & ~z; else return x ^ (y | ~z); } var BUF = /* @__PURE__ */ new Uint32Array(16); var RIPEMD160 = class extends SHA2 { constructor() { super(64, 20, 8, true); this.h0 = 1732584193 | 0; this.h1 = 4023233417 | 0; this.h2 = 2562383102 | 0; this.h3 = 271733878 | 0; this.h4 = 3285377520 | 0; } get() { const { h0, h1, h2, h3, h4 } = this; return [h0, h1, h2, h3, h4]; } set(h0, h1, h2, h3, h4) { this.h0 = h0 | 0; this.h1 = h1 | 0; this.h2 = h2 | 0; this.h3 = h3 | 0; this.h4 = h4 | 0; } process(view, offset) { for (let i = 0; i < 16; i++, offset += 4) BUF[i] = view.getUint32(offset, true); let al = this.h0 | 0, ar = al, bl = this.h1 | 0, br = bl, cl = this.h2 | 0, cr = cl, dl = this.h3 | 0, dr = dl, el = this.h4 | 0, er = el; for (let group = 0; group < 5; group++) { const rGroup = 4 - group; const hbl = Kl[group], hbr = Kr[group]; const rl = idxL[group], rr = idxR[group]; const sl = shiftsL[group], sr = shiftsR[group]; for (let i = 0; i < 16; i++) { const tl = rotl(al + f(group, bl, cl, dl) + BUF[rl[i]] + hbl, sl[i]) + el | 0; al = el, el = dl, dl = rotl(cl, 10) | 0, cl = bl, bl = tl; } for (let i = 0; i < 16; i++) { const tr = rotl(ar + f(rGroup, br, cr, dr) + BUF[rr[i]] + hbr, sr[i]) + er | 0; ar = er, er = dr, dr = rotl(cr, 10) | 0, cr = br, br = tr; } } this.set(this.h1 + cl + dr | 0, this.h2 + dl + er | 0, this.h3 + el + ar | 0, this.h4 + al + br | 0, this.h0 + bl + cr | 0); } roundClean() { BUF.fill(0); } destroy() { this.destroyed = true; this.buffer.fill(0); this.set(0, 0, 0, 0, 0); } }; var ripemd160 = /* @__PURE__ */ wrapConstructor(() => new RIPEMD160()); // ../../node_modules/.pnpm/@noble+hashes@1.3.3/node_modules/@noble/hashes/esm/sha256.js var Chi = (a, b, c) => a & b ^ ~a & c; var Maj = (a, b, c) => a & b ^ a & c ^ b & c; var SHA256_K = /* @__PURE__ */ new Uint32Array([ 1116352408, 1899447441, 3049323471, 3921009573, 961987163, 1508970993, 2453635748, 2870763221, 3624381080, 310598401, 607225278, 1426881987, 1925078388, 2162078206, 2614888103, 3248222580, 3835390401, 4022224774, 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, 2554220882, 2821834349, 2952996808, 3210313671, 3336571891, 3584528711, 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, 1695183700, 1986661051, 2177026350, 2456956037, 2730485921, 2820302411, 3259730800, 3345764771, 3516065817, 3600352804, 4094571909, 275423344, 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, 1537002063, 1747873779, 1955562222, 2024104815, 2227730452, 2361852424, 2428436474, 2756734187, 3204031479, 3329325298 ]); var IV = /* @__PURE__ */ new Uint32Array([ 1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225 ]); var SHA256_W = /* @__PURE__ */ new Uint32Array(64); var SHA256 = class extends SHA2 { constructor() { super(64, 32, 8, false); this.A = IV[0] | 0; this.B = IV[1] | 0; this.C = IV[2] | 0; this.D = IV[3] | 0; this.E = IV[4] | 0; this.F = IV[5] | 0; this.G = IV[6] | 0; this.H = IV[7] | 0; } get() { const { A, B, C, D, E, F, G, H } = this; return [A, B, C, D, E, F, G, H]; } // prettier-ignore set(A, B, C, D, E, F, G, H) { this.A = A | 0; this.B = B | 0; this.C = C | 0; this.D = D | 0; this.E = E | 0; this.F = F | 0; this.G = G | 0; this.H = H | 0; } process(view, offset) { for (let i = 0; i < 16; i++, offset += 4) SHA256_W[i] = view.getUint32(offset, false); for (let i = 16; i < 64; i++) { const W15 = SHA256_W[i - 15]; const W2 = SHA256_W[i - 2]; const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3; const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10; SHA256_W[i] = s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16] | 0; } let { A, B, C, D, E, F, G, H } = this; for (let i = 0; i < 64; i++) { const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25); const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i] | 0; const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22); const T2 = sigma0 + Maj(A, B, C) | 0; H = G; G = F; F = E; E = D + T1 | 0; D = C; C = B; B = A; A = T1 + T2 | 0; } A = A + this.A | 0; B = B + this.B | 0; C = C + this.C | 0; D = D + this.D | 0; E = E + this.E | 0; F = F + this.F | 0; G = G + this.G | 0; H = H + this.H | 0; this.set(A, B, C, D, E, F, G, H); } roundClean() { SHA256_W.fill(0); } destroy() { this.set(0, 0, 0, 0, 0, 0, 0, 0); this.buffer.fill(0); } }; var sha256 = /* @__PURE__ */ wrapConstructor(() => new SHA256()); // ../../node_modules/.pnpm/ed25519-keygen@0.4.11/node_modules/ed25519-keygen/hdkey.js var MASTER_SECRET = utf8ToBytes("ed25519 seed"); var HARDENED_OFFSET = 2147483648; var ZERO = new Uint8Array([0]); function ensureBytes2(b, ...lengths) { if (typeof b === "string") b = hexToBytes(b); bytes(b, ...lengths); return b; } var hash160 = (data) => ripemd160(sha256(data)); var fromU32 = (data) => createView(data).getUint32(0, false); var toU32 = (n) => { if (!Number.isSafeInteger(n) || n < 0 || n > 2 ** 32 - 1) { throw new Error(`Invalid number=${n}. Should be from 0 to 2 ** 32 - 1`); } const buf = new Uint8Array(4); createView(buf).setUint32(0, n, false); return buf; }; var HDKey = class _HDKey { get publicKeyRaw() { return ed25519.getPublicKey(this.privateKey); } get publicKey() { return concatBytes(ZERO, this.publicKeyRaw); } get pubHash() { return hash160(this.publicKey); } get fingerprint() { return fromU32(this.pubHash); } get fingerprintHex() { return bytesToHex(toU32(this.fingerprint)); } get parentFingerprintHex() { return bytesToHex(toU32(this.parentFingerprint)); } static fromMasterSeed(seed) { seed = ensureBytes2(seed); if (8 * seed.length < 128 || 8 * seed.length > 512) { throw new Error(`HDKey: wrong seed length=${seed.length}. Should be between 128 and 512 bits; 256 bits is advised)`); } const I = hmac(sha512, MASTER_SECRET, seed); return new _HDKey({ privateKey: I.slice(0, 32), chainCode: I.slice(32) }); } constructor(opt) { this.depth = 0; this.index = 0; this.parentFingerprint = 0; if (!opt || typeof opt !== "object") throw new Error("HDKey.constructor must not be called directly"); bytes(opt.privateKey, 32); bytes(opt.chainCode, 32); this.depth = opt.depth || 0; this.index = opt.index || 0; this.parentFingerprint = opt.parentFingerprint || 0; if (!this.depth) { if (this.parentFingerprint || this.index) throw new Error("HDKey: zero depth with non-zero index/parent fingerprint"); } this.chainCode = opt.chainCode; this.privateKey = opt.privateKey; } derive(path, forceHardened = false) { if (!/^[mM]'?/.test(path)) throw new Error('Path must start with "m" or "M"'); if (/^[mM]'?$/.test(path)) return this; const parts = path.replace(/^[mM]'?\//, "").split("/"); let child = this; for (const c of parts) { const m = /^(\d+)('?)$/.exec(c); if (!m || m.length !== 3) throw new Error(`Invalid child index: ${c}`); let idx = +m[1]; if (!Number.isSafeInteger(idx) || idx >= HARDENED_OFFSET) throw new Error("Invalid index"); if (forceHardened || m[2] === "'") idx += HARDENED_OFFSET; child = child.deriveChild(idx); } return child; } deriveChild(index) { if (index < HARDENED_OFFSET) throw new Error(`Non-hardened child derivation not possible for Ed25519 (index=${index})`); const data = concatBytes(ZERO, this.privateKey, toU32(index)); const I = hmac(sha512, this.chainCode, data); return new _HDKey({ chainCode: I.slice(32), depth: this.depth + 1, parentFingerprint: this.fingerprint, index, privateKey: I.slice(0, 32) }); } sign(message) { return ed25519.sign(message, this.privateKey); } verify(message, signature) { signature = ensureBytes2(signature, 64); return ed25519.verify(signature, message, this.publicKeyRaw); } }; // src/hd-identity-vault.ts var import_dids4 = require("@web5/dids"); var import_common10 = require("@web5/common"); var import_english = require("@scure/bip39/wordlists/english"); var import_bip39 = require("@scure/bip39"); // src/local-key-manager.ts var import_crypto13 = require("@web5/crypto"); // src/store-key.ts var import_crypto12 = require("@web5/crypto"); var import_common7 = require("@web5/common"); var DwnKeyStore = class extends DwnDataStore { constructor() { super(...arguments); this.name = "DwnKeyStore"; /** * Properties to use when writing and querying Private Key records with the DWN store. */ this._recordProperties = { dataFormat: "application/json", schema: "https://identity.foundation/schemas/web5/private-jwk" }; } async delete(params) { return await super.delete(params); } async get(params) { return await super.get(params); } async set(params) { await super.set(params); } async list(params) { return await super.list(params); } async getAllRecords({ agent, tenantDid }) { this._index.clear(); const { reply: queryReply } = await agent.dwn.processRequest({ author: tenantDid, target: tenantDid, messageType: DwnInterface.RecordsQuery, messageParams: { filter: { ...this._recordProperties } } }); let storedKeys = []; for (const record of queryReply.entries ?? []) { if (!record.encodedData) { throw new Error(`${this.name}: Expected 'encodedData' to be present in the DWN query result entry`); } const storedKey = import_common7.Convert.base64Url(record.encodedData).toObject(); if ((0, import_crypto12.isPrivateJwk)(storedKey)) { const indexKey = `${tenantDid}${TENANT_SEPARATOR}${import_crypto12.KEY_URI_PREFIX_JWK}${storedKey.kid}`; this._index.set(indexKey, record.recordId); this._cache.set(record.recordId, storedKey); storedKeys.push(storedKey); } } return storedKeys; } }; var InMemoryKeyStore = class extends InMemoryDataStore { constructor() { super(...arguments); this.name = "InMemoryKeyStore"; } async delete(params) { return await super.delete(params); } async get(params) { return await super.get(params); } async list(params) { return await super.list(params); } async set(params) { return await super.set(params); } }; // src/local-key-manager.ts var supportedAlgorithms2 = { "AES-GCM": { implementation: import_crypto13.AesGcmAlgorithm, names: ["A128GCM", "A192GCM", "A256GCM"] }, "AES-KW": { implementation: AesKwAlgorithm, names: ["A128KW", "A192KW", "A256KW"] }, "Ed25519": { implementation: import_crypto13.EdDsaAlgorithm, names: ["Ed25519"] }, "secp256k1": { implementation: import_crypto13.EcdsaAlgorithm, names: ["ES256K", "secp256k1"] }, "secp256r1": { implementation: import_crypto13.EcdsaAlgorithm, names: ["ES256", "secp256r1"] }, "SHA-256": { implementation: import_crypto13.Sha2Algorithm, names: ["SHA-256"] } }; var LocalKeyManager2 = class { constructor({ agent, keyStore } = {}) { /** * A private map that stores instances of cryptographic algorithm implementations. Each key in * this map is an `AlgorithmConstructor`, and its corresponding value is an instance of a class * that implements a specific cryptographic algorithm. This map is used to cache and reuse * instances for performance optimization, ensuring that each algorithm is instantiated only once. */ this._algorithmInstances = /* @__PURE__ */ new Map(); this._agent = agent; this._keyStore = keyStore ?? new InMemoryKeyStore(); } /** * Retrieves the `Web5PlatformAgent` execution context. * * @returns The `Web5PlatformAgent` instance that represents the current execution context. * @throws Will throw an error if the `agent` instance property is undefined. */ get agent() { if (this._agent === void 0) { throw new Error("LocalKeyManager: Unable to determine agent execution context."); } return this._agent; } set agent(agent) { this._agent = agent; } async decrypt({ keyUri, ...params }) { const privateKey = await this.getPrivateKey({ keyUri }); const algorithm = this.getAlgorithmName({ key: privateKey }); const cipher = this.getAlgorithm({ algorithm }); const ciphertext = await cipher.decrypt({ key: privateKey, ...params }); return ciphertext; } digest(_params) { throw new Error("Method not implemented."); } async encrypt({ keyUri, ...params }) { const privateKey = await this.getPrivateKey({ keyUri }); const algorithm = this.getAlgorithmName({ key: privateKey }); const cipher = this.getAlgorithm({ algorithm }); const ciphertext = await cipher.encrypt({ key: privateKey, ...params }); return ciphertext; } /** * Exports a private key identified by the provided key URI from the local KMS. * * @remarks * This method retrieves the key from the key store and returns it. It is primarily used * for extracting keys for backup or transfer purposes. * * @example * ```ts * const keyManager = new LocalKeyManager(); * const keyUri = await keyManager.generateKey({ algorithm: 'Ed25519' }); * const privateKey = await keyManager.exportKey({ keyUri }); * ``` * * @param params - Parameters for exporting the key. * @param params.keyUri - The key URI identifying the key to export. * * @returns A Promise resolving to the JWK representation of the exported key. */ async exportKey({ keyUri }) { const privateKey = await this.getPrivateKey({ keyUri }); return privateKey; } /** * Generates a new cryptographic key in the local KMS with the specified algorithm and returns a * unique key URI which can be used to reference the key in subsequent operations. * * @example * ```ts * const keyManager = new LocalKeyManager(); * const keyUri = await keyManager.generateKey({ algorithm: 'Ed25519' }); * console.log(keyUri); // Outputs the key URI * ``` * * @param params - The parameters for key generation. * @param params.algorithm - The algorithm to use for key generation, defined in `SupportedAlgorithm`. * * @returns A Promise that resolves to the key URI, a unique identifier for the generated key. */ async generateKey({ algorithm: algorithmIdentifier }) { const algorithm = this.getAlgorithmName({ key: { alg: algorithmIdentifier } }); const keyGenerator = this.getAlgorithm({ algorithm }); const privateKey = await keyGenerator.generateKey({ algorithm: algorithmIdentifier }); privateKey.kid ??= await (0, import_crypto13.computeJwkThumbprint)({ jwk: privateKey }); const keyUri = await this.getKeyUri({ key: privateKey }); await this._keyStore.set({ id: keyUri, data: privateKey, agent: this.agent, preventDuplicates: false, useCache: true }); return keyUri; } /** * Computes the Key URI for a given public JWK (JSON Web Key). * * @remarks * This method generates a {@link https://datatracker.ietf.org/doc/html/rfc3986 | URI} * (Uniform Resource Identifier) for the given JWK, which uniquely identifies the key across all * `CryptoApi` implementations. The key URI is constructed by appending the * {@link https://datatracker.ietf.org/doc/html/rfc7638 | JWK thumbprint} to the prefix * `urn:jwk:`. The JWK thumbprint is deterministically computed from the JWK and is consistent * regardless of property order or optional property inclusion in the JWK. This ensures that the * same key material represented as a JWK will always yield the same thumbprint, and therefore, * the same key URI. * * @example * ```ts * const keyManager = new LocalKeyManager(); * const keyUri = await keyManager.generateKey({ algorithm: 'Ed25519' }); * const publicKey = await keyManager.getPublicKey({ keyUri }); * const keyUriFromPublicKey = await keyManager.getKeyUri({ key: publicKey }); * console.log(keyUri === keyUriFromPublicKey); // Outputs `true` * ``` * * @param params - The parameters for getting the key URI. * @param params.key - The JWK for which to compute the key URI. * * @returns A Promise that resolves to the key URI as a string. */ async getKeyUri({ key }) { const jwkThumbprint = await (0, import_crypto13.computeJwkThumbprint)({ jwk: key }); const keyUri = `${import_crypto13.KEY_URI_PREFIX_JWK}${jwkThumbprint}`; return keyUri; } /** * Retrieves the public key associated with a previously generated private key, identified by * the provided key URI. * * @example * ```ts * const keyManager = new LocalKeyManager(); * const keyUri = await keyManager.generateKey({ algorithm: 'Ed25519' }); * const publicKey = await keyManager.getPublicKey({ keyUri }); * ``` * * @param params - The parameters for retrieving the public key. * @param params.keyUri - The key URI of the private key to retrieve the public key for. * * @returns A Promise that resolves to the public key in JWK format. */ async getPublicKey({ keyUri }) { const privateKey = await this.getPrivateKey({ keyUri }); const algorithm = this.getAlgorithmName({ key: privateKey }); const keyGenerator = this.getAlgorithm({ algorithm }); const publicKey = await keyGenerator.getPublicKey({ key: privateKey }); return publicKey; } /** * Imports a private key into the local KMS. * * @remarks * This method stores the provided JWK in the key store, making it available for subsequent * cryptographic operations. It is particularly useful for initializing the KMS with pre-existing * keys or for restoring keys from backups. * * Note that, if defined, the `kid` (key ID) property of the JWK is used as the key URI for the * imported key. If the `kid` property is not provided, the key URI is computed from the JWK * thumbprint of the key. * * @example * ```ts * const keyManager = new LocalKeyManager(); * const privateKey = { ... } // A private key in JWK format * const keyUri = await keyManager.importKey({ key: privateKey }); * ``` * * @param params - Parameters for importing the key. * @param params.key - The private key to import to in JWK format. * * @returns A Promise resolving to the key URI, uniquely identifying the imported key. */ async importKey({ key }) { if (!(0, import_crypto13.isPrivateJwk)(key)) throw new TypeError("Invalid key provided. Must be a private key in JWK format."); const privateKey = structuredClone(key); privateKey.kid ??= await (0, import_crypto13.computeJwkThumbprint)({ jwk: privateKey }); const keyUri = await this.getKeyUri({ key: privateKey }); await this._keyStore.set({ id: keyUri, data: privateKey, agent: this.agent, preventDuplicates: true, useCache: true }); return keyUri; } /** * Signs the provided data using the private key identified by the provided key URI. * * @remarks * This method uses the signature algorithm determined by the `alg` and/or `crv` properties of the * private key identified by the provided key URI to sign the provided data. The signature can * later be verified by parties with access to the corresponding public key, ensuring that the * data has not been tampered with and was indeed signed by the holder of the private key. * * @example * ```ts * const keyManager = new LocalKeyManager(); * const keyUri = await keyManager.generateKey({ algorithm: 'Ed25519' }); * const data = new TextEncoder().encode('Message to sign'); * const signature = await keyManager.sign({ keyUri, data }); * ``` * * @param params - The parameters for the signing operation. * @param params.keyUri - The key URI of the private key to use for signing. * @param params.data - The data to sign. * * @returns A Promise resolving to the digital signature as a `Uint8Array`. */ async sign({ keyUri, data }) { const privateKey = await this.getPrivateKey({ keyUri }); const algorithm = this.getAlgorithmName({ key: privateKey }); const signer = this.getAlgorithm({ algorithm }); const signature = signer.sign({ data, key: privateKey }); return signature; } async unwrapKey({ wrappedKeyBytes, wrappedKeyAlgorithm, decryptionKeyUri }) { const decryptionKey = await this.getPrivateKey({ keyUri: decryptionKeyUri }); const algorithm = this.getAlgorithmName({ key: decryptionKey }); const keyWrapper = this.getAlgorithm({ algorithm }); const unwrappedKey = await keyWrapper.unwrapKey({ wrappedKeyBytes, wrappedKeyAlgorithm, decryptionKey }); return unwrappedKey; } /** * Verifies a digital signature associated the provided data using the provided key. * * @remarks * This method uses the signature algorithm determined by the `alg` and/or `crv` properties of the * provided key to check the validity of a digital signature against the original data. It * confirms whether the signature was created by the holder of the corresponding private key and * that the data has not been tampered with. * * @example * ```ts * const keyManager = new LocalKeyManager(); * const keyUri = await keyManager.generateKey({ algorithm: 'Ed25519' }); * const data = new TextEncoder().encode('Message to sign'); * const signature = await keyManager.sign({ keyUri, data }); * const isSignatureValid = await keyManager.verify({ keyUri, data, signature }); * ``` * * @param params - The parameters for the verification operation. * @param params.key - The key to use for verification. * @param params.signature - The signature to verify. * @param params.data - The data to verify. * * @returns A Promise resolving to a boolean indicating whether the signature is valid. */ async verify({ key, signature, data }) { const algorithm = this.getAlgorithmName({ key }); const signer = this.getAlgorithm({ algorithm }); const isSignatureValid = signer.verify({ key, signature, data }); return isSignatureValid; } async wrapKey({ unwrappedKey, encryptionKeyUri }) { const encryptionKey = await this.getPrivateKey({ keyUri: encryptionKeyUri }); const algorithm = this.getAlgorithmName({ key: encryptionKey }); const keyWrapper = this.getAlgorithm({ algorithm }); const wrappedKeyBytes = await keyWrapper.wrapKey({ unwrappedKey, encryptionKey }); return wrappedKeyBytes; } /** * Retrieves an algorithm implementation instance based on the provided algorithm name. * * @remarks * This method checks if the requested algorithm is supported and returns a cached instance * if available. If an instance does not exist, it creates and caches a new one. This approach * optimizes performance by reusing algorithm instances across cryptographic operations. * * @example * ```ts * const signer = this.getAlgorithm({ algorithm: 'Ed25519' }); * ``` * * @param params - The parameters for retrieving the algorithm implementation. * @param params.algorithm - The name of the algorithm to retrieve. * * @returns An instance of the requested algorithm implementation. * * @throws Error if the requested algorithm is not supported. */ getAlgorithm({ algorithm }) { const AlgorithmImplementation = supportedAlgorithms2[algorithm]?.["implementation"]; if (!AlgorithmImplementation) { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `Algorithm not supported: ${algorithm}`); } if (!this._algorithmInstances.has(AlgorithmImplementation)) { this._algorithmInstances.set(AlgorithmImplementation, new AlgorithmImplementation()); } return this._algorithmInstances.get(AlgorithmImplementation); } /** * Determines the algorithm name based on the key's properties. * * @remarks * This method facilitates the identification of the correct algorithm for cryptographic * operations based on the `alg` or `crv` properties of a {@link Jwk | JWK}. * * @example * ```ts * const publicKey = { ... }; // Public key in JWK format * const algorithm = this.getAlgorithmName({ key: publicKey }); * ``` * * @param params - The parameters for determining the algorithm name. * @param params.key - A JWK containing the `alg` or `crv` properties. * * @returns The algorithm name associated with the key. * * @throws Error if the algorithm name cannot be determined from the provided input. */ getAlgorithmName({ key }) { const algProperty = key.alg; const crvProperty = key.crv; for (const algorithmIdentifier of Object.keys(supportedAlgorithms2)) { const algorithmNames = supportedAlgorithms2[algorithmIdentifier].names; if (algProperty && algorithmNames.includes(algProperty)) { return algorithmIdentifier; } else if (crvProperty && algorithmNames.includes(crvProperty)) { return algorithmIdentifier; } } throw new CryptoError( "algorithmNotSupported" /* AlgorithmNotSupported */, `Algorithm not supported based on provided input: alg=${algProperty}, crv=${crvProperty}. Please check the documentation for the list of supported algorithms.` ); } /** * Retrieves a private key from the key store based on the provided key URI. * * @example * ```ts * const privateKey = this.getPrivateKey({ keyUri: 'urn:jwk:...' }); * ``` * * @param params - Parameters for retrieving the private key. * @param params.keyUri - The key URI identifying the private key to retrieve. * * @returns A Promise resolving to the JWK representation of the private key. * * @throws Error if the key is not found in the key store. */ async getPrivateKey({ keyUri }) { const privateKey = await this._keyStore.get({ id: keyUri, agent: this.agent, useCache: true }); if (!privateKey) { throw new Error(`Key not found: ${keyUri}`); } return privateKey; } }; // src/prototyping/crypto/jose/jwe-compact.ts var import_crypto15 = require("@web5/crypto"); // src/prototyping/crypto/jose/jwe.ts var import_common8 = require("@web5/common"); function isValidJweHeader(obj) { return typeof obj === "object" && obj !== null && "alg" in obj && obj.alg !== void 0 && "enc" in obj && obj.enc !== void 0; } var JweKeyManagement = class { /** * Decrypts the encrypted key (JWE Encrypted Key) using the specified key encryption algorithm * defined in the JWE Header's "alg" parameter. * * This method supports multiple key management algorithms, including Direct Encryption (dir) and * PBES2 schemes with key wrapping. * * The method takes a key, which can be a Key Identifier, JWK, or raw byte array, and the * encrypted key along with the JWE header. It returns the decrypted Content Encryption Key (CEK) * which can then be used to decrypt the JWE ciphertext. * * @example * ```ts * // Decrypting the CEK with the PBES2-HS512+A256KW algorithm * const cek = await JweKeyManagement.decrypt({ * key: Convert.string(passphrase).toUint8Array(), * encryptedKey: encryptedCek, * joseHeader: { * alg: 'PBES2-HS512+A256KW', * enc: 'A256GCM', * p2c: 210_000, * p2s: Convert.uint8Array(saltInput).toBase64Url(), * }, * crypto: new AgentCryptoApi() * }); * ``` * * @param params - The decryption parameters. * @throws Throws an error if the key management algorithm is not supported or if required * parameters are missing or invalid. */ static async decrypt({ key, encryptedKey, joseHeader, crypto: crypto3 }) { switch (joseHeader.alg) { case "dir": { if (encryptedKey !== void 0) { throw new CryptoError("invalidJwe" /* InvalidJwe */, 'JWE "encrypted_key" is not allowed when using "dir" (Direct Encryption Mode).'); } if (key instanceof Uint8Array) { throw new CryptoError("invalidJwe" /* InvalidJwe */, 'Key management "key" must be a Key URI or JWK when using "dir" (Direct Encryption Mode).'); } return key; } case "PBES2-HS256+A128KW": case "PBES2-HS384+A192KW": case "PBES2-HS512+A256KW": { if (typeof joseHeader.p2c !== "number") { throw new CryptoError("invalidJwe" /* InvalidJwe */, 'JOSE Header "p2c" (PBES2 Count) is missing or not a number.'); } if (typeof joseHeader.p2s !== "string") { throw new CryptoError("invalidJwe" /* InvalidJwe */, 'JOSE Header "p2s" (PBES2 salt) is missing or not a string.'); } if (!(key instanceof Uint8Array)) { throw new CryptoError("invalidJwe" /* InvalidJwe */, 'Key management "key" must be a Uint8Array when using "PBES2" (Key Encryption Mode).'); } if (encryptedKey === void 0) { throw new CryptoError("invalidJwe" /* InvalidJwe */, 'JWE "encrypted_key" is required when using "PBES2" (Key Encryption Mode).'); } let salt; try { salt = new Uint8Array([ ...import_common8.Convert.string(joseHeader.alg).toUint8Array(), 0, ...import_common8.Convert.base64Url(joseHeader.p2s).toUint8Array() ]); } catch { throw new CryptoError("encodingError" /* EncodingError */, 'Failed to decode the JOSE Header "p2s" (PBES2 salt) value.'); } const kek = await crypto3.deriveKey({ algorithm: joseHeader.alg, baseKeyBytes: key, iterations: joseHeader.p2c, salt }); if (!(kek.alg && ["A128KW", "A192KW", "A256KW"].includes(kek.alg))) { throw new CryptoError("algorithmNotSupported" /* AlgorithmNotSupported */, `Unsupported Key Encryption Algorithm (alg) value: ${kek.alg}`); } return await crypto3.unwrapKey({ decryptionKey: kek, wrappedKeyBytes: encryptedKey, wrappedKeyAlgorithm: joseHeader.enc }); } default: { throw new CryptoError( "algorithmNotSupported" /* AlgorithmNotSupported */, `Unsupported "alg" (Algorithm) Header Parameter value: ${joseHeader.alg}` ); } } } /** * Encrypts a Content Encryption Key (CEK) using the key management algorithm specified in the * JWE Header's "alg" parameter. * * This method supports various key management algorithms, including Direct Encryption (dir) and * PBES2 with key wrapping. * * It generates a random CEK for the specified encryption algorithm in the JWE header, which * can then be used to encrypt the actual payload. For algorithms that require an encrypted key, * it returns the CEK along with the encrypted key. * * @example * ```ts * // Encrypting the CEK with the PBES2-HS512+A256KW algorithm * const { cek, encryptedKey } = await JweKeyManagement.encrypt({ * key: Convert.string(passphrase).toUint8Array(), * joseHeader: { * alg: 'PBES2-HS512+A256KW', * enc: 'A256GCM', * p2c: 210_000, * p2s: Convert.uint8Array(saltInput).toBase64Url(), * }, * crypto: crypto: new AgentCryptoApi() * }); * ``` * * @param params - The encryption parameters. * @returns The encrypted key result containing the CEK and optionally the encrypted CEK * (JWE Encrypted Key). * @throws Throws an error if the key management algorithm is not supported or if required * parameters are missing or invalid. */ static async encrypt({ key, joseHeader, crypto: crypto3 }) { let cek; let encryptedKey; switch (joseHeader.alg) { case "dir": { if (encryptedKey !== void 0) { throw new CryptoError("invalidJwe" /* InvalidJwe */, 'JWE "encrypted_key" is not allowed when using "dir" (Direct Encryption Mode).'); } if (key instanceof Uint8Array) { throw new CryptoError("invalidJwe" /* InvalidJwe */, 'Key management "key" must be a Key URI or JWK when using "dir" (Direct Encryption Mode).'); } cek = key; break; } case "PBES2-HS256+A128KW": case "PBES2-HS384+A192KW": case "PBES2-HS512+A256KW": { if (typeof joseHeader.p2c !== "number") { throw new CryptoError("invalidJwe" /* InvalidJwe */, 'JOSE Header "p2c" (PBES2 Count) is missing or not a number.'); } if (typeof joseHeader.p2s !== "string") { throw new CryptoError("invalidJwe" /* InvalidJwe */, 'JOSE Header "p2s" (PBES2 salt) is missing or not a string.'); } if (!(key instanceof Uint8Array)) { throw new CryptoError("invalidJwe" /* InvalidJwe */, 'Key management "key" must be a Uint8Array when using "PBES2" (Key Encryption Mode).'); } cek = await crypto3.generateKey({ algorithm: joseHeader.enc }); let salt; try { salt = new Uint8Array([ ...import_common8.Convert.string(joseHeader.alg).toUint8Array(), 0, ...import_common8.Convert.base64Url(joseHeader.p2s).toUint8Array() ]); } catch { throw new CryptoError("encodingError" /* EncodingError */, 'Failed to decode the JOSE Header "p2s" (PBES2 salt) value.'); } const kek = await crypto3.deriveKey({ algorithm: joseHeader.alg, baseKeyBytes: key, iterations: joseHeader.p2c, salt }); encryptedKey = await crypto3.wrapKey({ encryptionKey: kek, unwrappedKey: cek }); break; } default: { throw new CryptoError( "algorithmNotSupported" /* AlgorithmNotSupported */, `Unsupported "alg" (Algorithm) Header Parameter value: ${joseHeader.alg}` ); } } return { cek, encryptedKey }; } }; // src/prototyping/crypto/jose/jwe-flattened.ts var import_common9 = require("@web5/common"); var import_crypto14 = require("@web5/crypto"); // src/prototyping/crypto/utils.ts function isCipher(obj) { return obj !== null && typeof obj === "object" && "encrypt" in obj && typeof obj.encrypt === "function" && "decrypt" in obj && typeof obj.decrypt === "function"; } // src/prototyping/common/object.ts function hasDuplicateProperties(...objects) { const propertySet = /* @__PURE__ */ new Set(); const objectsWithoutUndefined = objects.filter(Boolean); for (const obj of objectsWithoutUndefined) { for (const key in obj) { if (propertySet.has(key)) { return true; } propertySet.add(key); } } return false; } // src/prototyping/crypto/jose/jwe-flattened.ts function decodeHeaderParam(param, value) { if (value === void 0) return void 0; try { if (typeof value !== "string") throw new Error(); return import_common9.Convert.base64Url(value).toUint8Array(); } catch { throw new CryptoError( "invalidJwe" /* InvalidJwe */, `Failed to decode the JWE Header parameter '${param}' from Base64 URL format to Uint8Array. Ensure the value is properly encoded in Base64 URL format without padding.` ); } } var FlattenedJwe = class _FlattenedJwe { constructor(params) { /** Base64URL encoded ciphertext. */ this.ciphertext = ""; Object.assign(this, params); } static async decrypt({ jwe, key, keyManager = new import_crypto14.LocalKeyManager(), crypto: crypto3 = new AgentCryptoApi(), options = {} }) { if (!isCipher(crypto3)) { throw new CryptoError("operationNotSupported" /* OperationNotSupported */, 'Crypto API does not support the "encrypt" operation.'); } if (!isCipher(keyManager)) { throw new CryptoError("operationNotSupported" /* OperationNotSupported */, 'Key Manager does not support the "decrypt" operation.'); } if (!jwe.protected && !jwe.header && !jwe.unprotected) { throw new CryptoError( "invalidJwe" /* InvalidJwe */, 'JWE is missing the required JOSE header parameters. Please provide at least one of the following: "protected", "header", or "unprotected"' ); } if (typeof jwe.ciphertext !== "string") { throw new CryptoError("invalidJwe" /* InvalidJwe */, "JWE Ciphertext is missing or not a string."); } let parsedProtectedHeader; if (jwe.protected) { try { parsedProtectedHeader = import_common9.Convert.base64Url(jwe.protected).toObject(); } catch { throw new Error("JWE Protected Header is invalid"); } } if (hasDuplicateProperties(parsedProtectedHeader, jwe.header, jwe.unprotected)) { throw new Error( 'Duplicate properties detected. Please ensure that each parameter is defined only once across the JWE "header", "protected", and "unprotected" objects.' ); } const joseHeader = { ...parsedProtectedHeader, ...jwe.header, ...jwe.unprotected }; if (!isValidJweHeader(joseHeader)) { throw new Error('JWE Header is missing required "alg" (Algorithm) and/or "enc" (Encryption) Header Parameters'); } if (Array.isArray(options.allowedAlgValues) && !options.allowedAlgValues.includes(joseHeader.alg)) { throw new Error(`"alg" (Algorithm) Header Parameter value not allowed: ${joseHeader.alg}`); } if (Array.isArray(options.allowedEncValues) && !options.allowedEncValues.includes(joseHeader.enc)) { throw new Error(`"enc" (Encryption Algorithm) Header Parameter value not allowed: ${joseHeader.enc}`); } let cek; try { const encryptedKey = jwe.encrypted_key ? import_common9.Convert.base64Url(jwe.encrypted_key).toUint8Array() : void 0; cek = await JweKeyManagement.decrypt({ key, encryptedKey, joseHeader, keyManager, crypto: crypto3 }); } catch (error) { if (error instanceof CryptoError && (error.code === "invalidJwe" /* InvalidJwe */ || error.code === "algorithmNotSupported" /* AlgorithmNotSupported */)) { throw error; } cek = typeof key === "string" ? await keyManager.generateKey({ algorithm: joseHeader.enc }) : await crypto3.generateKey({ algorithm: joseHeader.enc }); } const iv = decodeHeaderParam("iv", jwe.iv); const tag = decodeHeaderParam("tag", jwe.tag); const ciphertext = tag !== void 0 ? new Uint8Array([ ...import_common9.Convert.base64Url(jwe.ciphertext).toUint8Array(), ...tag ?? [] ]) : import_common9.Convert.base64Url(jwe.ciphertext).toUint8Array(); const additionalData = jwe.aad !== void 0 ? new Uint8Array([ ...import_common9.Convert.string(jwe.protected ?? "").toUint8Array(), ...import_common9.Convert.string(".").toUint8Array(), ...import_common9.Convert.string(jwe.aad).toUint8Array() ]) : import_common9.Convert.string(jwe.protected ?? "").toUint8Array(); const plaintext = typeof cek === "string" ? await keyManager.decrypt({ keyUri: cek, data: ciphertext, iv, additionalData }) : await crypto3.decrypt({ key: cek, data: ciphertext, iv, additionalData }); return { plaintext, protectedHeader: parsedProtectedHeader, additionalAuthenticatedData: decodeHeaderParam("aad", jwe.aad), sharedUnprotectedHeader: jwe.unprotected, unprotectedHeader: jwe.header }; } static async encrypt({ key, plaintext, additionalAuthenticatedData, protectedHeader, sharedUnprotectedHeader, unprotectedHeader, keyManager = new import_crypto14.LocalKeyManager(), crypto: crypto3 = new AgentCryptoApi() }) { if (!isCipher(crypto3)) { throw new CryptoError("operationNotSupported" /* OperationNotSupported */, 'Crypto API does not support the "encrypt" operation.'); } if (!isCipher(keyManager)) { throw new CryptoError("operationNotSupported" /* OperationNotSupported */, 'Key Manager does not support the "decrypt" operation.'); } if (!protectedHeader && !sharedUnprotectedHeader && !unprotectedHeader) { throw new CryptoError( "invalidJwe" /* InvalidJwe */, 'JWE is missing the required JOSE header parameters. Please provide at least one of the following: "protectedHeader", "sharedUnprotectedHeader", or "unprotectedHeader"' ); } if (!(plaintext instanceof Uint8Array)) { throw new CryptoError("invalidJwe" /* InvalidJwe */, "Plaintext is missing or not a byte array."); } if (hasDuplicateProperties(protectedHeader, sharedUnprotectedHeader, unprotectedHeader)) { throw new Error( 'Duplicate properties detected. Please ensure that each parameter is defined only once across the JWE "protectedHeader", "sharedUnprotectedHeader", and "unprotectedHeader" objects.' ); } const joseHeader = { ...protectedHeader, ...sharedUnprotectedHeader, ...unprotectedHeader }; if (!isValidJweHeader(joseHeader)) { throw new Error('JWE Header is missing required "alg" (Algorithm) and/or "enc" (Encryption) Header Parameters'); } const { cek, encryptedKey } = await JweKeyManagement.encrypt({ key, joseHeader, keyManager, crypto: crypto3 }); let iv; switch (joseHeader.enc) { case "A128GCM": case "A192GCM": case "A256GCM": iv = import_crypto14.utils.randomBytes(12); break; default: iv = new Uint8Array(0); } const encodedProtectedHeader = protectedHeader ? import_common9.Convert.object(protectedHeader).toBase64Url() : ""; let additionalData; let encodedAad; if (additionalAuthenticatedData) { encodedAad = import_common9.Convert.uint8Array(additionalAuthenticatedData).toBase64Url(); additionalData = import_common9.Convert.string(encodedProtectedHeader + "." + encodedAad).toUint8Array(); } else { additionalData = import_common9.Convert.string(encodedProtectedHeader).toUint8Array(); } const ciphertextWithTag = typeof cek === "string" ? await keyManager.encrypt({ keyUri: cek, data: plaintext, iv, additionalData }) : await crypto3.encrypt({ key: cek, data: plaintext, iv, additionalData }); const ciphertext = ciphertextWithTag.slice(0, -16); const authenticationTag = ciphertextWithTag.slice(-16); const jwe = new _FlattenedJwe({ ciphertext: import_common9.Convert.uint8Array(ciphertext).toBase64Url() }); if (encryptedKey) jwe.encrypted_key = import_common9.Convert.uint8Array(encryptedKey).toBase64Url(); if (protectedHeader) jwe.protected = encodedProtectedHeader; if (sharedUnprotectedHeader) jwe.unprotected = sharedUnprotectedHeader; if (unprotectedHeader) jwe.header = unprotectedHeader; if (iv) jwe.iv = import_common9.Convert.uint8Array(iv).toBase64Url(); if (encodedAad) jwe.aad = encodedAad; if (authenticationTag) jwe.tag = import_common9.Convert.uint8Array(authenticationTag).toBase64Url(); return jwe; } }; // src/prototyping/crypto/jose/jwe-compact.ts var CompactJwe = class { /** * Decrypts a JWE string in Compact Serialization format, extracting the plaintext and * reconstructing the JWE Protected Header. * * This method parses the compact JWE, validates its structure, and applies the appropriate * decryption algorithm as specified in the JWE Protected Header. It returns the decrypted * plaintext along with the reconstructed protected header, ensuring the data's authenticity * and integrity. * * @param params - The decryption parameters including the JWE string, cryptographic key, and * optional instances of Key Manager and Crypto API. * @returns A promise resolving to the decrypted content and the JWE Protected Header. * @throws {@link CryptoError} if the JWE format is invalid or decryption fails. */ static async decrypt({ jwe, key, keyManager = new import_crypto15.LocalKeyManager(), crypto: crypto3 = new AgentCryptoApi(), options = {} }) { if (typeof jwe !== "string") { throw new CryptoError("invalidJwe" /* InvalidJwe */, "Invalid JWE format. JWE must be a string."); } const { 0: protectedHeader, 1: encryptedKey, 2: initializationVector, 3: ciphertext, 4: authenticationTag, length } = jwe.split("."); if (length !== 5) { throw new CryptoError("invalidJwe" /* InvalidJwe */, "Invalid JWE format. JWE must have 5 parts."); } const flattenedJwe = await FlattenedJwe.decrypt({ jwe: { ciphertext, encrypted_key: encryptedKey || void 0, iv: initializationVector || void 0, protected: protectedHeader, tag: authenticationTag || void 0 }, key, keyManager, crypto: crypto3, options }); if (!isValidJweHeader(flattenedJwe.protectedHeader)) { throw new CryptoError("invalidJwe" /* InvalidJwe */, "Decrypt operation failed due to missing or malformed JWE Protected Header"); } return { plaintext: flattenedJwe.plaintext, protectedHeader: flattenedJwe.protectedHeader }; } /** * Encrypts plaintext to a JWE string in Compact Serialization format, encapsulating the content * with the specified cryptographic protections. * * It constructs the JWE by encrypting the plaintext, then serializing the output to the * compact format, which includes concatenating various components like the protected header, * encrypted key, initialization vector, ciphertext, and authentication tag. * * @param params - The encryption parameters, including plaintext, JWE Protected Header, * cryptographic key, and optional Key Manager and Crypto API instances. * @returns A promise that resolves to a string representing the JWE in Compact Serialization * format. * @throws {@link CryptoError} if encryption fails or the input parameters are invalid. */ static async encrypt({ plaintext, protectedHeader, key, keyManager = new import_crypto15.LocalKeyManager(), crypto: crypto3 = new AgentCryptoApi(), options = {} }) { const jwe = await FlattenedJwe.encrypt({ plaintext, protectedHeader, key, keyManager, crypto: crypto3, options }); return [jwe.protected, jwe.encrypted_key, jwe.iv, jwe.ciphertext, jwe.tag].join("."); } }; // src/hd-identity-vault.ts function isEmptyString(obj) { return typeof obj !== "string" || obj.trim().length === 0; } function isIdentityVaultBackup(obj) { return typeof obj === "object" && obj !== null && "dateCreated" in obj && typeof obj.dateCreated === "string" && "size" in obj && typeof obj.size === "number" && "data" in obj && typeof obj.data === "string"; } function isIdentityVaultStatus(obj) { return typeof obj === "object" && obj !== null && "initialized" in obj && typeof obj.initialized === "boolean" && "lastBackup" in obj && "lastRestore" in obj; } var HdIdentityVault = class { /** * Constructs an instance of `HdIdentityVault`, initializing the key derivation factor and data * store. It sets the default key derivation work factor and initializes the internal data store, * either with the provided store or a default in-memory store. It also establishes the initial * status of the vault as uninitialized and locked. * * @param params - Optional parameters when constructing a vault instance. * @param params.keyDerivationWorkFactor - Optionally set the computational effort for key derivation. * @param params.store - Optionally specify a custom key-value store for vault data. */ constructor({ keyDerivationWorkFactor, store } = {}) { /** Provides cryptographic functions needed for secure storage and management of the vault. */ this.crypto = new AgentCryptoApi(); this._keyDerivationWorkFactor = keyDerivationWorkFactor ?? 21e4; this._store = store ?? new import_common10.MemoryStore(); } /** * Creates a backup of the vault's current state, including the encrypted DID and content * encryption key, and returns it as an `IdentityVaultBackup` object. The backup includes a * Base64Url-encoded string representing the vault's encrypted data, encapsulating the * {@link PortableDid}, the content encryption key, and the vault's status. * * This method ensures that the vault is initialized and unlocked before proceeding with the * backup operation. * * @throws Error if the vault is not initialized or is locked, preventing the backup. * @returns A promise that resolves to the `IdentityVaultBackup` object containing the vault's * encrypted backup data. */ async backup() { if (this.isLocked() || await this.isInitialized() === false) { throw new Error( "HdIdentityVault: Unable to proceed with the backup operation because the identity vault has not been initialized and unlocked. Please ensure the vault is properly initialized with a secure password before attempting to backup its contents." ); } const backupData = { did: await this.getStoredDid(), contentEncryptionKey: await this.getStoredContentEncryptionKey(), status: await this.getStatus() }; const backupDataString = import_common10.Convert.object(backupData).toBase64Url(); const backup = { data: backupDataString, dateCreated: (/* @__PURE__ */ new Date()).toISOString(), size: backupDataString.length }; await this.setStatus({ lastBackup: backup.dateCreated }); return backup; } /** * Changes the password used to secure the vault. * * This method decrypts the existing content encryption key (CEK) with the old password, then * re-encrypts it with the new password, updating the vault's stored encrypted CEK. It ensures * that the vault is initialized and unlocks the vault if the password is successfully changed. * * @param params - Parameters required for changing the vault password. * @param params.oldPassword - The current password used to unlock the vault. * @param params.newPassword - The new password to replace the existing one. * @throws Error if the vault is not initialized or the old password is incorrect. * @returns A promise that resolves when the password change is complete. */ async changePassword({ oldPassword, newPassword }) { if (await this.isInitialized() === false) { throw new Error( "HdIdentityVault: Unable to proceed with the change password operation because the identity vault has not been initialized. Please ensure the vault is properly initialized with a secure password before trying again." ); } await this.lock(); const cekJwe = await this.getStoredContentEncryptionKey(); let protectedHeader; let contentEncryptionKey; try { let contentEncryptionKeyBytes; ({ plaintext: contentEncryptionKeyBytes, protectedHeader } = await CompactJwe.decrypt({ jwe: cekJwe, key: import_common10.Convert.string(oldPassword).toUint8Array(), crypto: this.crypto, keyManager: new LocalKeyManager2() })); contentEncryptionKey = import_common10.Convert.uint8Array(contentEncryptionKeyBytes).toObject(); } catch (error) { throw new Error(`HdIdentityVault: Unable to change the vault password due to an incorrectly entered old password.`); } const newCekJwe = await CompactJwe.encrypt({ key: import_common10.Convert.string(newPassword).toUint8Array(), protectedHeader, // Re-use the protected header from the original JWE. plaintext: import_common10.Convert.object(contentEncryptionKey).toUint8Array(), crypto: this.crypto, keyManager: new LocalKeyManager2() }); await this._store.set("contentEncryptionKey", newCekJwe); this._contentEncryptionKey = contentEncryptionKey; } /** * Retrieves the DID (Decentralized Identifier) associated with the vault. * * This method ensures the vault is initialized and unlocked before decrypting and returning the * DID. The DID is stored encrypted and is decrypted using the vault's content encryption key. * * @throws Error if the vault is not initialized, is locked, or the DID cannot be decrypted. * @returns A promise that resolves with a {@link BearerDid}. */ async getDid() { if (this.isLocked()) { throw new Error(`HdIdentityVault: Vault has not been initialized and unlocked.`); } const didJwe = await this.getStoredDid(); const { plaintext: portableDidBytes } = await CompactJwe.decrypt({ jwe: didJwe, key: this._contentEncryptionKey, crypto: this.crypto, keyManager: new LocalKeyManager2() }); const portableDid = import_common10.Convert.uint8Array(portableDidBytes).toObject(); if (!isPortableDid(portableDid)) { throw new Error("HdIdentityVault: Unable to decode malformed DID in identity vault"); } return await import_dids4.BearerDid.import({ portableDid }); } /** * Fetches the current status of the `HdIdentityVault`, providing details on whether it's * initialized and the timestamps of the last backup and restore operations. * * @returns A promise that resolves with the current status of the `HdIdentityVault`, detailing * its initialization, lock state, and the timestamps of the last backup and restore. */ async getStatus() { const storedStatus = await this._store.get("vaultStatus"); if (!storedStatus) { return { initialized: false, lastBackup: null, lastRestore: null }; } const vaultStatus = import_common10.Convert.string(storedStatus).toObject(); if (!isIdentityVaultStatus(vaultStatus)) { throw new Error("HdIdentityVault: Invalid IdentityVaultStatus object in store"); } return vaultStatus; } /** * Initializes the `HdIdentityVault` with a password and an optional recovery phrase. * * If a recovery phrase is not provided, a new one is generated. This process sets up the vault, * deriving the necessary cryptographic keys and preparing the vault for use. It ensures the vault * is ready to securely store and manage identity data. * * @example * ```ts * const identityVault = new HdIdentityVault(); * const recoveryPhrase = await identityVault.initialize({ * password: 'your-secure-phrase' * }); * console.log('Vault initialized. Recovery phrase:', recoveryPhrase); * ``` * * @param params - The initialization parameters. * @param params.password - The password used to secure the vault. * @param params.recoveryPhrase - An optional 12-word recovery phrase for key derivation. If * omitted, a new recovery is generated. * @returns A promise that resolves with the recovery phrase used during the initialization, which * should be securely stored by the user. */ async initialize({ password, recoveryPhrase }) { if (await this.isInitialized()) { throw new Error(`HdIdentityVault: Vault has already been initialized.`); } if (isEmptyString(password)) { throw new Error( `HdIdentityVault: The password is required and cannot be blank. Please provide a ' + 'valid, non-empty password.` ); } if (recoveryPhrase && isEmptyString(recoveryPhrase)) { throw new Error( `HdIdentityVault: The password is required and cannot be blank. Please provide a ' + 'valid, non-empty password.` ); } recoveryPhrase ??= (0, import_bip39.generateMnemonic)(import_english.wordlist, 128); if (!(0, import_bip39.validateMnemonic)(recoveryPhrase, import_english.wordlist)) { throw new Error( "HdIdentityVault: The provided recovery phrase is invalid. Please ensure that the recovery phrase is a correctly formatted series of 12 words." ); } const rootSeed = await (0, import_bip39.mnemonicToSeed)(recoveryPhrase); const rootHdKey = HDKey.fromMasterSeed(rootSeed); const vaultHdKey = rootHdKey.derive(`m/44'/0'/0'/0'/0'`); const contentEncryptionKey = await this.crypto.deriveKey({ algorithm: "HKDF-512", // key derivation function baseKeyBytes: vaultHdKey.privateKey, // input keying material salt: "", // empty salt because private key is sufficiently random info: "vault_cek", // non-secret application specific information derivedKeyAlgorithm: "A256GCM" // derived key algorithm }); const saltInput = await this.crypto.deriveKeyBytes({ algorithm: "HKDF-512", // key derivation function baseKeyBytes: vaultHdKey.publicKey, // input keying material salt: "", // empty salt because public key is sufficiently random info: "vault_unlock_salt", // non-secret application specific information length: 256 // derived key length, in bits }); const cekJweProtectedHeader = { alg: "PBES2-HS512+A256KW", enc: "A256GCM", cty: "text/plain", p2c: this._keyDerivationWorkFactor, p2s: import_common10.Convert.uint8Array(saltInput).toBase64Url() }; const cekJwe = await CompactJwe.encrypt({ key: import_common10.Convert.string(password).toUint8Array(), protectedHeader: cekJweProtectedHeader, plaintext: import_common10.Convert.object(contentEncryptionKey).toUint8Array(), crypto: this.crypto, keyManager: new LocalKeyManager2() }); await this._store.set("contentEncryptionKey", cekJwe); const identityHdKey = rootHdKey.derive(`m/44'/0'/1708523827'/0'/0'`); const identityPrivateKey = await this.crypto.bytesToPrivateKey({ algorithm: "Ed25519", privateKeyBytes: identityHdKey.privateKey }); let signingHdKey = rootHdKey.derive(`m/44'/0'/1708523827'/0'/1'`); const signingPrivateKey = await this.crypto.bytesToPrivateKey({ algorithm: "Ed25519", privateKeyBytes: signingHdKey.privateKey }); const deterministicKeyGenerator = new DeterministicKeyGenerator(); await deterministicKeyGenerator.addPredefinedKeys({ privateKeys: [identityPrivateKey, signingPrivateKey] }); const did = await import_dids4.DidDht.create({ keyManager: deterministicKeyGenerator, options: { verificationMethods: [ { algorithm: "Ed25519", id: "sig", purposes: ["assertionMethod", "authentication"] } // TODO: Enable this once DID DHT supports X25519 keys. // { // algorithm : 'X25519', // id : 'enc', // purposes : ['keyAgreement'] // } ] } }); const portableDid = await did.export(); const didJweProtectedHeader = { alg: "dir", enc: "A256GCM", cty: "json" }; const didJwe = await CompactJwe.encrypt({ key: contentEncryptionKey, plaintext: import_common10.Convert.object(portableDid).toUint8Array(), protectedHeader: didJweProtectedHeader, crypto: this.crypto, keyManager: new LocalKeyManager2() }); await this._store.set("did", didJwe); this._contentEncryptionKey = contentEncryptionKey; await this.setStatus({ initialized: true }); return recoveryPhrase; } /** * Determines whether the vault has been initialized. * * This method checks the vault's current status to determine if it has been * initialized. Initialization is a prerequisite for most operations on the vault, * ensuring that it is ready for use. * * @example * ```ts * const isInitialized = await identityVault.isInitialized(); * console.log('Is the vault initialized?', isInitialized); * ``` * * @returns A promise that resolves to `true` if the vault has been initialized, otherwise `false`. */ async isInitialized() { return this.getStatus().then(({ initialized }) => initialized); } /** * Checks if the vault is currently locked. * * This method assesses the vault's current state to determine if it is locked. * A locked vault restricts access to its contents, requiring the correct password * to unlock and access the stored identity data. The vault must be unlocked to * perform operations that access or modify its contents. * * @example * ```ts * const isLocked = await identityVault.isLocked(); * console.log('Is the vault locked?', isLocked); * ``` * * @returns `true` if the vault is locked, otherwise `false`. */ isLocked() { return !this._contentEncryptionKey; } /** * Locks the `HdIdentityVault`, securing its contents by clearing the in-memory encryption key. * * This method ensures that the vault's sensitive data cannot be accessed without unlocking the * vault again with the correct password. It's an essential security feature for safeguarding * the vault's contents against unauthorized access. * * @example * ```ts * const identityVault = new HdIdentityVault(); * await identityVault.lock(); * console.log('Vault is now locked.'); * ``` * @throws An error if the identity vault has not been initialized. * @returns A promise that resolves when the vault is successfully locked. */ async lock() { if (await this.isInitialized() === false) { throw new Error(`HdIdentityVault: Lock operation failed. Vault has not been initialized.`); } if (this._contentEncryptionKey) this._contentEncryptionKey.k = ""; this._contentEncryptionKey = void 0; } /** * Restores the vault's data from a backup object, decrypting and reinitializing the vault's * content with the provided backup data. * * This operation is crucial for data recovery scenarios, allowing users to regain access to their * encrypted data using a previously saved backup and their password. * * @example * ```ts * const identityVault = new HdIdentityVault(); * await identityVault.initialize({ password: 'your-secure-phrase' }); * // Create a backup of the vault's contents. * const backup = await identityVault.backup(); * // Restore the vault with the same password. * await identityVault.restore({ backup: backup, password: 'your-secure-phrase' }); * console.log('Vault restored successfully.'); * ``` * * @param params - The parameters required for the restore operation. * @param params.backup - The backup object containing the encrypted vault data. * @param params.password - The password used to encrypt the backup, necessary for decryption. * @returns A promise that resolves when the vault has been successfully restored. * @throws An error if the backup object is invalid or if the password is incorrect. */ async restore({ backup, password }) { if (!isIdentityVaultBackup(backup)) { throw new Error(`HdIdentityVault: Restore operation failed due to invalid backup object.`); } let previousStatus; let previousContentEncryptionKey; let previousDid; try { previousDid = await this.getStoredDid(); previousContentEncryptionKey = await this.getStoredContentEncryptionKey(); previousStatus = await this.getStatus(); } catch { throw new Error( "HdIdentityVault: The restore operation cannot proceed because the existing vault contents are missing or inaccessible. If the problem persists consider re-initializing the vault and retrying the restore." ); } try { const backupData = import_common10.Convert.base64Url(backup.data).toObject(); await this._store.set("did", backupData.did); await this._store.set("contentEncryptionKey", backupData.contentEncryptionKey); await this.setStatus(backupData.status); await this.unlock({ password }); } catch (error) { await this.setStatus(previousStatus); await this._store.set("contentEncryptionKey", previousContentEncryptionKey); await this._store.set("did", previousDid); throw new Error( "HdIdentityVault: Restore operation failed due to invalid backup data or an incorrect password. Please verify the password is correct for the provided backup and try again." ); } await this.setStatus({ lastRestore: (/* @__PURE__ */ new Date()).toISOString() }); } /** * Unlocks the vault by decrypting the stored content encryption key (CEK) using the provided * password. * * This method is essential for accessing the vault's encrypted contents, enabling the decryption * of stored data and the execution of further operations requiring the vault to be unlocked. * * @example * ```ts * const identityVault = new HdIdentityVault(); * await identityVault.initialize({ password: 'your-initial-phrase' }); * // Unlock the vault with the correct password before accessing its contents * await identityVault.unlock({ password: 'your-initial-phrase' }); * console.log('Vault unlocked successfully.'); * ``` * * * @param params - The parameters required for the unlock operation. * @param params.password - The password used to encrypt the vault's CEK, necessary for * decryption. * @returns A promise that resolves when the vault has been successfully unlocked. * @throws An error if the vault has not been initialized or if the provided password is * incorrect. */ async unlock({ password }) { await this.lock(); const cekJwe = await this.getStoredContentEncryptionKey(); try { const { plaintext: contentEncryptionKeyBytes } = await CompactJwe.decrypt({ jwe: cekJwe, key: import_common10.Convert.string(password).toUint8Array(), crypto: this.crypto, keyManager: new LocalKeyManager2() }); const contentEncryptionKey = import_common10.Convert.uint8Array(contentEncryptionKeyBytes).toObject(); this._contentEncryptionKey = contentEncryptionKey; } catch (error) { throw new Error(`HdIdentityVault: Unable to unlock the vault due to an incorrect password.`); } } /** * Retrieves the Decentralized Identifier (DID) associated with the identity vault from the vault * store. * * This DID is encrypted in compact JWE format and needs to be decrypted after the vault is * unlocked. The method is intended to be used internally within the HdIdentityVault class to access * the encrypted PortableDid. * * @returns A promise that resolves to the encrypted DID stored in the vault as a compact JWE. * @throws Will throw an error if the DID cannot be retrieved from the vault. */ async getStoredDid() { const didJwe = await this._store.get("did"); if (!didJwe) { throw new Error( "HdIdentityVault: Unable to retrieve the DID record from the vault. Please check the vault status and if the problem persists consider re-initializing the vault and restoring the contents from a previous backup." ); } return didJwe; } /** * Retrieves the encrypted Content Encryption Key (CEK) from the vault's storage. * * This CEK is used for encrypting and decrypting the vault's contents. It is stored as a * compact JWE and should be decrypted with the user's password to be used for further * cryptographic operations. * * @returns A promise that resolves to the stored CEK as a string in compact JWE format. * @throws Will throw an error if the CEK cannot be retrieved, indicating potential issues with * the vault's integrity or state. */ async getStoredContentEncryptionKey() { const cekJwe = await this._store.get("contentEncryptionKey"); if (!cekJwe) { throw new Error( "HdIdentityVault: Unable to retrieve the Content Encryption Key record from the vault. Please check the vault status and if the problem persists consider re-initializing the vault and restoring the contents from a previous backup." ); } return cekJwe; } /** * Updates the status of the `HdIdentityVault`, reflecting changes in its initialization, lock * state, and the timestamps of the last backup and restore operations. * * This method directly manipulates the internal state stored in the vault's key-value store. * * @param params - The status properties to be updated. * @param params.initialized - Updates the initialization state of the vault. * @param params.lastBackup - Updates the timestamp of the last successful backup. * @param params.lastRestore - Updates the timestamp of the last successful restore. * @returns A promise that resolves to a boolean indicating successful status update. * @throws Will throw an error if the status cannot be updated in the key-value store. */ async setStatus({ initialized, lastBackup, lastRestore }) { let vaultStatus = await this.getStatus(); vaultStatus.initialized = initialized ?? vaultStatus.initialized; vaultStatus.lastBackup = lastBackup ?? vaultStatus.lastBackup; vaultStatus.lastRestore = lastRestore ?? vaultStatus.lastRestore; await this._store.set("vaultStatus", JSON.stringify(vaultStatus)); return true; } }; // src/store-identity.ts var import_common11 = require("@web5/common"); function isIdentityMetadata(obj) { return !(!obj || typeof obj !== "object" || obj === null) && "name" in obj; } var DwnIdentityStore = class extends DwnDataStore { constructor() { super(...arguments); this.name = "DwnIdentityStore"; /** * Properties to use when writing and querying Identity records with the DWN store. */ this._recordProperties = { dataFormat: "application/json", schema: "https://identity.foundation/schemas/web5/identity-metadata" }; } async delete(params) { return await super.delete(params); } async get(params) { return await super.get(params); } async set(params) { return await super.set(params); } async list(params) { return await super.list(params); } async getAllRecords({ agent, tenantDid }) { this._index.clear(); const { reply: queryReply } = await agent.dwn.processRequest({ author: tenantDid, target: tenantDid, messageType: DwnInterface.RecordsQuery, messageParams: { filter: { ...this._recordProperties } } }); let storedIdentities = []; for (const record of queryReply.entries ?? []) { if (!record.encodedData) { throw new Error(`${this.name}: Expected 'encodedData' to be present in the DWN query result entry`); } const storedIdentity = import_common11.Convert.base64Url(record.encodedData).toObject(); if (isIdentityMetadata(storedIdentity)) { const indexKey = `${tenantDid}${TENANT_SEPARATOR}${storedIdentity.uri}`; this._index.set(indexKey, record.recordId); this._cache.set(record.recordId, storedIdentity); storedIdentities.push(storedIdentity); } } return storedIdentities; } }; var InMemoryIdentityStore = class extends InMemoryDataStore { constructor() { super(...arguments); this.name = "InMemoryIdentityStore"; } async delete(params) { return await super.delete(params); } async get(params) { return await super.get(params); } async list(params) { return await super.list(params); } async set(params) { return await super.set(params); } }; // src/identity-api.ts function isPortableIdentity(obj) { return !(!obj || typeof obj !== "object" || obj === null) && "did" in obj && "metadata" in obj && isPortableDid(obj.did); } var AgentIdentityApi = class { constructor({ agent, store } = {}) { this._agent = agent; this._store = store ?? new InMemoryIdentityStore(); } /** * Retrieves the `Web5PlatformAgent` execution context. * * @returns The `Web5PlatformAgent` instance that represents the current execution context. * @throws Will throw an error if the `agent` instance property is undefined. */ get agent() { if (this._agent === void 0) { throw new Error("AgentIdentityApi: Unable to determine agent execution context."); } return this._agent; } set agent(agent) { this._agent = agent; } async create({ metadata, didMethod = "dht", didOptions, store, tenant }) { const bearerDid = await this.agent.did.create({ method: didMethod, options: didOptions, store, tenant }); const identity = new BearerIdentity({ did: bearerDid, metadata: { ...metadata, uri: bearerDid.uri, tenant: tenant ?? bearerDid.uri } }); if (store ?? true) { await this._store.set({ id: identity.did.uri, data: identity.metadata, agent: this.agent, tenant: identity.metadata.tenant, preventDuplicates: false, useCache: true }); } return identity; } async export({ didUri, tenant }) { const bearerIdentity = await this.get({ didUri, tenant }); if (!bearerIdentity) { throw new Error(`AgentIdentityApi: Failed to export due to Identity not found: ${didUri}`); } const portableIdentity = await bearerIdentity.export(); return portableIdentity; } async get({ didUri, tenant }) { const storedIdentity = await this._store.get({ id: didUri, agent: this.agent, tenant, useCache: true }); if (!storedIdentity) return void 0; const storedDid = await this.agent.did.get({ didUri, tenant: storedIdentity.tenant }); if (!storedDid) { throw new Error(`AgentIdentityApi: Identity is present in the store but DID is missing: ${didUri}`); } const identity = new BearerIdentity({ did: storedDid, metadata: storedIdentity }); return identity; } async import({ portableIdentity }) { const storedDid = await this.agent.did.import({ portableDid: portableIdentity.portableDid, tenant: portableIdentity.metadata.tenant }); if (!storedDid) { throw new Error(`AgentIdentityApi: Failed to import Identity: ${portableIdentity.metadata.uri}`); } const identity = new BearerIdentity({ did: storedDid, metadata: portableIdentity.metadata }); await this._store.set({ id: identity.did.uri, data: identity.metadata, agent: this.agent, tenant: identity.metadata.tenant, preventDuplicates: true, useCache: true }); return identity; } async list({ tenant } = {}) { const storedIdentities = await this._store.list({ agent: this.agent, tenant }); const identities = []; for (const metadata of storedIdentities) { const identity = await this.get({ didUri: metadata.uri, tenant: metadata.tenant }); identities.push(identity); } return identities; } async manage({ portableIdentity }) { const storedDid = await this.agent.did.get({ didUri: portableIdentity.metadata.uri, tenant: portableIdentity.metadata.tenant }); if (!storedDid) { throw new Error(`AgentIdentityApi: Failed to manage Identity: ${portableIdentity.metadata.uri}`); } const identity = new BearerIdentity({ did: storedDid, metadata: portableIdentity.metadata }); await this._store.set({ id: identity.did.uri, data: identity.metadata, agent: this.agent, preventDuplicates: true, useCache: true }); return identity; } }; // src/rpc-client.ts var import_crypto19 = require("@web5/crypto"); // src/prototyping/clients/json-rpc.ts var createJsonRpcRequest = (id, method, params) => { return { jsonrpc: "2.0", id, method, params }; }; var createJsonRpcSubscriptionRequest = (id, method, subscriptionId, params) => { return { jsonrpc: "2.0", id, method: `rpc.subscribe.${method}`, params, subscription: { id: subscriptionId } }; }; function parseJson(text) { try { return JSON.parse(text); } catch { return null; } } // src/prototyping/clients/http-dwn-rpc-client.ts var import_crypto16 = require("@web5/crypto"); // src/prototyping/clients/dwn-server-info-cache-memory.ts var import_ms3 = __toESM(require("ms"), 1); var import_common12 = require("@web5/common"); var DwnServerInfoCacheMemory = class { constructor({ ttl = "15m" } = {}) { this.cache = new import_common12.TtlCache({ ttl: (0, import_ms3.default)(ttl) }); } /** * Retrieves a DWN ServerInfo entry from the cache. * * If the cached item has exceeded its TTL, it's scheduled for deletion and undefined is returned. * * @param dwnUrl - The DWN URL endpoint string used as the key for getting the entry. * @returns The cached DWN ServerInfo entry or undefined if not found or expired. */ async get(dwnUrl) { return this.cache.get(dwnUrl); } /** * Stores a DWN ServerInfo entry in the cache with a TTL. * * @param dwnUrl - The DWN URL endpoint string used as the key for storing the entry. * @param value - The DWN ServerInfo entry to be cached. * @returns A promise that resolves when the operation is complete. */ async set(dwnUrl, value) { this.cache.set(dwnUrl, value); } /** * Deletes a DWN ServerInfo entry from the cache. * * @param dwnUrl - The DWN URL endpoint string used as the key for deletion. * @returns A promise that resolves when the operation is complete. */ async delete(dwnUrl) { this.cache.delete(dwnUrl); } /** * Clears all entries from the cache. * * @returns A promise that resolves when the operation is complete. */ async clear() { this.cache.clear(); } /** * This method is a no-op but exists to be consistent with other DWN ServerInfo Cache * implementations. * * @returns A promise that resolves immediately. */ async close() { } }; // src/prototyping/clients/http-dwn-rpc-client.ts var HttpDwnRpcClient = class { constructor(serverInfoCache) { this.serverInfoCache = serverInfoCache ?? new DwnServerInfoCacheMemory(); } get transportProtocols() { return ["http:", "https:"]; } async sendDwnRequest(request) { const requestId = import_crypto16.utils.randomUuid(); const jsonRpcRequest = createJsonRpcRequest(requestId, "dwn.processMessage", { target: request.targetDid, message: request.message }); const fetchOpts = { method: "POST", headers: { "dwn-request": JSON.stringify(jsonRpcRequest) } }; if (request.data) { fetchOpts.headers["content-type"] = "application/octet-stream"; fetchOpts["body"] = request.data; } const resp = await fetch(request.dwnUrl, fetchOpts); let dwnRpcResponse; let dataStream; const { headers } = resp; if (headers.has("dwn-response")) { const jsonRpcResponse = parseJson(headers.get("dwn-response")); if (jsonRpcResponse == null) { throw new Error(`failed to parse json rpc response. dwn url: ${request.dwnUrl}`); } dataStream = resp.body; dwnRpcResponse = jsonRpcResponse; } else { const responseBody = await resp.text(); dwnRpcResponse = JSON.parse(responseBody); } if (dwnRpcResponse.error) { const { code, message } = dwnRpcResponse.error; throw new Error(`(${code}) - ${message}`); } const { reply } = dwnRpcResponse.result; if (dataStream) { reply["record"]["data"] = dataStream; } return reply; } async getServerInfo(dwnUrl) { const serverInfo = await this.serverInfoCache.get(dwnUrl); if (serverInfo) { return serverInfo; } const url = new URL(dwnUrl); url.pathname.endsWith("/") ? url.pathname += "info" : url.pathname += "/info"; try { const response = await fetch(url.toString()); if (response.ok) { const results = await response.json(); const serverInfo2 = { registrationRequirements: results.registrationRequirements, maxFileSize: results.maxFileSize, webSocketSupport: results.webSocketSupport }; this.serverInfoCache.set(dwnUrl, serverInfo2); return serverInfo2; } else { throw new Error(`HTTP (${response.status}) - ${response.statusText}`); } } catch (error) { throw new Error(`Error encountered while processing response from ${url.toString()}: ${error.message}`); } } }; // src/prototyping/clients/web-socket-clients.ts var import_crypto18 = require("@web5/crypto"); // src/prototyping/clients/json-rpc-socket.ts var import_crypto17 = require("@web5/crypto"); var import_isomorphic_ws = __toESM(require("isomorphic-ws"), 1); var CONNECT_TIMEOUT = 3e3; var RESPONSE_TIMEOUT = 3e4; var JsonRpcSocket = class _JsonRpcSocket { constructor(socket, responseTimeout) { this.socket = socket; this.responseTimeout = responseTimeout; this.messageHandlers = /* @__PURE__ */ new Map(); } static async connect(url, options = {}) { const { connectTimeout = CONNECT_TIMEOUT, responseTimeout = RESPONSE_TIMEOUT, onclose, onerror } = options; const socket = new import_isomorphic_ws.default(url); if (!onclose) { socket.onclose = () => { console.info(`JSON RPC Socket close ${url}`); }; } else { socket.onclose = onclose; } if (!onerror) { socket.onerror = (error) => { console.error(`JSON RPC Socket error ${url}`, error); }; } else { socket.onerror = onerror; } return new Promise((resolve, reject) => { socket.addEventListener("open", () => { const jsonRpcSocket = new _JsonRpcSocket(socket, responseTimeout); socket.addEventListener("message", (event) => { const jsonRpcResponse = parseJson(event.data); const handler = jsonRpcSocket.messageHandlers.get(jsonRpcResponse.id); if (handler) { handler(event); } }); resolve(jsonRpcSocket); }); socket.addEventListener("error", (error) => { reject(error); }); setTimeout(() => reject, connectTimeout); }); } close() { this.socket.close(); } /** * Sends a JSON-RPC request through the socket and waits for a single response. */ async request(request) { return new Promise((resolve, reject) => { request.id ??= import_crypto17.utils.randomUuid(); const handleResponse = (event) => { const jsonRpsResponse = parseJson(event.data); if (jsonRpsResponse.id === request.id) { this.messageHandlers.delete(request.id); return resolve(jsonRpsResponse); } }; this.messageHandlers.set(request.id, handleResponse); this.send(request); setTimeout(() => { this.messageHandlers.delete(request.id); reject(new Error("request timed out")); }, this.responseTimeout); }); } /** * Sends a JSON-RPC request through the socket and keeps a listener open to read associated responses as they arrive. * Returns a close method to clean up the listener. */ async subscribe(request, listener) { if (!request.method.startsWith("rpc.subscribe.")) { throw new Error("subscribe rpc requests must include the `rpc.subscribe` prefix"); } if (!request.subscription) { throw new Error("subscribe rpc requests must include subscribe options"); } const subscriptionId = request.subscription.id; const socketEventListener = (event) => { const jsonRpcResponse = parseJson(event.data.toString()); if (jsonRpcResponse.id === subscriptionId) { if (jsonRpcResponse.error !== void 0) { this.messageHandlers.delete(subscriptionId); this.closeSubscription(subscriptionId); } listener(jsonRpcResponse); } }; this.messageHandlers.set(subscriptionId, socketEventListener); const response = await this.request(request); if (response.error) { this.messageHandlers.delete(subscriptionId); return { response }; } const close = async () => { this.messageHandlers.delete(subscriptionId); await this.closeSubscription(subscriptionId); }; return { response, close }; } closeSubscription(id) { const requestId = import_crypto17.utils.randomUuid(); const request = createJsonRpcSubscriptionRequest(requestId, "close", id, {}); return this.request(request); } /** * Sends a JSON-RPC request through the socket. You must subscribe to a message listener separately to capture the response. */ send(request) { this.socket.send(JSON.stringify(request)); } }; // src/prototyping/clients/web-socket-clients.ts var WebSocketDwnRpcClient = class _WebSocketDwnRpcClient { get transportProtocols() { return ["ws:", "wss:"]; } static { // a map of dwn host to WebSocket connection this.connections = /* @__PURE__ */ new Map(); } async sendDwnRequest(request, jsonRpcSocketOptions) { const url = new URL(request.dwnUrl); if (url.protocol !== "ws:" && url.protocol !== "wss:") { throw new Error(`Invalid websocket protocol ${url.protocol}`); } const hasConnection = _WebSocketDwnRpcClient.connections.has(url.host); if (!hasConnection) { try { const socket = await JsonRpcSocket.connect(url.toString(), jsonRpcSocketOptions); const subscriptions = /* @__PURE__ */ new Map(); _WebSocketDwnRpcClient.connections.set(url.host, { socket, subscriptions }); } catch (error) { throw new Error(`Error connecting to ${url.host}: ${error.message}`); } } const connection = _WebSocketDwnRpcClient.connections.get(url.host); const { targetDid, message, subscriptionHandler } = request; if (subscriptionHandler) { return _WebSocketDwnRpcClient.subscriptionRequest(connection, targetDid, message, subscriptionHandler); } return _WebSocketDwnRpcClient.processMessage(connection, targetDid, message); } static async processMessage(connection, target, message) { const requestId = import_crypto18.utils.randomUuid(); const request = createJsonRpcRequest(requestId, "dwn.processMessage", { target, message }); const { socket } = connection; const response = await socket.request(request); const { error, result } = response; if (error !== void 0) { throw new Error(`error sending DWN request: ${error.message}`); } return result.reply; } static async subscriptionRequest(connection, target, message, messageHandler) { const requestId = import_crypto18.utils.randomUuid(); const subscriptionId = import_crypto18.utils.randomUuid(); const request = createJsonRpcSubscriptionRequest(requestId, "dwn.processMessage", subscriptionId, { target, message }); const { socket, subscriptions } = connection; const { response, close } = await socket.subscribe(request, (response2) => { const { result: result2, error: error2 } = response2; if (error2) { const subscription = subscriptions.get(subscriptionId); if (subscription) { subscription.close(); } subscriptions.delete(subscriptionId); return; } const { event } = result2; messageHandler(event); }); const { error, result } = response; if (error) { throw new Error(`could not subscribe via jsonrpc socket: ${error.message}`); } const { reply } = result; if (reply.subscription && close) { subscriptions.set(subscriptionId, { ...reply.subscription, close }); reply.subscription.close = close; } return reply; } }; // src/rpc-client.ts var DidRpcMethod = /* @__PURE__ */ ((DidRpcMethod2) => { DidRpcMethod2["Create"] = "did.create"; DidRpcMethod2["Resolve"] = "did.resolve"; return DidRpcMethod2; })(DidRpcMethod || {}); var Web5RpcClient = class { constructor(clients = []) { this.transportClients = /* @__PURE__ */ new Map(); clients = [new HttpWeb5RpcClient(), new WebSocketWeb5RpcClient(), ...clients]; for (let client of clients) { for (let transportScheme of client.transportProtocols) { this.transportClients.set(transportScheme, client); } } } get transportProtocols() { return Array.from(this.transportClients.keys()); } async sendDidRequest(request) { const url = new URL(request.url); const transportClient = this.transportClients.get(url.protocol); if (!transportClient) { const error = new Error(`no ${url.protocol} transport client available`); error.name = "NO_TRANSPORT_CLIENT"; throw error; } return transportClient.sendDidRequest(request); } sendDwnRequest(request) { const url = new URL(request.dwnUrl); const transportClient = this.transportClients.get(url.protocol); if (!transportClient) { const error = new Error(`no ${url.protocol} transport client available`); error.name = "NO_TRANSPORT_CLIENT"; throw error; } return transportClient.sendDwnRequest(request); } async getServerInfo(dwnUrl) { const url = new URL(dwnUrl); const transportClient = this.transportClients.get(url.protocol); if (!transportClient) { const error = new Error(`no ${url.protocol} transport client available`); error.name = "NO_TRANSPORT_CLIENT"; throw error; } return transportClient.getServerInfo(dwnUrl); } }; var HttpWeb5RpcClient = class extends HttpDwnRpcClient { async sendDidRequest(request) { const requestId = import_crypto19.utils.randomUuid(); const jsonRpcRequest = createJsonRpcRequest(requestId, request.method, { data: request.data }); const httpRequest = new Request(request.url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(jsonRpcRequest) }); let jsonRpcResponse; try { const response = await fetch(httpRequest); if (response.ok) { jsonRpcResponse = await response.json(); if (jsonRpcResponse.error) { const { code, message } = jsonRpcResponse.error; throw new Error(`JSON RPC (${code}) - ${message}`); } } else { throw new Error(`HTTP (${response.status}) - ${response.statusText}`); } } catch (error) { throw new Error(`Error encountered while processing response from ${request.url}: ${error.message}`); } return jsonRpcResponse.result; } }; var WebSocketWeb5RpcClient = class extends WebSocketDwnRpcClient { async sendDidRequest(_request) { throw new Error(`not implemented for transports [${this.transportProtocols.join(", ")}]`); } async getServerInfo(_dwnUrl) { throw new Error(`not implemented for transports [${this.transportProtocols.join(", ")}]`); } }; // src/sync-api.ts var AgentSyncApi = class { constructor({ agent, syncEngine }) { this._syncEngine = syncEngine; this._agent = agent; } /** * Retrieves the `Web5PlatformAgent` execution context. * * @returns The `Web5PlatformAgent` instance that represents the current execution context. * @throws Will throw an error if the `agent` instance property is undefined. */ get agent() { if (this._agent === void 0) { throw new Error("AgentSyncApi: Unable to determine agent execution context."); } return this._agent; } set agent(agent) { this._agent = agent; this._syncEngine.agent = agent; } async registerIdentity(params) { await this._syncEngine.registerIdentity(params); } startSync(params) { return this._syncEngine.startSync(params); } stopSync() { this._syncEngine.stopSync(); } }; // src/sync-engine-level.ts var import_ms4 = __toESM(require("ms"), 1); var import_level = require("level"); var import_ulidx = require("ulidx"); var import_common13 = require("@web5/common"); var import_dwn_sdk_js5 = require("@tbd54566975/dwn-sdk-js"); var is2xx = (code) => code >= 200 && code <= 299; var is4xx = (code) => code >= 400 && code <= 499; var SyncEngineLevel = class { constructor({ agent, dataPath, db }) { this._agent = agent; this._db = db ? db : new import_level.Level(dataPath ?? "DATA/AGENT/SYNC_STORE"); this._ulidFactory = (0, import_ulidx.monotonicFactory)(); } /** * Retrieves the `Web5PlatformAgent` execution context. * * @returns The `Web5PlatformAgent` instance that represents the current execution context. * @throws Will throw an error if the `agent` instance property is undefined. */ get agent() { if (this._agent === void 0) { throw new Error("SyncEngineLevel: Unable to determine agent execution context."); } return this._agent; } set agent(agent) { this._agent = agent; } async clear() { await this._db.clear(); } async close() { await this._db.close(); } async pull() { const syncPeerState = await this.getSyncPeerState({ syncDirection: "pull" }); await this.enqueueOperations({ syncDirection: "pull", syncPeerState }); const pullQueue = this.getPullQueue(); const pullJobs = await pullQueue.iterator().all(); const deleteOperations = []; const errored = /* @__PURE__ */ new Set(); for (let job of pullJobs) { const [key] = job; const [did, dwnUrl, _, messageCid] = key.split("~"); if (errored.has(dwnUrl)) { continue; } const messageExists = await this.messageExists(did, messageCid); if (messageExists) { deleteOperations.push({ type: "del", key }); continue; } const messagesGet = await this.agent.dwn.createMessage({ author: did, messageType: DwnInterface.MessagesGet, messageParams: { messageCids: [messageCid] } }); let reply; try { reply = await this.agent.rpc.sendDwnRequest({ dwnUrl, targetDid: did, message: messagesGet }); } catch (e) { errored.add(dwnUrl); continue; } for (let entry of reply.entries ?? []) { if (entry.error || !entry.message) { await this.addMessage(did, messageCid); deleteOperations.push({ type: "del", key }); continue; } let dataStream; if (isRecordsWrite(entry)) { const { encodedData } = entry; const message = entry.message; if (encodedData) { const dataBytes = import_common13.Convert.base64Url(encodedData).toUint8Array(); dataStream = import_dwn_sdk_js5.DataStream.fromBytes(dataBytes); } else { const recordsRead = await this.agent.dwn.createMessage({ author: did, messageType: DwnInterface.RecordsRead, messageParams: { filter: { recordId: message.recordId } } }); const recordsReadReply = await this.agent.rpc.sendDwnRequest({ dwnUrl, targetDid: did, message: recordsRead.message }); const { record, status: readStatus } = recordsReadReply; if (is2xx(readStatus.code) && record) { dataStream = import_common13.NodeStream.fromWebReadable({ readableStream: record.data }); } else if (readStatus.code >= 400) { const pruneReply = await this.agent.dwn.processMessage({ targetDid: did, message }); if (pruneReply.status.code === 202 || pruneReply.status.code === 409) { await this.addMessage(did, messageCid); deleteOperations.push({ type: "del", key }); continue; } else { throw new Error(`SyncManager: Failed to sync tombstone for message '${messageCid}'`); } } } } const pullReply = await this.agent.dwn.processMessage({ targetDid: did, message: entry.message, dataStream }); if (pullReply.status.code === 202 || pullReply.status.code === 409) { await this.addMessage(did, messageCid); deleteOperations.push({ type: "del", key }); } } } await pullQueue.batch(deleteOperations); } async push() { const syncPeerState = await this.getSyncPeerState({ syncDirection: "push" }); await this.enqueueOperations({ syncDirection: "push", syncPeerState }); const pushQueue = this.getPushQueue(); const pushJobs = await pushQueue.iterator().all(); const deleteOperations = []; const errored = /* @__PURE__ */ new Set(); for (let job of pushJobs) { const [key] = job; const [did, dwnUrl, _, messageCid] = key.split("~"); if (errored.has(dwnUrl)) { continue; } const dwnMessage = await this.getDwnMessage({ author: did, messageCid }); if (!dwnMessage) { deleteOperations.push({ type: "del", key }); await this.addMessage(did, messageCid); continue; } try { const reply = await this.agent.rpc.sendDwnRequest({ dwnUrl, targetDid: did, data: dwnMessage.data, message: dwnMessage.message }); if (reply.status.code === 202 || reply.status.code === 409) { await this.addMessage(did, messageCid); deleteOperations.push({ type: "del", key }); } } catch { errored.add(dwnUrl); } } await pushQueue.batch(deleteOperations); } async registerIdentity({ did }) { const registeredIdentities = this._db.sublevel("registeredIdentities"); await registeredIdentities.put(did, ""); } startSync({ interval }) { const intervalMilliseconds = (0, import_ms4.default)(interval); return new Promise((resolve, reject) => { const intervalSync = async () => { if (this._syncIntervalId) { clearInterval(this._syncIntervalId); } try { await this.push(); await this.pull(); } catch (error) { this.stopSync(); reject(error); } this._syncIntervalId = setInterval(intervalSync, intervalMilliseconds); }; this._syncIntervalId = setInterval(intervalSync, intervalMilliseconds); }); } stopSync() { if (this._syncIntervalId) { clearInterval(this._syncIntervalId); this._syncIntervalId = void 0; } } async enqueueOperations({ syncDirection, syncPeerState }) { for (let syncState of syncPeerState) { const eventLog = await this.getDwnEventLog({ did: syncState.did, dwnUrl: syncState.dwnUrl, cursor: syncState.cursor, syncDirection }); const syncOperations = []; for (let messageCid of eventLog) { const watermark = this._ulidFactory(); const operationKey = [ syncState.did, syncState.dwnUrl, watermark, messageCid ].join("~"); syncOperations.push({ type: "put", key: operationKey, value: "" }); } if (syncOperations.length > 0) { const syncQueue = syncDirection === "pull" ? this.getPullQueue() : this.getPushQueue(); await syncQueue.batch(syncOperations); } } } async getDwnEventLog({ did, dwnUrl, syncDirection, cursor }) { let eventsReply = {}; if (syncDirection === "pull") { const eventsGetMessage = await this.agent.dwn.createMessage({ author: did, messageType: DwnInterface.EventsGet, messageParams: { cursor } }); try { eventsReply = await this.agent.rpc.sendDwnRequest({ dwnUrl, targetDid: did, message: eventsGetMessage }); } catch { } } else if (syncDirection === "push") { const eventsGetDwnResponse = await this.agent.dwn.processRequest({ author: did, target: did, messageType: DwnInterface.EventsGet, messageParams: { cursor } }); eventsReply = eventsGetDwnResponse.reply; } const eventLog = eventsReply.entries ?? []; if (eventsReply.cursor) { this.setCursor(did, dwnUrl, syncDirection, eventsReply.cursor); } return eventLog; } async getDwnMessage({ author, messageCid }) { let { reply } = await this.agent.dwn.processRequest({ author, target: author, messageType: DwnInterface.MessagesGet, messageParams: { messageCids: [messageCid] } }); if (!(reply.entries && reply.entries.length === 1)) { return void 0; } const [messageEntry] = reply.entries; const message = messageEntry.message; if (!message) { return void 0; } let dwnMessageWithBlob = { message }; if (isRecordsWrite(messageEntry)) { if (messageEntry.encodedData) { const dataBytes = import_common13.Convert.base64Url(messageEntry.encodedData).toUint8Array(); dwnMessageWithBlob.data = new Blob([dataBytes]); } else { let readResponse = await this.agent.dwn.processRequest({ author, target: author, messageType: DwnInterface.RecordsRead, messageParams: { filter: { recordId: messageEntry.message.recordId } } }); const reply2 = readResponse.reply; if (is2xx(reply2.status.code) && reply2.record) { dwnMessageWithBlob.data = await import_common13.NodeStream.consumeToBlob({ readable: reply2.record.data }); } else if (is4xx(reply2.status.code)) { } else { const { status: { code, detail } } = reply2; throw new Error(`SyncEngineLevel: (${code}) Failed to read data associated with record ${messageEntry.message.recordId}. ${detail}}`); } } } return dwnMessageWithBlob; } async getSyncPeerState({ syncDirection }) { const registeredIdentities = await this._db.sublevel("registeredIdentities").keys().all(); const syncPeerState = []; for (let did of registeredIdentities) { const dwnEndpointUrls = await getDwnServiceEndpointUrls(did, this.agent.did); if (dwnEndpointUrls.length === 0) { continue; } for (let dwnUrl of dwnEndpointUrls) { const cursor = await this.getCursor(did, dwnUrl, syncDirection); syncPeerState.push({ did, dwnUrl, cursor }); } } return syncPeerState; } async getCursor(did, dwnUrl, direction) { const cursorKey = `${did}~${dwnUrl}~${direction}`; const cursorsStore = this.getCursorStore(); try { const cursorValue = await cursorsStore.get(cursorKey); if (cursorValue) { return JSON.parse(cursorValue); } } catch (error) { if (error.notFound) { return void 0; } } } async setCursor(did, dwnUrl, direction, cursor) { const cursorKey = `${did}~${dwnUrl}~${direction}`; const cursorsStore = this.getCursorStore(); await cursorsStore.put(cursorKey, JSON.stringify(cursor)); } /** * The message store is used to prevent "echoes" that occur during a sync pull operation. * After a message is confirmed to already be synchronized on the local DWN, its CID is added * to the message store to ensure that any subsequent pull attempts are skipped. */ async messageExists(did, messageCid) { const messageStore = this.getMessageStore(did); try { await messageStore.get(messageCid); return true; } catch (error) { if (error.notFound) { return false; } throw error; } } async addMessage(did, messageCid) { const messageStore = this.getMessageStore(did); return await messageStore.put(messageCid, ""); } getMessageStore(did) { return this._db.sublevel("history").sublevel(did).sublevel("messages"); } getCursorStore() { return this._db.sublevel("cursors"); } getPushQueue() { return this._db.sublevel("pushQueue"); } getPullQueue() { return this._db.sublevel("pullQueue"); } }; // src/test-harness.ts var import_level2 = require("level"); var import_common14 = require("@web5/common"); var import_dwn_sdk_js6 = require("@tbd54566975/dwn-sdk-js"); var import_dids5 = require("@web5/dids"); var PlatformAgentTestHarness = class _PlatformAgentTestHarness { constructor(params) { this.agent = params.agent; this.agentStores = params.agentStores; this.didResolverCache = params.didResolverCache; this.dwn = params.dwn; this.dwnDataStore = params.dwnDataStore; this.dwnEventLog = params.dwnEventLog; this.dwnMessageStore = params.dwnMessageStore; this.syncStore = params.syncStore; this.vaultStore = params.vaultStore; } async clearStorage() { this.agent.agentDid = void 0; await this.didResolverCache.clear(); await this.dwnDataStore.clear(); await this.dwnEventLog.clear(); await this.dwnMessageStore.clear(); await this.syncStore.clear(); await this.vaultStore.clear(); if (this.agentStores === "memory") { const { didApi, identityApi, keyManager } = _PlatformAgentTestHarness.useMemoryStores({ agent: this.agent }); this.agent.did = didApi; this.agent.identity = identityApi; this.agent.keyManager = keyManager; } } async closeStorage() { await this.didResolverCache.close(); await this.dwnDataStore.close(); await this.dwnEventLog.close(); await this.dwnMessageStore.close(); await this.syncStore.close(); await this.vaultStore.close(); } async createAgentDid() { this.agent.agentDid = await import_dids5.DidJwk.create({ options: { algorithm: "Ed25519" } }); } async createIdentity({ name, testDwnUrls }) { const bearerIdentity = await this.agent.identity.create({ didMethod: "dht", didOptions: { services: [ { id: "dwn", type: "DecentralizedWebNode", serviceEndpoint: testDwnUrls, enc: "#enc", sig: "#sig" } ], verificationMethods: [ { algorithm: "Ed25519", id: "sig", purposes: ["assertionMethod", "authentication"] }, { algorithm: "secp256k1", id: "enc", purposes: ["keyAgreement"] } ] }, metadata: { name } }); return bearerIdentity; } async preloadResolverCache({ didUri, resolutionResult }) { await this.didResolverCache.set(didUri, resolutionResult); } static async setup({ agentClass, agentStores, testDataLocation }) { agentStores ??= "memory"; testDataLocation ??= "__TESTDATA__"; const testDataPath = (path) => `${testDataLocation}/${path}`; const cryptoApi = new AgentCryptoApi(); const rpcClient = new Web5RpcClient(); const { agentVault, didApi, identityApi, keyManager, didResolverCache, vaultStore } = agentStores === "memory" ? _PlatformAgentTestHarness.useMemoryStores() : _PlatformAgentTestHarness.useDiskStores({ testDataLocation }); const dwnDataStore = new import_dwn_sdk_js6.DataStoreLevel({ blockstoreLocation: testDataPath("DWN_DATASTORE") }); const dwnEventLog = new import_dwn_sdk_js6.EventLogLevel({ location: testDataPath("DWN_EVENTLOG") }); const dwnEventStream = new import_dwn_sdk_js6.EventEmitterStream(); const dwnMessageStore = new import_dwn_sdk_js6.MessageStoreLevel({ blockstoreLocation: testDataPath("DWN_MESSAGESTORE"), indexLocation: testDataPath("DWN_MESSAGEINDEX") }); const dwn = await AgentDwnApi.createDwn({ dataPath: testDataLocation, dataStore: dwnDataStore, didResolver: didApi, eventLog: dwnEventLog, eventStream: dwnEventStream, messageStore: dwnMessageStore }); const dwnApi = new AgentDwnApi({ dwn }); const syncStore = new import_level2.Level(testDataPath("SYNC_STORE")); const syncEngine = new SyncEngineLevel({ db: syncStore }); const syncApi = new AgentSyncApi({ syncEngine }); const agent = new agentClass({ agentVault, cryptoApi, didApi, dwnApi, identityApi, keyManager, rpcClient, syncApi }); return new _PlatformAgentTestHarness({ agent, agentStores, didResolverCache, dwn, dwnDataStore, dwnEventLog, dwnMessageStore, syncStore, vaultStore }); } static useDiskStores({ agent, testDataLocation }) { const testDataPath = (path) => `${testDataLocation}/${path}`; const vaultStore = new import_common14.LevelStore({ location: testDataPath("VAULT_STORE") }); const agentVault = new HdIdentityVault({ keyDerivationWorkFactor: 1, store: vaultStore }); const didResolverCache = new import_dids5.DidResolverCacheLevel({ location: testDataPath("DID_RESOLVERCACHE") }); const didApi = new AgentDidApi({ agent, didMethods: [import_dids5.DidDht, import_dids5.DidJwk], resolverCache: didResolverCache, store: new DwnDidStore() }); const identityApi = new AgentIdentityApi({ agent, store: new DwnIdentityStore() }); const keyManager = new LocalKeyManager2({ agent, keyStore: new DwnKeyStore() }); return { agentVault, didApi, didResolverCache, identityApi, keyManager, vaultStore }; } static useMemoryStores({ agent } = {}) { const vaultStore = new import_common14.MemoryStore(); const agentVault = new HdIdentityVault({ keyDerivationWorkFactor: 1, store: vaultStore }); const didResolverCache = new DidResolverCacheMemory(); const didApi = new AgentDidApi({ agent, didMethods: [import_dids5.DidDht, import_dids5.DidJwk], resolverCache: didResolverCache, store: new InMemoryDidStore() }); const keyManager = new LocalKeyManager2({ agent, keyStore: new InMemoryKeyStore() }); const identityApi = new AgentIdentityApi({ agent, store: new InMemoryIdentityStore() }); return { agentVault, didApi, didResolverCache, identityApi, keyManager, vaultStore }; } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { AgentCryptoApi, AgentDidApi, AgentDwnApi, AgentIdentityApi, AgentSyncApi, BearerIdentity, DidInterface, DidRpcMethod, DwnConstant, DwnDataStore, DwnDateSort, DwnDidStore, DwnEncryptionAlgorithm, DwnEventSubscriptionHandler, DwnIdentityStore, DwnInterface, DwnKeyDerivationScheme, DwnKeyStore, DwnMessageSubscription, DwnPaginationCursor, DwnPublicKeyJwk, DwnRecordSubscriptionHandler, DwnSigner, HdIdentityVault, HttpWeb5RpcClient, InMemoryDataStore, InMemoryDidStore, InMemoryIdentityStore, InMemoryKeyStore, LocalKeyManager, PlatformAgentTestHarness, SyncEngineLevel, Web5RpcClient, WebSocketWeb5RpcClient, blobToIsomorphicNodeReadable, dwnMessageConstructors, getDwnServiceEndpointUrls, getPaginationCursor, getRecordAuthor, getRecordMessageCid, isDidRequest, isDwnMessage, isDwnRequest, isIdentityMetadata, isPortableIdentity, isRecordsWrite, webReadableToIsomorphicNodeReadable }); /*! Bundled license information: @noble/hashes/esm/utils.js: (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *) @noble/curves/esm/abstract/utils.js: (*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) *) @noble/curves/esm/abstract/modular.js: (*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) *) @noble/curves/esm/abstract/curve.js: (*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) *) @noble/curves/esm/abstract/edwards.js: (*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) *) @noble/curves/esm/ed25519.js: (*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) *) ed25519-keygen/hdkey.js: (*! micro-ed25519-hdkey - MIT License (c) 2022 Paul Miller (paulmillr.com) *) */ //# sourceMappingURL=index.js.map