@web5/agent
Version:
1,250 lines (1,230 loc) • 315 kB
JavaScript
"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;
}
/**