6937 lines
249 KiB
JavaScript
Raw Normal View History

"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