UNPKG

@web5/agent

Version:
1,250 lines (1,230 loc) 315 kB
"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, AgentDidResolverCache: () => AgentDidResolverCache, AgentDwnApi: () => AgentDwnApi, AgentIdentityApi: () => AgentIdentityApi, AgentPermissionsApi: () => AgentPermissionsApi, AgentSyncApi: () => AgentSyncApi, BearerIdentity: () => BearerIdentity, DidInterface: () => DidInterface, DidRpcMethod: () => DidRpcMethod, DwnConstant: () => import_dwn_sdk_js2.DwnConstant, DwnDataEncodedRecordsWriteMessage: () => import_dwn_sdk_js2.DataEncodedRecordsWriteMessage, DwnDataStore: () => DwnDataStore, DwnDateSort: () => import_dwn_sdk_js2.DateSort, DwnDidStore: () => DwnDidStore, DwnEncryptionAlgorithm: () => import_dwn_sdk_js2.EncryptionAlgorithm, DwnIdentityStore: () => DwnIdentityStore, DwnInterface: () => DwnInterface, DwnKeyDerivationScheme: () => import_dwn_sdk_js2.KeyDerivationScheme, DwnKeyStore: () => DwnKeyStore, DwnMessageSubscription: () => import_dwn_sdk_js2.MessageSubscription, DwnMessageSubscriptionHandler: () => import_dwn_sdk_js2.MessageSubscriptionHandler, DwnMessagesPermissionScope: () => import_dwn_sdk_js2.MessagesPermissionScope, DwnPaginationCursor: () => import_dwn_sdk_js2.PaginationCursor, DwnPermissionConditions: () => import_dwn_sdk_js2.PermissionConditions, DwnPermissionGrant: () => import_dwn_sdk_js2.PermissionGrant, DwnPermissionGrantData: () => import_dwn_sdk_js2.PermissionGrantData, DwnPermissionRequest: () => import_dwn_sdk_js2.PermissionRequest, DwnPermissionRequestData: () => import_dwn_sdk_js2.PermissionRequestData, DwnPermissionScope: () => import_dwn_sdk_js2.PermissionScope, DwnPermissionsProtocol: () => import_dwn_sdk_js2.PermissionsProtocol, DwnProtocolDefinition: () => import_dwn_sdk_js2.ProtocolDefinition, DwnProtocolPermissionScope: () => import_dwn_sdk_js2.ProtocolPermissionScope, DwnPublicKeyJwk: () => import_dwn_sdk_js2.PublicJwk, DwnRecordSubscriptionHandler: () => import_dwn_sdk_js2.RecordSubscriptionHandler, DwnRecordsPermissionScope: () => import_dwn_sdk_js2.RecordsPermissionScope, DwnRegistrar: () => DwnRegistrar, DwnSigner: () => import_dwn_sdk_js2.Signer, HdIdentityVault: () => HdIdentityVault, HttpWeb5RpcClient: () => HttpWeb5RpcClient, InMemoryDataStore: () => InMemoryDataStore, InMemoryDidStore: () => InMemoryDidStore, InMemoryIdentityStore: () => InMemoryIdentityStore, InMemoryKeyStore: () => InMemoryKeyStore, LocalKeyManager: () => LocalKeyManager2, Oidc: () => Oidc, PlatformAgentTestHarness: () => PlatformAgentTestHarness, SyncEngineLevel: () => SyncEngineLevel, WalletConnect: () => WalletConnect, Web5RpcClient: () => Web5RpcClient, WebSocketWeb5RpcClient: () => WebSocketWeb5RpcClient, blobToIsomorphicNodeReadable: () => blobToIsomorphicNodeReadable, concatenateUrl: () => concatenateUrl, dwnMessageConstructors: () => dwnMessageConstructors, getDwnServiceEndpointUrls: () => getDwnServiceEndpointUrls, getPaginationCursor: () => getPaginationCursor, getRecordAuthor: () => getRecordAuthor, getRecordMessageCid: () => getRecordMessageCid, getRecordProtocolRole: () => getRecordProtocolRole, isDidRequest: () => isDidRequest, isDwnMessage: () => isDwnMessage, isDwnRequest: () => isDwnRequest, isIdentityMetadata: () => isIdentityMetadata, isMessagesPermissionScope: () => isMessagesPermissionScope, isPortableIdentity: () => isPortableIdentity, isRecordPermissionScope: () => isRecordPermissionScope, isRecordsType: () => isRecordsType, isRecordsWrite: () => isRecordsWrite, pollWithTtl: () => pollWithTtl, 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["MessagesQuery"] = import_dwn_sdk_js.DwnInterfaceName.Messages + import_dwn_sdk_js.DwnMethodName.Query] = "MessagesQuery"; DwnInterface2[DwnInterface2["MessagesRead"] = import_dwn_sdk_js.DwnInterfaceName.Messages + import_dwn_sdk_js.DwnMethodName.Read] = "MessagesRead"; DwnInterface2[DwnInterface2["MessagesSubscribe"] = import_dwn_sdk_js.DwnInterfaceName.Messages + import_dwn_sdk_js.DwnMethodName.Subscribe] = "MessagesSubscribe"; 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.MessagesQuery]: import_dwn_sdk_js.MessagesQuery, [DwnInterface.MessagesRead]: import_dwn_sdk_js.MessagesRead, [DwnInterface.MessagesSubscribe]: import_dwn_sdk_js.MessagesSubscribe, [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/agent-did-resolver-cache.ts var import_dids = require("@web5/dids"); var import_common = require("@web5/common"); var AgentDidResolverCache = class extends import_dids.DidResolverCacheLevel { constructor({ agent, db, location, ttl }) { super({ db, location, ttl }); /** A map of DIDs that are currently in-flight. This helps avoid going into an infinite loop */ this._resolving = /* @__PURE__ */ new Map(); this._agent = agent; } get agent() { if (!this._agent) { throw new Error("Agent not initialized"); } return this._agent; } set agent(agent) { this._agent = agent; } /** * Get the DID resolution result from the cache for the given DID. * * If the DID is managed by the agent, or is the agent's own DID, it will not evict it from the cache until a new resolution is successful. * This is done to achieve quick and offline access to the agent's own managed DIDs. */ async get(did) { try { const str = await this.cache.get(did); const cachedResult = JSON.parse(str); if (!this._resolving.has(did) && Date.now() >= cachedResult.ttlMillis) { this._resolving.set(did, true); const storedDid = await this.agent.did.get({ didUri: did, tenant: this.agent.agentDid.uri }); if ("undefined" !== typeof storedDid) { try { const result = await this.agent.did.resolve(did); if (!result.didResolutionMetadata.error && result.didDocument) { const portableDid = { ...storedDid, document: result.didDocument, metadata: result.didDocumentMetadata }; try { await this.agent.did.update({ portableDid, tenant: this.agent.agentDid.uri, publish: false }); } catch (error) { if (error.message && !error.message.includes("No changes detected, update aborted")) { import_common.logger.error(`Error updating DID: ${error.message}`); } } } } finally { this._resolving.delete(did); } } else { this._resolving.delete(did); this.cache.nextTick(() => this.cache.del(did)); } } return cachedResult.value; } catch (error) { if (error.notFound) { return; } throw error; } } }; // 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_webcrypto = require("@noble/ciphers/webcrypto"); var import_common2 = 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_webcrypto.getWebcryptoSubtle)(); const webCryptoKey = await webCrypto.importKey("raw", baseKeyBytes, { name: "HKDF" }, false, ["deriveBits"]); salt = typeof salt === "string" ? import_common2.Convert.string(salt).toUint8Array() : salt; info = typeof info === "string" ? import_common2.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_webcrypto2 = require("@noble/ciphers/webcrypto"); var import_common3 = 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_common3.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_webcrypto2.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_common3.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_webcrypto2.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_webcrypto2.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_webcrypto3 = require("@noble/ciphers/webcrypto"); 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_webcrypto3.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; } /**