Dorian 0d073fa89e Add comprehensive installation and setup documentation
- Add GETTING_STARTED.md with quick start guide and development modes
- Add INSTALL.sh automated installation script
- Add INSTALLATION_CHECKLIST.md, INSTALLATION_SUCCESS.md, and INSTALLATION_SUMMARY.md
- Add QUICK_REFERENCE.md for common commands
- Add SETUP_GUIDE.md with detailed setup instructions
- Update README.md with improved project overview
- Add did-wallet app dependencies and node_modules
2026-01-27 17:18:21 +00:00

6937 lines
249 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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