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
This commit is contained in:
Dorian
2026-01-27 17:18:21 +00:00
parent a81f655133
commit 0d073fa89e
22658 changed files with 4494151 additions and 6 deletions
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
{"type": "commonjs"}
+41
View File
@@ -0,0 +1,41 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
/**
* Represents a Web5 Identity with its DID and metadata.
*/
export class BearerIdentity {
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`.
*/
export() {
return __awaiter(this, void 0, void 0, function* () {
return {
portableDid: yield this.did.export(),
metadata: this.metadata
};
});
}
}
//# sourceMappingURL=bearer-identity.js.map
@@ -0,0 +1 @@
{"version":3,"file":"bearer-identity.js","sourceRoot":"","sources":["../../src/bearer-identity.ts"],"names":[],"mappings":";;;;;;;;;AAGA;;GAEG;AACH,MAAM,OAAO,cAAc;IAOzB,YAAY,EAAE,GAAG,EAAE,QAAQ,EAG1B;QACC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED;;;;;;;;;;;;;OAaG;IACU,MAAM;;YACjB,OAAO;gBACL,WAAW,EAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE;gBACrC,QAAQ,EAAM,IAAI,CAAC,QAAQ;aAC5B,CAAC;QACJ,CAAC;KAAA;CACF"}
+346
View File
@@ -0,0 +1,346 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Sha2Algorithm, computeJwkThumbprint } from '@web5/crypto';
import { HkdfAlgorithm } from './prototyping/crypto/algorithms/hkdf.js';
import { EcdsaAlgorithm } from './prototyping/crypto/algorithms/ecdsa.js';
import { EdDsaAlgorithm } from './prototyping/crypto/algorithms/eddsa.js';
import { AesKwAlgorithm } from './prototyping/crypto/algorithms/aes-kw.js';
import { Pbkdf2Algorithm } from './prototyping/crypto/algorithms/pbkdf2.js';
import { AesGcmAlgorithm } from './prototyping/crypto/algorithms/aes-gcm.js';
import { CryptoError, CryptoErrorCode } from './prototyping/crypto/crypto-error.js';
/**
* `supportedAlgorithms` is an object mapping algorithm names to their respective implementations
* Each entry in this map specifies the algorithm name and its associated properties, including the
* implementation class and any relevant names or identifiers for the algorithm. This structure
* allows for easy retrieval and instantiation of algorithm implementations based on the algorithm
* name or key specification. It facilitates the support of multiple algorithms within the
* `LocalKeyManager` class.
*/
const 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: Sha2Algorithm,
names: ['SHA-256'],
operations: ['digest'],
}
};
export class AgentCryptoApi {
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 = new Map();
}
bytesToPrivateKey({ algorithm: algorithmIdentifier, privateKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the given algorithm identifier.
const algorithm = this.getAlgorithmName({ algorithm: algorithmIdentifier });
// Get the key converter based on the algorithm name.
const keyConverter = this.getAlgorithm({ algorithm });
// Convert the byte array to a JWK.
const privateKey = yield keyConverter.bytesToPrivateKey({ algorithm: algorithmIdentifier, privateKeyBytes });
return privateKey;
});
}
bytesToPublicKey({ algorithm: algorithmIdentifier, publicKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the given algorithm identifier.
const algorithm = this.getAlgorithmName({ algorithm: algorithmIdentifier });
// Get the key converter based on the algorithm name.
const keyConverter = this.getAlgorithm({ algorithm });
// Convert the byte array to a JWK.
const publicKey = yield keyConverter.bytesToPublicKey({ algorithm: algorithmIdentifier, publicKeyBytes });
return publicKey;
});
}
decrypt(params) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the JWK's `alg` property.
const algorithm = this.getAlgorithmName({ key: params.key });
// Get the cipher algorithm based on the algorithm name.
const cipher = this.getAlgorithm({ algorithm });
// Decrypt the data.
return yield cipher.decrypt(params);
});
}
deriveKey(params) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the given algorithm identifier.
const algorithm = this.getAlgorithmName({ algorithm: params.algorithm });
// Get the key derivation function based on the algorithm name.
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(CryptoErrorCode.AlgorithmNotSupported, `The specified "algorithm" is not supported: ${params.algorithm}`);
}
// Determine the bit length of the derived key based on the given algorithm.
const length = +((_b = (_a = derivedKeyAlgorithm.match(/\d+/)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : -1);
if (length === -1) {
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `The derived key algorithm" is not supported: ${derivedKeyAlgorithm}`);
}
// Derive the byte array.
const privateKeyBytes = yield kdf.deriveKeyBytes(Object.assign(Object.assign({}, params), { length }));
return yield this.bytesToPrivateKey({ algorithm: derivedKeyAlgorithm, privateKeyBytes });
});
}
deriveKeyBytes(params) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the given algorithm identifier.
const algorithm = this.getAlgorithmName({ algorithm: params.algorithm });
// Get the key derivation function based on the algorithm name.
const kdf = this.getAlgorithm({ algorithm });
// Derive the byte array.
const derivedKeyBytes = yield 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.
*/
digest({ algorithm, data }) {
return __awaiter(this, void 0, void 0, function* () {
// Get the hash function implementation based on the specified `algorithm` parameter.
const hasher = this.getAlgorithm({ algorithm });
// Compute the hash.
const hash = yield hasher.digest({ algorithm, data });
return hash;
});
}
encrypt(params) {
return __awaiter(this, void 0, void 0, function* () {
// If th
// Determine the algorithm name based on the JWK's `alg` property.
const algorithm = this.getAlgorithmName({ key: params.key });
// Get the cipher algorithm based on the algorithm name.
const cipher = this.getAlgorithm({ algorithm });
// Encrypt the data and return the ciphertext.
return yield cipher.encrypt(params);
});
}
generateKey(params) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the given algorithm identifier.
const algorithm = this.getAlgorithmName({ algorithm: params.algorithm });
// Get the key generator implementation based on the algorithm.
const keyGenerator = this.getAlgorithm({ algorithm });
// Generate the key.
const privateKey = yield keyGenerator.generateKey({ algorithm: params.algorithm });
// If the key ID is undefined, set it to the JWK thumbprint.
(_a = privateKey.kid) !== null && _a !== void 0 ? _a : (privateKey.kid = yield computeJwkThumbprint({ jwk: privateKey }));
return privateKey;
});
}
// ! TODO: Remove this once the `Dsa` interface is updated in @web5/crypto to remove KMS-specific methods.
getKeyUri(_params) {
return __awaiter(this, void 0, void 0, function* () {
throw new Error('Method not implemented.');
});
}
getPublicKey({ key }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the JWK's `alg` and `crv` properties.
const algorithm = this.getAlgorithmName({ key });
// Get the key generator based on the algorithm name.
const keyGenerator = this.getAlgorithm({ algorithm });
// Get the public key properties from the private JWK.
const publicKey = yield keyGenerator.getPublicKey({ key });
return publicKey;
});
}
privateKeyToBytes({ privateKey }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the JWK's `alg` property.
const algorithm = this.getAlgorithmName({ key: privateKey });
// Get the key converter based on the algorithm name.
const keyConverter = this.getAlgorithm({ algorithm });
// Convert the JWK to a byte array.
const privateKeyBytes = yield keyConverter.privateKeyToBytes({ privateKey });
return privateKeyBytes;
});
}
publicKeyToBytes({ publicKey }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the JWK's `alg` property.
const algorithm = this.getAlgorithmName({ key: publicKey });
// Get the key converter based on the algorithm name.
const keyConverter = this.getAlgorithm({ algorithm });
// Convert the JWK to a byte array.
const publicKeyBytes = yield keyConverter.publicKeyToBytes({ publicKey });
return publicKeyBytes;
});
}
sign({ key, data }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the JWK's `alg` and `crv` properties.
const algorithm = this.getAlgorithmName({ key });
// Get the signature algorithm based on the algorithm name.
const signer = this.getAlgorithm({ algorithm });
// Sign the data.
const signature = signer.sign({ data, key });
return signature;
});
}
unwrapKey(params) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the JWK's `alg` property.
const algorithm = this.getAlgorithmName({ key: params.decryptionKey });
// Get the key wrapping algorithm based on the algorithm name.
const keyWrapper = this.getAlgorithm({ algorithm });
// decrypt the key and return the ciphertext.
return yield keyWrapper.unwrapKey(params);
});
}
verify({ key, signature, data }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the JWK's `alg` and `crv` properties.
const algorithm = this.getAlgorithmName({ key });
// Get the signature algorithm based on the algorithm name.
const signer = this.getAlgorithm({ algorithm });
// Verify the signature.
const isSignatureValid = signer.verify({ key, signature, data });
return isSignatureValid;
});
}
wrapKey(params) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the JWK's `alg` property.
const algorithm = this.getAlgorithmName({ key: params.encryptionKey });
// Get the key wrapping algorithm based on the algorithm name.
const keyWrapper = this.getAlgorithm({ algorithm });
// Encrypt the key and return the ciphertext.
return yield 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 }) {
var _a;
// Check if algorithm is supported.
const AlgorithmImplementation = (_a = supportedAlgorithms[algorithm]) === null || _a === void 0 ? void 0 : _a['implementation'];
if (!AlgorithmImplementation) {
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `Algorithm not supported: ${algorithm}`);
}
// Check if instance already exists for the `AlgorithmImplementation`.
if (!this._algorithmInstances.has(AlgorithmImplementation)) {
// If not, create a new instance and store it in the cache
this._algorithmInstances.set(AlgorithmImplementation, new AlgorithmImplementation());
}
// Return the cached instance
return this._algorithmInstances.get(AlgorithmImplementation);
}
getAlgorithmName({ algorithm, key }) {
var _a;
const algProperty = (_a = key === null || key === void 0 ? void 0 : key.alg) !== null && _a !== void 0 ? _a : algorithm;
const crvProperty = key === null || key === void 0 ? void 0 : 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(CryptoErrorCode.AlgorithmNotSupported, `Algorithm not supported based on provided input: alg=${algProperty}, crv=${crvProperty}. ` +
'Please check the documentation for the list of supported algorithms.');
}
}
//# sourceMappingURL=crypto-api.js.map
File diff suppressed because one or more lines are too long
+193
View File
@@ -0,0 +1,193 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { BearerDid, Did, UniversalResolver } from '@web5/dids';
import { InMemoryDidStore } from './store-did.js';
import { DidResolverCacheMemory } from './prototyping/dids/resolver-cache-memory.js';
export var DidInterface;
(function (DidInterface) {
DidInterface["Create"] = "Create";
// Deactivate = 'Deactivate',
DidInterface["Resolve"] = "Resolve";
// Update = 'Update'
})(DidInterface || (DidInterface = {}));
export function isDidRequest(didRequest, messageType) {
return didRequest.messageType === messageType;
}
export class AgentDidApi extends UniversalResolver {
constructor({ agent, didMethods, resolverCache, store }) {
if (!didMethods) {
throw new TypeError(`AgentDidApi: Required parameter missing: 'didMethods'`);
}
// Initialize the DID resolver with the given DID methods and resolver cache, or use a default
// in-memory cache if none is provided.
super({
didResolvers: didMethods,
cache: resolverCache !== null && resolverCache !== void 0 ? resolverCache : new DidResolverCacheMemory()
});
this._didMethods = new Map();
this._agent = agent;
// If `store` is not given, use an in-memory store by default.
this._store = store !== null && store !== void 0 ? 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 === undefined) {
throw new Error('AgentDidApi: Unable to determine agent execution context.');
}
return this._agent;
}
set agent(agent) {
this._agent = agent;
}
create({ method, tenant, options, store }) {
return __awaiter(this, void 0, void 0, function* () {
// Get the DID method implementation, which also verifies the method is supported.
const didMethod = this.getMethod(method);
// Create the DID and store the generated keys in the Agent's key manager.
const bearerDid = yield didMethod.create({ keyManager: this.agent.keyManager, options });
// Persist the DID to the store, by default, unless the `store` option is set to false.
if (store !== null && store !== void 0 ? store : true) {
// Data stored in the Agent's DID store must be in PortableDid format.
const { uri, document, metadata } = bearerDid;
const portableDid = { uri, document, metadata };
// Unless an existing `tenant` is specified, a record that includes the DID's URI, document,
// and metadata will be stored under a new tenant controlled by the newly created DID.
yield this._store.set({
id: portableDid.uri,
data: portableDid,
agent: this.agent,
tenant: tenant !== null && tenant !== void 0 ? tenant : portableDid.uri,
preventDuplicates: false,
useCache: true
});
}
return bearerDid;
});
}
export({ didUri, tenant }) {
return __awaiter(this, void 0, void 0, function* () {
// Attempt to retrieve the DID from the agent's DID store.
const bearerDid = yield this.get({ didUri, tenant });
if (!bearerDid) {
throw new Error(`AgentDidApi: Failed to export due to DID not found: ${didUri}`);
}
// If the DID was found, return the DID in a portable format, and if supported by the Agent's
// key manager, the private key material.
const portableDid = yield bearerDid.export();
return portableDid;
});
}
get({ didUri, tenant }) {
return __awaiter(this, void 0, void 0, function* () {
const portableDid = yield this._store.get({ id: didUri, agent: this.agent, tenant, useCache: true });
if (!portableDid)
return undefined;
const bearerDid = yield BearerDid.import({ portableDid, keyManager: this.agent.keyManager });
return bearerDid;
});
}
getSigningMethod({ didUri, methodId }) {
return __awaiter(this, void 0, void 0, function* () {
// Verify the DID method is supported.
const parsedDid = Did.parse(didUri);
if (!parsedDid) {
throw new Error(`Invalid DID URI: ${didUri}`);
}
// Get the DID method implementation, which also verifies the method is supported.
const didMethod = this.getMethod(parsedDid.method);
// Resolve the DID document.
const { didDocument, didResolutionMetadata } = yield this.resolve(didUri);
if (!didDocument) {
throw new Error(`DID resolution failed for '${didUri}': ${JSON.stringify(didResolutionMetadata)}`);
}
// Retrieve the method-specific verification method to be used for signing operations.
const verificationMethod = yield didMethod.getSigningMethod({ didDocument, methodId });
return verificationMethod;
});
}
import({ portableDid, tenant }) {
return __awaiter(this, void 0, void 0, function* () {
// If private keys are present in the PortableDid, import the key material into the Agent's key
// manager. Validate that the key material for every verification method in the DID document is
// present in the key manager.
const bearerDid = yield BearerDid.import({ keyManager: this.agent.keyManager, portableDid });
// Only the DID URI, document, and metadata are stored in the Agent's DID store.
const { uri, document, metadata } = bearerDid;
const portableDidWithoutKeys = { uri, document, metadata };
// Store the DID in the agent's DID store.
// Unless an existing `tenant` is specified, a record that includes the DID's URI, document,
// and metadata will be stored under a new tenant controlled by the imported DID.
yield this._store.set({
id: portableDidWithoutKeys.uri,
data: portableDidWithoutKeys,
agent: this.agent,
tenant: tenant !== null && tenant !== void 0 ? tenant : portableDidWithoutKeys.uri,
preventDuplicates: true,
useCache: true
});
return bearerDid;
});
}
processRequest(request) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
// Process Create DID request.
if (isDidRequest(request, DidInterface.Create)) {
try {
const bearerDid = yield this.create(Object.assign({}, 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: (_a = error.message) !== null && _a !== void 0 ? _a : 'Unknown error occurred' }
};
}
}
// Process Resolve DID request.
if (isDidRequest(request, DidInterface.Resolve)) {
const { didUri, options } = request.messageParams;
const resolutionResult = yield 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 === undefined) {
throw new Error(`DID Method not supported: ${methodName}`);
}
return didMethodApi;
}
}
//# sourceMappingURL=did-api.js.map
File diff suppressed because one or more lines are too long
+337
View File
@@ -0,0 +1,337 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Convert, NodeStream } from '@web5/common';
import { utils as cryptoUtils } from '@web5/crypto';
import { DidDht, DidJwk, DidResolverCacheLevel, UniversalResolver } from '@web5/dids';
import { Cid, DataStoreLevel, Dwn, DwnMethodName, EventLogLevel, Message, MessageStoreLevel } from '@tbd54566975/dwn-sdk-js';
import { DwnInterface, dwnMessageConstructors } from './types/dwn.js';
import { blobToIsomorphicNodeReadable, getDwnServiceEndpointUrls, isRecordsWrite, webReadableToIsomorphicNodeReadable } from './utils.js';
export function isDwnRequest(dwnRequest, messageType) {
return dwnRequest.messageType === messageType;
}
export function isDwnMessage(messageType, message) {
const incomingMessageInterfaceName = message.descriptor.interface + message.descriptor.method;
return incomingMessageInterfaceName === messageType;
}
export class AgentDwnApi {
constructor({ agent, dwn }) {
// If an agent is provided, set it as the execution context for this API.
this._agent = agent;
// Set the DWN instance for this API.
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 === undefined) {
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 createDwn({ dataPath, dataStore, didResolver, eventLog, eventStream, messageStore, tenantGate }) {
return __awaiter(this, void 0, void 0, function* () {
dataStore !== null && dataStore !== void 0 ? dataStore : (dataStore = new DataStoreLevel({ blockstoreLocation: `${dataPath}/DWN_DATASTORE` }));
didResolver !== null && didResolver !== void 0 ? didResolver : (didResolver = new UniversalResolver({
didResolvers: [DidDht, DidJwk],
cache: new DidResolverCacheLevel({ location: `${dataPath}/DID_RESOLVERCACHE` }),
}));
eventLog !== null && eventLog !== void 0 ? eventLog : (eventLog = new EventLogLevel({ location: `${dataPath}/DWN_EVENTLOG` }));
messageStore !== null && messageStore !== void 0 ? messageStore : (messageStore = new MessageStoreLevel(({
blockstoreLocation: `${dataPath}/DWN_MESSAGESTORE`,
indexLocation: `${dataPath}/DWN_MESSAGEINDEX`
})));
return yield Dwn.create({ dataStore, didResolver, eventLog, eventStream, messageStore, tenantGate });
});
}
processRequest(request) {
return __awaiter(this, void 0, void 0, function* () {
// Constructs a DWN message. and if there is a data payload, transforms the data to a Node
// Readable stream.
const { message, dataStream } = yield this.constructDwnMessage({ request });
// Extracts the optional subscription handler from the request to pass into `processMessage.
const { subscriptionHandler } = request;
// Conditionally processes the message with the DWN instance:
// - If `store` is not explicitly set to false, it sends the message to the DWN node for
// processing, passing along the target DID, the message, and any associated data stream.
// - If `store` is set to false, it immediately returns a simulated 'accepted' status without
// storing the message/data in the DWN node.
const reply = (request.store !== false)
? yield this._dwn.processMessage(request.target, message, { dataStream, subscriptionHandler })
: { status: { code: 202, detail: 'Accepted' } };
// Returns an object containing the reply from processing the message, the original message,
// and the content identifier (CID) of the message.
return {
reply,
message,
messageCid: yield Message.getCid(message),
};
});
}
sendRequest(request) {
return __awaiter(this, void 0, void 0, function* () {
// First, confirm the target DID can be dereferenced and extract the DWN service endpoint URLs.
const dwnEndpointUrls = yield 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` is given, retrieve message and data, if any.
if ('messageCid' in request) {
({ message, data } = yield this.getDwnMessage({
author: request.author,
messageCid: request.messageCid,
messageType: request.messageType
}));
messageCid = request.messageCid;
}
else {
// Otherwise, construct a new message.
({ message } = yield 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;
}
// Send the RPC request to the target DID's DWN service endpoint using the Agent's RPC client.
const reply = yield this.sendDwnRpcRequest({
targetDid: request.target,
dwnEndpointUrls,
message,
data,
subscriptionHandler
});
// If the message CID was not given in the `request`, compute it.
messageCid !== null && messageCid !== void 0 ? messageCid : (messageCid = yield Message.getCid(message));
// Returns an object containing the reply from processing the message, the original message,
// and the content identifier (CID) of the message.
return { reply, message, messageCid };
});
}
sendDwnRpcRequest({ targetDid, dwnEndpointUrls, message, data, subscriptionHandler }) {
return __awaiter(this, void 0, void 0, function* () {
const errorMessages = [];
if (message.descriptor.method === DwnMethodName.Subscribe && subscriptionHandler === undefined) {
throw new Error('AgentDwnApi: Subscription handler is required for subscription requests.');
}
// Try sending to author's publicly addressable DWNs until the first request succeeds.
for (let dwnUrl of dwnEndpointUrls) {
try {
if (subscriptionHandler !== undefined) {
// we get the server info to check if the server supports WebSocket for subscription requests
const serverInfo = yield this.agent.rpc.getServerInfo(dwnUrl);
if (!serverInfo.webSocketSupport) {
// If the server does not support WebSocket, add an error message and continue to the next URL.
errorMessages.push({
url: dwnUrl,
message: 'WebSocket support is not enabled on the server.'
});
continue;
}
// If the server supports WebSocket, replace the subscription URL with a socket transport.
// For `http` we use the unsecured `ws` protocol, and for `https` we use the secured `wss` protocol.
const parsedUrl = new URL(dwnUrl);
parsedUrl.protocol = parsedUrl.protocol === 'http:' ? 'ws:' : 'wss:';
dwnUrl = parsedUrl.toString();
}
const dwnReply = yield 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)}`);
});
}
constructDwnMessage({ request }) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const rawMessage = request.rawMessage;
let readableStream;
// TODO: Consider refactoring to move data transformations imposed by fetch() limitations to the HTTP transport-related methods.
if (isDwnRequest(request, DwnInterface.RecordsWrite)) {
const messageParams = request.messageParams;
if (request.dataStream && !(messageParams === null || messageParams === void 0 ? void 0 : 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) {
// @ts-ignore
messageParams.dataCid = yield Cid.computeDagPbCidFromStream(isomorphicNodeReadable);
// @ts-ignore
(_a = messageParams.dataSize) !== null && _a !== void 0 ? _a : (messageParams.dataSize = isomorphicNodeReadable['bytesRead']);
}
}
}
// Determine the signer for the message.
const signer = yield this.getSigner(request.author);
const dwnMessageConstructor = dwnMessageConstructors[request.messageType];
const dwnMessage = rawMessage ? yield dwnMessageConstructor.parse(rawMessage) : yield dwnMessageConstructor.create(Object.assign(Object.assign({}, request.messageParams), { signer }));
if (isRecordsWrite(dwnMessage) && request.signAsOwner) {
yield dwnMessage.signAsOwner(signer);
}
return { message: dwnMessage.message, dataStream: readableStream };
});
}
getSigner(author) {
return __awaiter(this, void 0, void 0, function* () {
// If the author is the Agent's DID, use the Agent's signer.
if (author === this.agent.agentDid.uri) {
const signer = yield this.agent.agentDid.getSigner();
return {
algorithm: signer.algorithm,
keyId: signer.keyId,
sign: (data) => __awaiter(this, void 0, void 0, function* () {
return yield signer.sign({ data });
})
};
}
else {
// Otherwise, use the author's DID to determine the signing method.
try {
const signingMethod = yield 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`);
}
// Compute the key URI of the verification method's public key.
const keyUri = yield this.agent.keyManager.getKeyUri({ key: signingMethod.publicKeyJwk });
// Verify that the key is present in the key manager. If not, an error is thrown.
const publicKey = yield this.agent.keyManager.getPublicKey({ keyUri });
// Bind the Agent's Key Manager to the signer.
const keyManager = this.agent.keyManager;
return {
algorithm: cryptoUtils.getJoseSignatureAlgorithmFromPublicKey(publicKey),
keyId: signingMethod.id,
sign: (data) => __awaiter(this, void 0, void 0, function* () {
return yield keyManager.sign({ data, keyUri: keyUri });
})
};
}
catch (error) {
throw new Error(`AgentDwnApi: Unable to get signer for author '${author}': ${error.message}`);
}
}
});
}
/**
* FURTHER REFACTORING NEEDED BELOW THIS LINE
*/
getDwnMessage({ author, messageCid }) {
return __awaiter(this, void 0, void 0, function* () {
const signer = yield this.getSigner(author);
// Construct a MessagesGet message to fetch the message.
const messagesGet = yield dwnMessageConstructors[DwnInterface.MessagesGet].create({
messageCids: [messageCid],
signer
});
const result = yield 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 };
// isRecordsWrite(message) && (dwnMessage.data = await this.getDataForRecordsWrite({ author, message, messageEntry, messageType, signer }));
// If the message is a RecordsWrite, either data will be present,
// OR we have to fetch it using a RecordsRead.
if (isRecordsWrite(messageEntry)) {
if (messageEntry.encodedData) {
const dataBytes = Convert.base64Url(messageEntry.encodedData).toUint8Array();
// TODO: test adding the messageEntry.message.descriptor.dataFormat to the Blob constructor.
dwnMessageWithBlob.data = new Blob([dataBytes]);
}
else {
const recordsRead = yield dwnMessageConstructors[DwnInterface.RecordsRead].create({
filter: {
recordId: messageEntry.message.recordId
},
signer
});
const reply = yield 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 = yield 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()
*/
createMessage({ author, messageParams, messageType }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the signer for the message.
const signer = yield this.getSigner(author);
const dwnMessageConstructor = dwnMessageConstructors[messageType];
const dwnMessage = yield dwnMessageConstructor.create(Object.assign(Object.assign({}, messageParams), { signer }));
return dwnMessage;
});
}
processMessage({ dataStream, message, targetDid }) {
return __awaiter(this, void 0, void 0, function* () {
return yield this._dwn.processMessage(targetDid, message, { dataStream });
});
}
}
//# sourceMappingURL=dwn-api.js.map
File diff suppressed because one or more lines are too long
+726
View File
@@ -0,0 +1,726 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { HDKey } from 'ed25519-keygen/hdkey';
import { BearerDid, DidDht } from '@web5/dids';
import { Convert, MemoryStore } from '@web5/common';
import { wordlist } from '@scure/bip39/wordlists/english';
import { generateMnemonic, mnemonicToSeed, validateMnemonic } from '@scure/bip39';
import { AgentCryptoApi } from './crypto-api.js';
import { LocalKeyManager } from './local-key-manager.js';
import { isPortableDid } from './prototyping/dids/utils.js';
import { DeterministicKeyGenerator } from './utils-internal.js';
import { CompactJwe } from './prototyping/crypto/jose/jwe-compact.js';
/**
* Type guard function to check if a given object is an empty string or a string containing only
* whitespace.
*
* This is an internal utility function used to validate password inputs, ensuring they are not
* empty or filled with only whitespace characters, which are considered invalid for password
* purposes.
*
* @param obj - The object to be checked, typically expected to be a password string.
* @returns A boolean value indicating whether the object is an empty string or a string with only
* whitespace.
*/
function isEmptyString(obj) {
return typeof obj !== 'string' || obj.trim().length === 0;
}
/**
* Type guard function to check if a given object conforms to the {@link IdentityVaultBackup}
* interface.
*
* This function is an internal utility meant to ensure the integrity and structure of the data
* assumed to be an {@link IdentityVaultBackup}. It verifies the presence and types of the
* `dateCreated`, `size`, and `data` properties, aligning with the expected structure of a backup
* object in the context of an {@link IdentityVault}.
*
* @param obj - The object to be verified against the {@link IdentityVaultBackup} interface.
* @returns A boolean value indicating whether the object is a valid {@link IdentityVaultBackup}.
*/
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';
}
/**
* Internal-only type guard function that checks if a given object conforms to the
* {@link IdentityVaultStatus} interface.
*
* This function is utilized within the {@link HdIdentityVault} implementation to ensure the
* integrity of the object representing the vault's status, verifying the presence and types of
* required properties. It aasserts the presence and correct types of `initialized`, `lastBackup`,
* and `lastRestore` properties, ensuring they align with the expected structure of an identity
* vault's status.
*
* @param obj - The object to be checked against the {{@link IdentityVaultStatus} interface.
* @returns A boolean indicating whether the object is an instance of {@link IdentityVaultStatus}.
*/
function isIdentityVaultStatus(obj) {
return typeof obj === 'object' && obj !== null
&& 'initialized' in obj && typeof obj.initialized === 'boolean'
&& 'lastBackup' in obj
&& 'lastRestore' in obj;
}
/**
* The `HdIdentityVault` class provides secure storage and management of identity data.
*
* The `HdIdentityVault` class implements the `IdentityVault` interface, providing secure storage
* and management of identity data with an added layer of security using Hierarchical Deterministic
* (HD) key derivation based on the SLIP-0010 standard for Ed25519 keys. It enhances identity
* protection by generating and securing the identity using a derived HD key, allowing for the
* deterministic regeneration of keys from a recovery phrase.
*
* The vault is capable of:
* - Secure initialization with a password and an optional recovery phrase, employing HD key
* derivation.
* - Encrypting the identity data using a derived content encryption key (CEK) which is securely
* encrypted and stored, accessible only by the correct password.
* - Securely backing up and restoring the vaults contents, including the HD-derived keys and
* associated DID.
* - Locking and unlocking the vault, which encrypts and decrypts the CEK for secure access to the
* vault's contents.
* - Managing the DID associated with the identity, providing a secure identity layer for
* applications.
*
* Usage involves initializing the vault with a secure password (and optionally a recovery phrase),
* which then allows for the secure storage, backup, and retrieval of the identity data.
*
* Note: Ensure the password is strong and securely managed, as it is crucial for the security of the
* vault's encrypted contents.
*
* @example
* ```typescript
* const vault = new HdIdentityVault();
* await vault.initialize({ password: 'secure-unique-phrase', recoveryPhrase: 'twelve words ...' });
* const backup = await vault.backup();
* await vault.restore({ backup, password: 'secure-unique-phrase' });
* ```
*/
export class HdIdentityVault {
/**
* 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 !== null && keyDerivationWorkFactor !== void 0 ? keyDerivationWorkFactor : 210000;
this._store = store !== null && store !== void 0 ? store : new 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.
*/
backup() {
return __awaiter(this, void 0, void 0, function* () {
// Verify the identity vault has already been initialized and unlocked.
if (this.isLocked() || (yield 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.');
}
// Encode the encrypted CEK and DID as a single Base64Url string.
const backupData = {
did: yield this.getStoredDid(),
contentEncryptionKey: yield this.getStoredContentEncryptionKey(),
status: yield this.getStatus()
};
const backupDataString = Convert.object(backupData).toBase64Url();
// Create a backup object containing the encrypted vault contents.
const backup = {
data: backupDataString,
dateCreated: new Date().toISOString(),
size: backupDataString.length
};
// Update the last backup timestamp in the data store.
yield 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.
*/
changePassword({ oldPassword, newPassword }) {
return __awaiter(this, void 0, void 0, function* () {
// Verify the identity vault has already been initialized.
if ((yield 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.');
}
// Lock the vault.
yield this.lock();
// Retrieve the content encryption key (CEK) record as a compact JWE from the data store.
const cekJwe = yield this.getStoredContentEncryptionKey();
// Decrypt the compact JWE using the given `oldPassword` to verify it is correct.
let protectedHeader;
let contentEncryptionKey;
try {
let contentEncryptionKeyBytes;
({ plaintext: contentEncryptionKeyBytes, protectedHeader } = yield CompactJwe.decrypt({
jwe: cekJwe,
key: Convert.string(oldPassword).toUint8Array(),
crypto: this.crypto,
keyManager: new LocalKeyManager()
}));
contentEncryptionKey = Convert.uint8Array(contentEncryptionKeyBytes).toObject();
}
catch (error) {
throw new Error(`HdIdentityVault: Unable to change the vault password due to an incorrectly entered old password.`);
}
// Re-encrypt the vault content encryption key (CEK) using the new password.
const newCekJwe = yield CompactJwe.encrypt({
key: Convert.string(newPassword).toUint8Array(),
protectedHeader,
plaintext: Convert.object(contentEncryptionKey).toUint8Array(),
crypto: this.crypto,
keyManager: new LocalKeyManager()
});
// Update the vault with the new CEK JWE.
yield this._store.set('contentEncryptionKey', newCekJwe);
// Update the vault CEK in memory, effectively unlocking the vault.
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}.
*/
getDid() {
return __awaiter(this, void 0, void 0, function* () {
// Verify the identity vault is unlocked.
if (this.isLocked()) {
throw new Error(`HdIdentityVault: Vault has not been initialized and unlocked.`);
}
// Retrieve the encrypted DID record as compact JWE from the vault store.
const didJwe = yield this.getStoredDid();
// Decrypt the compact JWE to obtain the PortableDid as a byte array.
const { plaintext: portableDidBytes } = yield CompactJwe.decrypt({
jwe: didJwe,
key: this._contentEncryptionKey,
crypto: this.crypto,
keyManager: new LocalKeyManager()
});
// Convert the DID from a byte array to PortableDid format.
const portableDid = Convert.uint8Array(portableDidBytes).toObject();
if (!isPortableDid(portableDid)) {
throw new Error('HdIdentityVault: Unable to decode malformed DID in identity vault');
}
// Return the DID in Bearer DID format.
return yield 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.
*/
getStatus() {
return __awaiter(this, void 0, void 0, function* () {
const storedStatus = yield this._store.get('vaultStatus');
// On the first run, the store will not contain an IdentityVaultStatus object yet, so return an
// uninitialized status.
if (!storedStatus) {
return {
initialized: false,
lastBackup: null,
lastRestore: null
};
}
const vaultStatus = 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.
*/
initialize({ password, recoveryPhrase }) {
return __awaiter(this, void 0, void 0, function* () {
/**
* STEP 0: Validate the input parameters and verify the identity vault is not already
* initialized.
*/
// Verify that the identity vault was not previously initialized.
if (yield this.isInitialized()) {
throw new Error(`HdIdentityVault: Vault has already been initialized.`);
}
// Verify that the password is not empty.
if (isEmptyString(password)) {
throw new Error(`HdIdentityVault: The password is required and cannot be blank. Please provide a ' +
'valid, non-empty password.`);
}
// If provided, verify that the recovery phrase is not empty.
if (recoveryPhrase && isEmptyString(recoveryPhrase)) {
throw new Error(`HdIdentityVault: The password is required and cannot be blank. Please provide a ' +
'valid, non-empty password.`);
}
/**
* STEP 1: Derive a Hierarchical Deterministic (HD) key pair from the given (or generated)
* recoveryPhrase.
*/
// Generate a 12-word (128-bit) mnemonic, if one was not provided.
recoveryPhrase !== null && recoveryPhrase !== void 0 ? recoveryPhrase : (recoveryPhrase = generateMnemonic(wordlist, 128));
// Validate the mnemonic for being 12-24 words contained in `wordlist`.
if (!validateMnemonic(recoveryPhrase, 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.');
}
// Derive a root seed from the mnemonic.
const rootSeed = yield mnemonicToSeed(recoveryPhrase);
// Derive a root key for the DID from the root seed.
const rootHdKey = HDKey.fromMasterSeed(rootSeed);
/**
* STEP 2: Derive the vault HD key pair from the root key.
*/
// The vault HD key is derived using account 0 and index 0 so that it can be
// deterministically re-derived. The vault key pair serves as input keying material for:
// - deriving the vault content encryption key (CEK)
// - deriving the salt that serves as input to derive the key that encrypts the vault CEK
const vaultHdKey = rootHdKey.derive(`m/44'/0'/0'/0'/0'`);
/**
* STEP 3: Derive the vault Content Encryption Key (CEK) from the vault private
* key and a non-secret static info value.
*/
// A non-secret static info value is combined with the vault private key as input to HKDF
// (Hash-based Key Derivation Function) to derive a 32-byte content encryption key (CEK).
const contentEncryptionKey = yield this.crypto.deriveKey({
algorithm: 'HKDF-512',
baseKeyBytes: vaultHdKey.privateKey,
salt: '',
info: 'vault_cek',
derivedKeyAlgorithm: 'A256GCM' // derived key algorithm
});
/**
* STEP 4: Using the given `password` and a `salt` derived from the vault public key, encrypt
* the vault CEK and store it in the data store as a compact JWE.
*/
// A non-secret static info value is combined with the vault public key as input to HKDF
// (Hash-based Key Derivation Function) to derive a new 32-byte salt.
const saltInput = yield this.crypto.deriveKeyBytes({
algorithm: 'HKDF-512',
baseKeyBytes: vaultHdKey.publicKey,
salt: '',
info: 'vault_unlock_salt',
length: 256, // derived key length, in bits
});
// Construct the JWE header.
const cekJweProtectedHeader = {
alg: 'PBES2-HS512+A256KW',
enc: 'A256GCM',
cty: 'text/plain',
p2c: this._keyDerivationWorkFactor,
p2s: Convert.uint8Array(saltInput).toBase64Url()
};
// Encrypt the vault content encryption key (CEK) to compact JWE format.
const cekJwe = yield CompactJwe.encrypt({
key: Convert.string(password).toUint8Array(),
protectedHeader: cekJweProtectedHeader,
plaintext: Convert.object(contentEncryptionKey).toUint8Array(),
crypto: this.crypto,
keyManager: new LocalKeyManager()
});
// Store the compact JWE in the data store.
yield this._store.set('contentEncryptionKey', cekJwe);
/**
* STEP 5: Create a DID using identity, signing, and encryption keys derived from the root key.
*/
// Derive the identity key pair using index 0 and convert to JWK format.
// Note: The account is set to Unix epoch time so that in the future, the keys for a DID DHT
// document can be deterministically derived based on the versionId returned in a DID
// resolution result.
const identityHdKey = rootHdKey.derive(`m/44'/0'/1708523827'/0'/0'`);
const identityPrivateKey = yield this.crypto.bytesToPrivateKey({
algorithm: 'Ed25519',
privateKeyBytes: identityHdKey.privateKey
});
// Derive the signing key using index 1 and convert to JWK format.
let signingHdKey = rootHdKey.derive(`m/44'/0'/1708523827'/0'/1'`);
const signingPrivateKey = yield this.crypto.bytesToPrivateKey({
algorithm: 'Ed25519',
privateKeyBytes: signingHdKey.privateKey
});
// TODO: Enable this once DID DHT supports X25519 keys.
// Derive the encryption key using index 1 and convert to JWK format.
// const encryptionHdKey = rootHdKey.derive(`m/44'/0'/1708523827'/0'/1'`);
// const encryptionKeyEd25519 = await this.crypto.bytesToPrivateKey({
// algorithm : 'Ed25519',
// privateKeyBytes : encryptionHdKey.privateKey
// });
// const encryptionPrivateKey = await Ed25519.convertPrivateKeyToX25519({ privateKey: encryptionKeyEd25519 });
// Add the identity and signing keys to the deterministic key generator so that when the DID is
// created it will use the derived keys.
const deterministicKeyGenerator = new DeterministicKeyGenerator();
yield deterministicKeyGenerator.addPredefinedKeys({
privateKeys: [identityPrivateKey, signingPrivateKey]
});
// Create the DID using the derived identity, signing, and encryption keys.
const did = yield 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']
// }
]
}
});
/**
* STEP 6: Convert the DID to portable format and store it in the data store as a
* compact JWE.
*/
// Convert the DID to a portable format.
const portableDid = yield did.export();
// Construct the JWE header.
const didJweProtectedHeader = {
alg: 'dir',
enc: 'A256GCM',
cty: 'json'
};
// Encrypt the DID to compact JWE format.
const didJwe = yield CompactJwe.encrypt({
key: contentEncryptionKey,
plaintext: Convert.object(portableDid).toUint8Array(),
protectedHeader: didJweProtectedHeader,
crypto: this.crypto,
keyManager: new LocalKeyManager()
});
// Store the compact JWE in the data store.
yield this._store.set('did', didJwe);
/**
* STEP 7: Set the vault CEK (effectively unlocking the vault), set the status to initialized,
* and return the mnemonic used to generate the vault key.
*/
this._contentEncryptionKey = contentEncryptionKey;
yield this.setStatus({ initialized: true });
// Return the recovery phrase in case it was generated so that it can be displayed to the user
// for safekeeping.
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`.
*/
isInitialized() {
return __awaiter(this, void 0, void 0, function* () {
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.
*/
lock() {
return __awaiter(this, void 0, void 0, function* () {
// Verify the identity vault has already been initialized.
if ((yield this.isInitialized()) === false) {
throw new Error(`HdIdentityVault: Lock operation failed. Vault has not been initialized.`);
}
// Clear the vault content encryption key (CEK), effectively locking the vault.
if (this._contentEncryptionKey)
this._contentEncryptionKey.k = '';
this._contentEncryptionKey = undefined;
});
}
/**
* 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.
*/
restore({ backup, password }) {
return __awaiter(this, void 0, void 0, function* () {
// Validate the backup object.
if (!isIdentityVaultBackup(backup)) {
throw new Error(`HdIdentityVault: Restore operation failed due to invalid backup object.`);
}
// Temporarily save the status and contents of the data store while attempting to restore the
// backup so that they are not lost in case the restore operation fails.
let previousStatus;
let previousContentEncryptionKey;
let previousDid;
try {
previousDid = yield this.getStoredDid();
previousContentEncryptionKey = yield this.getStoredContentEncryptionKey();
previousStatus = yield this.getStatus();
}
catch (_a) {
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 {
// Convert the backup data to a JSON object.
const backupData = Convert.base64Url(backup.data).toObject();
// Restore the backup to the data store.
yield this._store.set('did', backupData.did);
yield this._store.set('contentEncryptionKey', backupData.contentEncryptionKey);
yield this.setStatus(backupData.status);
// Attempt to unlock the vault with the given `password`.
yield this.unlock({ password });
}
catch (error) {
// If the restore operation fails, revert the data store to the status and contents that were
// saved before the restore operation was attempted.
yield this.setStatus(previousStatus);
yield this._store.set('contentEncryptionKey', previousContentEncryptionKey);
yield 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.');
}
// Update the last restore timestamp in the data store.
yield this.setStatus({ lastRestore: 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.
*/
unlock({ password }) {
return __awaiter(this, void 0, void 0, function* () {
// Lock the vault.
yield this.lock();
// Retrieve the content encryption key (CEK) record as a compact JWE from the data store.
const cekJwe = yield this.getStoredContentEncryptionKey();
// Decrypt the compact JWE.
try {
const { plaintext: contentEncryptionKeyBytes } = yield CompactJwe.decrypt({
jwe: cekJwe,
key: Convert.string(password).toUint8Array(),
crypto: this.crypto,
keyManager: new LocalKeyManager()
});
const contentEncryptionKey = Convert.uint8Array(contentEncryptionKeyBytes).toObject();
// Save the content encryption key in memory, thereby unlocking the vault.
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.
*/
getStoredDid() {
return __awaiter(this, void 0, void 0, function* () {
// Retrieve the DID record as a compact JWE from the data store.
const didJwe = yield 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.
*/
getStoredContentEncryptionKey() {
return __awaiter(this, void 0, void 0, function* () {
// Retrieve the content encryption key (CEK) record as a compact JWE from the data store.
const cekJwe = yield 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.
*/
setStatus({ initialized, lastBackup, lastRestore }) {
return __awaiter(this, void 0, void 0, function* () {
// Get the current status values from the store, if any.
let vaultStatus = yield this.getStatus();
// Update the status properties with new values specified, if any.
vaultStatus.initialized = initialized !== null && initialized !== void 0 ? initialized : vaultStatus.initialized;
vaultStatus.lastBackup = lastBackup !== null && lastBackup !== void 0 ? lastBackup : vaultStatus.lastBackup;
vaultStatus.lastRestore = lastRestore !== null && lastRestore !== void 0 ? lastRestore : vaultStatus.lastRestore;
// Write the changes to the store.
yield this._store.set('vaultStatus', JSON.stringify(vaultStatus));
return true;
});
}
}
//# sourceMappingURL=hd-identity-vault.js.map
File diff suppressed because one or more lines are too long
+164
View File
@@ -0,0 +1,164 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { BearerIdentity } from './bearer-identity.js';
import { isPortableDid } from './prototyping/dids/utils.js';
import { InMemoryIdentityStore } from './store-identity.js';
export function isPortableIdentity(obj) {
// Validate that the given value is an object that has the necessary properties of PortableIdentity.
return !(!obj || typeof obj !== 'object' || obj === null)
&& 'did' in obj
&& 'metadata' in obj
&& isPortableDid(obj.did);
}
export class AgentIdentityApi {
constructor({ agent, store } = {}) {
this._agent = agent;
// If `store` is not given, use an in-memory store by default.
this._store = store !== null && store !== void 0 ? 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 === undefined) {
throw new Error('AgentIdentityApi: Unable to determine agent execution context.');
}
return this._agent;
}
set agent(agent) {
this._agent = agent;
}
create({ metadata, didMethod = 'dht', didOptions, store, tenant }) {
return __awaiter(this, void 0, void 0, function* () {
// Unless an existing `tenant` is specified, a record that includes the DID's URI, document,
// and metadata will be stored under a new tenant controlled by the newly created DID.
const bearerDid = yield this.agent.did.create({
method: didMethod,
options: didOptions,
store,
tenant
});
// Create the BearerIdentity object.
const identity = new BearerIdentity({
did: bearerDid,
metadata: Object.assign(Object.assign({}, metadata), { uri: bearerDid.uri, tenant: tenant !== null && tenant !== void 0 ? tenant : bearerDid.uri })
});
// Persist the Identity to the store, by default, unless the `store` option is set to false.
if (store !== null && store !== void 0 ? store : true) {
yield this._store.set({
id: identity.did.uri,
data: identity.metadata,
agent: this.agent,
tenant: identity.metadata.tenant,
preventDuplicates: false,
useCache: true
});
}
return identity;
});
}
export({ didUri, tenant }) {
return __awaiter(this, void 0, void 0, function* () {
// Attempt to retrieve the Identity from the Agent's Identity store.
const bearerIdentity = yield this.get({ didUri, tenant });
if (!bearerIdentity) {
throw new Error(`AgentIdentityApi: Failed to export due to Identity not found: ${didUri}`);
}
// If the Identity was found, return the Identity in a portable format, and if supported by the
// Agent's key manager, the private key material.
const portableIdentity = yield bearerIdentity.export();
return portableIdentity;
});
}
get({ didUri, tenant }) {
return __awaiter(this, void 0, void 0, function* () {
// Attempt to retrieve the Identity from the Agent's Identity store.
const storedIdentity = yield this._store.get({ id: didUri, agent: this.agent, tenant, useCache: true });
// If the Identity is not found in the store, return undefined.
if (!storedIdentity)
return undefined;
// Retrieve the DID from the Agent's DID store using the tenant value from the stored
// Identity's metadata.
const storedDid = yield this.agent.did.get({ didUri, tenant: storedIdentity.tenant });
// If the Identity is present but the DID is not found, throw an error.
if (!storedDid) {
throw new Error(`AgentIdentityApi: Identity is present in the store but DID is missing: ${didUri}`);
}
// Create the BearerIdentity object.
const identity = new BearerIdentity({ did: storedDid, metadata: storedIdentity });
return identity;
});
}
import({ portableIdentity }) {
return __awaiter(this, void 0, void 0, function* () {
// Import the PortableDid to the Agent's DID store.
const storedDid = yield this.agent.did.import({
portableDid: portableIdentity.portableDid,
tenant: portableIdentity.metadata.tenant
});
// Verify the DID is present in the Agent's DID store.
if (!storedDid) {
throw new Error(`AgentIdentityApi: Failed to import Identity: ${portableIdentity.metadata.uri}`);
}
// Create the BearerIdentity object.
const identity = new BearerIdentity({ did: storedDid, metadata: portableIdentity.metadata });
// Store the Identity metadata in the Agent's Identity store.
yield this._store.set({
id: identity.did.uri,
data: identity.metadata,
agent: this.agent,
tenant: identity.metadata.tenant,
preventDuplicates: true,
useCache: true
});
return identity;
});
}
list({ tenant } = {}) {
return __awaiter(this, void 0, void 0, function* () {
// Retrieve the list of Identities from the Agent's Identity store.
const storedIdentities = yield this._store.list({ agent: this.agent, tenant });
const identities = [];
for (const metadata of storedIdentities) {
const identity = yield this.get({ didUri: metadata.uri, tenant: metadata.tenant });
identities.push(identity);
}
return identities;
});
}
manage({ portableIdentity }) {
return __awaiter(this, void 0, void 0, function* () {
// Retrieve the DID using the `tenant` stored in the given Identity's metadata.
const storedDid = yield this.agent.did.get({
didUri: portableIdentity.metadata.uri,
tenant: portableIdentity.metadata.tenant
});
// Verify the DID is present in the DID store.
if (!storedDid) {
throw new Error(`AgentIdentityApi: Failed to manage Identity: ${portableIdentity.metadata.uri}`);
}
// Create the BearerIdentity object.
const identity = new BearerIdentity({ did: storedDid, metadata: portableIdentity.metadata });
// Store the Identity metadata in the Agent's Identity store.
yield this._store.set({
id: identity.did.uri,
data: identity.metadata,
agent: this.agent,
preventDuplicates: true,
useCache: true
});
return identity;
});
}
}
//# sourceMappingURL=identity-api.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"identity-api.js","sourceRoot":"","sources":["../../src/identity-api.ts"],"names":[],"mappings":"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"}
+18
View File
@@ -0,0 +1,18 @@
export * from './types/dwn.js';
export * from './bearer-identity.js';
export * from './crypto-api.js';
export * from './did-api.js';
export * from './dwn-api.js';
export * from './hd-identity-vault.js';
export * from './identity-api.js';
export * from './local-key-manager.js';
export * from './rpc-client.js';
export * from './store-data.js';
export * from './store-did.js';
export * from './store-identity.js';
export * from './store-key.js';
export * from './sync-api.js';
export * from './sync-engine-level.js';
export * from './test-harness.js';
export * from './utils.js';
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,cAAc,gBAAgB,CAAC;AAO/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,wBAAwB,CAAC;AACvC,cAAe,mBAAmB,CAAC;AACnC,cAAc,YAAY,CAAC"}
+488
View File
@@ -0,0 +1,488 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import { isPrivateJwk, Sha2Algorithm, EcdsaAlgorithm, EdDsaAlgorithm, AesGcmAlgorithm, KEY_URI_PREFIX_JWK, computeJwkThumbprint, } from '@web5/crypto';
import { InMemoryKeyStore } from './store-key.js';
import { AesKwAlgorithm } from './prototyping/crypto/algorithms/aes-kw.js';
import { CryptoError, CryptoErrorCode } from './prototyping/crypto/crypto-error.js';
/**
* `supportedAlgorithms` is an object mapping algorithm names to their respective implementations
* Each entry in this map specifies the algorithm name and its associated properties, including the
* implementation class and any relevant names or identifiers for the algorithm. This structure
* allows for easy retrieval and instantiation of algorithm implementations based on the algorithm
* name or key specification. It facilitates the support of multiple algorithms within the
* `LocalKeyManager` class.
*/
const supportedAlgorithms = {
'AES-GCM': {
implementation: AesGcmAlgorithm,
names: ['A128GCM', 'A192GCM', 'A256GCM'],
},
'AES-KW': {
implementation: AesKwAlgorithm,
names: ['A128KW', 'A192KW', 'A256KW'],
},
'Ed25519': {
implementation: EdDsaAlgorithm,
names: ['Ed25519'],
},
'secp256k1': {
implementation: EcdsaAlgorithm,
names: ['ES256K', 'secp256k1'],
},
'secp256r1': {
implementation: EcdsaAlgorithm,
names: ['ES256', 'secp256r1'],
},
'SHA-256': {
implementation: Sha2Algorithm,
names: ['SHA-256']
}
};
export class LocalKeyManager {
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 = new Map();
this._agent = agent;
this._keyStore = keyStore !== null && keyStore !== void 0 ? 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 === undefined) {
throw new Error('LocalKeyManager: Unable to determine agent execution context.');
}
return this._agent;
}
set agent(agent) {
this._agent = agent;
}
decrypt(_a) {
var { keyUri } = _a, params = __rest(_a, ["keyUri"]);
return __awaiter(this, void 0, void 0, function* () {
// Get the private key from the key store.
const privateKey = yield this.getPrivateKey({ keyUri });
// Determine the algorithm name based on the JWK's `alg` property.
const algorithm = this.getAlgorithmName({ key: privateKey });
// Get the cipher algorithm based on the algorithm name.
const cipher = this.getAlgorithm({ algorithm });
// Encrypt the data.
const ciphertext = yield cipher.decrypt(Object.assign({ key: privateKey }, params));
return ciphertext;
});
}
digest(_params) {
throw new Error('Method not implemented.');
}
encrypt(_a) {
var { keyUri } = _a, params = __rest(_a, ["keyUri"]);
return __awaiter(this, void 0, void 0, function* () {
// Get the private key from the key store.
const privateKey = yield this.getPrivateKey({ keyUri });
// Determine the algorithm name based on the JWK's `alg` property.
const algorithm = this.getAlgorithmName({ key: privateKey });
// Get the cipher algorithm based on the algorithm name.
const cipher = this.getAlgorithm({ algorithm });
// Encrypt the data.
const ciphertext = yield cipher.encrypt(Object.assign({ 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.
*/
exportKey({ keyUri }) {
return __awaiter(this, void 0, void 0, function* () {
// Get the private key from the key store.
const privateKey = yield 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.
*/
generateKey({ algorithm: algorithmIdentifier }) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the given algorithm identifier.
const algorithm = this.getAlgorithmName({ key: { alg: algorithmIdentifier } });
// Get the key generator implementation based on the algorithm.
const keyGenerator = this.getAlgorithm({ algorithm });
// Generate the key.
const privateKey = yield keyGenerator.generateKey({ algorithm: algorithmIdentifier });
// If the key ID is undefined, set it to the JWK thumbprint.
(_a = privateKey.kid) !== null && _a !== void 0 ? _a : (privateKey.kid = yield computeJwkThumbprint({ jwk: privateKey }));
// Compute the key URI for the key.
const keyUri = yield this.getKeyUri({ key: privateKey });
// Store the key in the key store.
yield 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.
*/
getKeyUri({ key }) {
return __awaiter(this, void 0, void 0, function* () {
// Compute the JWK thumbprint.
const jwkThumbprint = yield computeJwkThumbprint({ jwk: key });
// Construct the key URI by appending the JWK thumbprint to the key URI prefix.
const keyUri = `${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.
*/
getPublicKey({ keyUri }) {
return __awaiter(this, void 0, void 0, function* () {
// Get the private key from the key store.
const privateKey = yield this.getPrivateKey({ keyUri });
// Determine the algorithm name based on the JWK's `alg` and `crv` properties.
const algorithm = this.getAlgorithmName({ key: privateKey });
// Get the key generator based on the algorithm name.
const keyGenerator = this.getAlgorithm({ algorithm });
// Get the public key properties from the private JWK.
const publicKey = yield 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.
*/
importKey({ key }) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
if (!isPrivateJwk(key))
throw new TypeError('Invalid key provided. Must be a private key in JWK format.');
// Make a deep copy of the key to avoid mutating the original.
const privateKey = structuredClone(key);
// If the key ID is undefined, set it to the JWK thumbprint.
(_a = privateKey.kid) !== null && _a !== void 0 ? _a : (privateKey.kid = yield computeJwkThumbprint({ jwk: privateKey }));
// Compute the key URI for the key.
const keyUri = yield this.getKeyUri({ key: privateKey });
// Store the key in the key store.
yield 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`.
*/
sign({ keyUri, data }) {
return __awaiter(this, void 0, void 0, function* () {
// Get the private key from the key store.
const privateKey = yield this.getPrivateKey({ keyUri });
// Determine the algorithm name based on the JWK's `alg` and `crv` properties.
const algorithm = this.getAlgorithmName({ key: privateKey });
// Get the signature algorithm based on the algorithm name.
const signer = this.getAlgorithm({ algorithm });
// Sign the data.
const signature = signer.sign({ data, key: privateKey });
return signature;
});
}
unwrapKey({ wrappedKeyBytes, wrappedKeyAlgorithm, decryptionKeyUri }) {
return __awaiter(this, void 0, void 0, function* () {
// Get the private key from the key store.
const decryptionKey = yield this.getPrivateKey({ keyUri: decryptionKeyUri });
// Determine the algorithm name based on the JWK's `alg` property.
const algorithm = this.getAlgorithmName({ key: decryptionKey });
// Get the key wrapping algorithm based on the algorithm name.
const keyWrapper = this.getAlgorithm({ algorithm });
// Decrypt the key.
const unwrappedKey = yield 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.
*/
verify({ key, signature, data }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the JWK's `alg` and `crv` properties.
const algorithm = this.getAlgorithmName({ key });
// Get the signature algorithm based on the algorithm name.
const signer = this.getAlgorithm({ algorithm });
// Verify the signature.
const isSignatureValid = signer.verify({ key, signature, data });
return isSignatureValid;
});
}
wrapKey({ unwrappedKey, encryptionKeyUri }) {
return __awaiter(this, void 0, void 0, function* () {
// Get the private key from the key store.
const encryptionKey = yield this.getPrivateKey({ keyUri: encryptionKeyUri });
// Determine the algorithm name based on the JWK's `alg` property.
const algorithm = this.getAlgorithmName({ key: encryptionKey });
// Get the key wrapping algorithm based on the algorithm name.
const keyWrapper = this.getAlgorithm({ algorithm });
// Encrypt the key.
const wrappedKeyBytes = yield 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 }) {
var _a;
// Check if algorithm is supported.
const AlgorithmImplementation = (_a = supportedAlgorithms[algorithm]) === null || _a === void 0 ? void 0 : _a['implementation'];
if (!AlgorithmImplementation) {
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `Algorithm not supported: ${algorithm}`);
}
// Check if instance already exists for the `AlgorithmImplementation`.
if (!this._algorithmInstances.has(AlgorithmImplementation)) {
// If not, create a new instance and store it in the cache
this._algorithmInstances.set(AlgorithmImplementation, new AlgorithmImplementation());
}
// Return the cached instance
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(supportedAlgorithms)) {
const algorithmNames = supportedAlgorithms[algorithmIdentifier].names;
if (algProperty && algorithmNames.includes(algProperty)) {
return algorithmIdentifier;
}
else if (crvProperty && algorithmNames.includes(crvProperty)) {
return algorithmIdentifier;
}
}
throw new CryptoError(CryptoErrorCode.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.
*/
getPrivateKey({ keyUri }) {
return __awaiter(this, void 0, void 0, function* () {
// Get the private key from the key store.
const privateKey = yield this._keyStore.get({ id: keyUri, agent: this.agent, useCache: true });
if (!privateKey) {
throw new Error(`Key not found: ${keyUri}`);
}
return privateKey;
});
}
}
//# sourceMappingURL=local-key-manager.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=dwn-rpc-types.js.map
@@ -0,0 +1 @@
{"version":3,"file":"dwn-rpc-types.js","sourceRoot":"","sources":["../../../../src/prototyping/clients/dwn-rpc-types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,74 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import ms from 'ms';
import { TtlCache } from '@web5/common';
export class DwnServerInfoCacheMemory {
constructor({ ttl = '15m' } = {}) {
this.cache = new TtlCache({ ttl: ms(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.
*/
get(dwnUrl) {
return __awaiter(this, void 0, void 0, function* () {
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.
*/
set(dwnUrl, value) {
return __awaiter(this, void 0, void 0, function* () {
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.
*/
delete(dwnUrl) {
return __awaiter(this, void 0, void 0, function* () {
this.cache.delete(dwnUrl);
});
}
/**
* Clears all entries from the cache.
*
* @returns A promise that resolves when the operation is complete.
*/
clear() {
return __awaiter(this, void 0, void 0, function* () {
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.
*/
close() {
return __awaiter(this, void 0, void 0, function* () {
// No-op since there is no underlying store to close.
});
}
}
//# sourceMappingURL=dwn-server-info-cache-memory.js.map
@@ -0,0 +1 @@
{"version":3,"file":"dwn-server-info-cache-memory.js","sourceRoot":"","sources":["../../../../src/prototyping/clients/dwn-server-info-cache-memory.ts"],"names":[],"mappings":";;;;;;;;;AACA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAkBxC,MAAM,OAAO,wBAAwB;IAGnC,YAAY,EAAE,GAAG,GAAG,KAAK,KAAoC,EAAE;QAC7D,IAAI,CAAC,KAAK,GAAG,IAAI,QAAQ,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED;;;;;;;OAOG;IACU,GAAG,CAAC,MAAc;;YAC7B,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;KAAA;IAED;;;;;;OAMG;IACU,GAAG,CAAC,MAAc,EAAE,KAAiB;;YAChD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAChC,CAAC;KAAA;IAED;;;;;OAKG;IACU,MAAM,CAAC,MAAc;;YAChC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;KAAA;IAED;;;;OAIG;IACU,KAAK;;YAChB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;KAAA;IAED;;;;;OAKG;IACU,KAAK;;YAChB,qDAAqD;QACvD,CAAC;KAAA;CACF"}
@@ -0,0 +1,102 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { createJsonRpcRequest, parseJson } from './json-rpc.js';
import { utils as cryptoUtils } from '@web5/crypto';
import { DwnServerInfoCacheMemory } from './dwn-server-info-cache-memory.js';
/**
* HTTP client that can be used to communicate with Dwn Servers
*/
export class HttpDwnRpcClient {
constructor(serverInfoCache) {
this.serverInfoCache = serverInfoCache !== null && serverInfoCache !== void 0 ? serverInfoCache : new DwnServerInfoCacheMemory();
}
get transportProtocols() { return ['http:', 'https:']; }
sendDwnRequest(request) {
return __awaiter(this, void 0, void 0, function* () {
const requestId = cryptoUtils.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) {
// @ts-expect-error TODO: REMOVE
fetchOpts.headers['content-type'] = 'application/octet-stream';
// @ts-expect-error TODO: REMOVE
fetchOpts['body'] = request.data;
}
const resp = yield fetch(request.dwnUrl, fetchOpts);
let dwnRpcResponse;
// check to see if response is in header first. if it is, that means the response is a ReadableStream
let dataStream;
const { headers } = resp;
if (headers.has('dwn-response')) {
// @ts-expect-error TODO: REMOVE
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 {
// TODO: wonder if i need to try/catch this?
const responseBody = yield 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;
});
}
getServerInfo(dwnUrl) {
return __awaiter(this, void 0, void 0, function* () {
const serverInfo = yield this.serverInfoCache.get(dwnUrl);
if (serverInfo) {
return serverInfo;
}
const url = new URL(dwnUrl);
// add `/info` to the dwn server url path
url.pathname.endsWith('/') ? url.pathname += 'info' : url.pathname += '/info';
try {
const response = yield fetch(url.toString());
if (response.ok) {
const results = yield response.json();
// explicitly return and cache only the desired properties.
const serverInfo = {
registrationRequirements: results.registrationRequirements,
maxFileSize: results.maxFileSize,
webSocketSupport: results.webSocketSupport,
};
this.serverInfoCache.set(dwnUrl, serverInfo);
return serverInfo;
}
else {
throw new Error(`HTTP (${response.status}) - ${response.statusText}`);
}
}
catch (error) {
throw new Error(`Error encountered while processing response from ${url.toString()}: ${error.message}`);
}
});
}
}
//# sourceMappingURL=http-dwn-rpc-client.js.map
@@ -0,0 +1 @@
{"version":3,"file":"http-dwn-rpc-client.js","sourceRoot":"","sources":["../../../../src/prototyping/clients/http-dwn-rpc-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,150 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { utils as cryptoUtils } from '@web5/crypto';
import IsomorphicWebSocket from 'isomorphic-ws';
import { createJsonRpcSubscriptionRequest, parseJson } from './json-rpc.js';
// These were arbitrarily chosen, but can be modified via connect options
const CONNECT_TIMEOUT = 3000;
const RESPONSE_TIMEOUT = 30000;
/**
* JSON RPC Socket Client for WebSocket request/response and long-running subscriptions.
*
* NOTE: This is temporarily copied over from https://github.com/TBD54566975/dwn-server/blob/main/src/json-rpc-socket.ts
* This was done in order to avoid taking a dependency on the `dwn-server`, until a future time when there will be a `clients` package.
*/
export class JsonRpcSocket {
constructor(socket, responseTimeout) {
this.socket = socket;
this.responseTimeout = responseTimeout;
this.messageHandlers = new Map();
}
static connect(url, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const { connectTimeout = CONNECT_TIMEOUT, responseTimeout = RESPONSE_TIMEOUT, onclose, onerror } = options;
const socket = new IsomorphicWebSocket(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.
*/
request(request) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
var _a;
(_a = request.id) !== null && _a !== void 0 ? _a : (request.id = cryptoUtils.randomUuid());
const handleResponse = (event) => {
const jsonRpsResponse = parseJson(event.data);
if (jsonRpsResponse.id === request.id) {
// if the incoming response id matches the request id, we will remove the listener and resolve the response
this.messageHandlers.delete(request.id);
return resolve(jsonRpsResponse);
}
};
// add the listener to the map of message handlers
this.messageHandlers.set(request.id, handleResponse);
this.send(request);
// reject this promise if we don't receive any response back within the timeout period
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.
*/
subscribe(request, listener) {
return __awaiter(this, void 0, void 0, function* () {
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 !== undefined) {
// remove the event listener upon receipt of a JSON RPC Error.
this.messageHandlers.delete(subscriptionId);
this.closeSubscription(subscriptionId);
}
listener(jsonRpcResponse);
}
};
this.messageHandlers.set(subscriptionId, socketEventListener);
const response = yield this.request(request);
if (response.error) {
this.messageHandlers.delete(subscriptionId);
return { response };
}
// clean up listener and create a `rpc.subscribe.close` message to use when closing this JSON RPC subscription
const close = () => __awaiter(this, void 0, void 0, function* () {
this.messageHandlers.delete(subscriptionId);
yield this.closeSubscription(subscriptionId);
});
return {
response,
close
};
});
}
closeSubscription(id) {
const requestId = cryptoUtils.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));
}
}
//# sourceMappingURL=json-rpc-socket.js.map
@@ -0,0 +1 @@
{"version":3,"file":"json-rpc-socket.js","sourceRoot":"","sources":["../../../../src/prototyping/clients/json-rpc-socket.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
export var JsonRpcErrorCodes;
(function (JsonRpcErrorCodes) {
// JSON-RPC 2.0 pre-defined errors
JsonRpcErrorCodes[JsonRpcErrorCodes["InvalidRequest"] = -32600] = "InvalidRequest";
JsonRpcErrorCodes[JsonRpcErrorCodes["MethodNotFound"] = -32601] = "MethodNotFound";
JsonRpcErrorCodes[JsonRpcErrorCodes["InvalidParams"] = -32602] = "InvalidParams";
JsonRpcErrorCodes[JsonRpcErrorCodes["InternalError"] = -32603] = "InternalError";
JsonRpcErrorCodes[JsonRpcErrorCodes["ParseError"] = -32700] = "ParseError";
JsonRpcErrorCodes[JsonRpcErrorCodes["TransportError"] = -32300] = "TransportError";
// App defined errors
JsonRpcErrorCodes[JsonRpcErrorCodes["BadRequest"] = -50400] = "BadRequest";
JsonRpcErrorCodes[JsonRpcErrorCodes["Unauthorized"] = -50401] = "Unauthorized";
JsonRpcErrorCodes[JsonRpcErrorCodes["Forbidden"] = -50403] = "Forbidden";
})(JsonRpcErrorCodes || (JsonRpcErrorCodes = {}));
export const createJsonRpcErrorResponse = (id, code, message, data) => {
const error = { code, message, data };
return {
jsonrpc: '2.0',
id,
error,
};
};
export const createJsonRpcRequest = (id, method, params) => {
return {
jsonrpc: '2.0',
id,
method,
params,
};
};
export const createJsonRpcSubscriptionRequest = (id, method, subscriptionId, params) => {
return {
jsonrpc: '2.0',
id,
method: `rpc.subscribe.${method}`,
params,
subscription: {
id: subscriptionId,
}
};
};
export const createJsonRpcSuccessResponse = (id, result) => {
return {
jsonrpc: '2.0',
id,
result,
};
};
export function parseJson(text) {
try {
return JSON.parse(text);
}
catch (_a) {
return null;
}
}
//# sourceMappingURL=json-rpc.js.map
@@ -0,0 +1 @@
{"version":3,"file":"json-rpc.js","sourceRoot":"","sources":["../../../../src/prototyping/clients/json-rpc.ts"],"names":[],"mappings":"AAmBA,MAAM,CAAN,IAAY,iBAaX;AAbD,WAAY,iBAAiB;IAC3B,kCAAkC;IAClC,kFAAuB,CAAA;IACvB,kFAAuB,CAAA;IACvB,gFAAsB,CAAA;IACtB,gFAAsB,CAAA;IACtB,0EAAmB,CAAA;IACnB,kFAAuB,CAAA;IAEvB,qBAAqB;IACrB,0EAAmB,CAAA;IACnB,8EAAqB,CAAA;IACrB,wEAAkB,CAAA;AACpB,CAAC,EAbW,iBAAiB,KAAjB,iBAAiB,QAa5B;AAkBD,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACxC,EAAa,EACb,IAAuB,EACvB,OAAe,EACf,IAAU,EACY,EAAE;IACxB,MAAM,KAAK,GAAiB,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACpD,OAAO;QACL,OAAO,EAAE,KAAK;QACd,EAAE;QACF,KAAK;KACN,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,EAAa,EACb,MAAc,EACd,MAAsB,EACN,EAAE;IAClB,OAAO;QACL,OAAO,EAAE,KAAK;QACd,EAAE;QACF,MAAM;QACN,MAAM;KACP,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAC9C,EAAa,EACb,MAAc,EACd,cAAyB,EACzB,MAAY,EACI,EAAE;IAClB,OAAO;QACL,OAAO,EAAQ,KAAK;QACpB,EAAE;QACF,MAAM,EAAS,iBAAiB,MAAM,EAAE;QACxC,MAAM;QACN,YAAY,EAAG;YACb,EAAE,EAAE,cAAc;SACnB;KACF,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAC1C,EAAa,EACb,MAAW,EACa,EAAE;IAC1B,OAAO;QACL,OAAO,EAAE,KAAK;QACd,EAAE;QACF,MAAM;KACP,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,IAAI;QACF,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;KACzB;IAAC,WAAM;QACN,OAAO,IAAI,CAAC;KACb;AACH,CAAC"}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=server-info-types.js.map
@@ -0,0 +1 @@
{"version":3,"file":"server-info-types.js","sourceRoot":"","sources":["../../../../src/prototyping/clients/server-info-types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,90 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { utils as cryptoUtils } from '@web5/crypto';
import { createJsonRpcRequest, createJsonRpcSubscriptionRequest } from './json-rpc.js';
import { JsonRpcSocket } from './json-rpc-socket.js';
export class WebSocketDwnRpcClient {
get transportProtocols() { return ['ws:', 'wss:']; }
sendDwnRequest(request, jsonRpcSocketOptions) {
return __awaiter(this, void 0, void 0, function* () {
// validate that the dwn URL provided is a valid WebSocket URL
const url = new URL(request.dwnUrl);
if (url.protocol !== 'ws:' && url.protocol !== 'wss:') {
throw new Error(`Invalid websocket protocol ${url.protocol}`);
}
// check if there is already a connection to this host, if it does not exist, initiate a new connection
const hasConnection = WebSocketDwnRpcClient.connections.has(url.host);
if (!hasConnection) {
try {
const socket = yield JsonRpcSocket.connect(url.toString(), jsonRpcSocketOptions);
const subscriptions = 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 processMessage(connection, target, message) {
return __awaiter(this, void 0, void 0, function* () {
const requestId = cryptoUtils.randomUuid();
const request = createJsonRpcRequest(requestId, 'dwn.processMessage', { target, message });
const { socket } = connection;
const response = yield socket.request(request);
const { error, result } = response;
if (error !== undefined) {
throw new Error(`error sending DWN request: ${error.message}`);
}
return result.reply;
});
}
static subscriptionRequest(connection, target, message, messageHandler) {
return __awaiter(this, void 0, void 0, function* () {
const requestId = cryptoUtils.randomUuid();
const subscriptionId = cryptoUtils.randomUuid();
const request = createJsonRpcSubscriptionRequest(requestId, 'dwn.processMessage', subscriptionId, { target, message });
const { socket, subscriptions } = connection;
const { response, close } = yield socket.subscribe(request, (response) => {
const { result, error } = response;
if (error) {
// if there is an error, close the subscription and delete it from the connection
const subscription = subscriptions.get(subscriptionId);
if (subscription) {
subscription.close();
}
subscriptions.delete(subscriptionId);
return;
}
const { event } = result;
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, Object.assign(Object.assign({}, reply.subscription), { close }));
reply.subscription.close = close;
}
return reply;
});
}
}
// a map of dwn host to WebSocket connection
WebSocketDwnRpcClient.connections = new Map();
//# sourceMappingURL=web-socket-clients.js.map
@@ -0,0 +1 @@
{"version":3,"file":"web-socket-clients.js","sourceRoot":"","sources":["../../../../src/prototyping/clients/web-socket-clients.ts"],"names":[],"mappings":";;;;;;;;;AAGA,OAAO,EAAE,KAAK,IAAI,WAAW,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,gCAAgC,EAAE,MAAM,eAAe,CAAC;AACvF,OAAO,EAAE,aAAa,EAAwB,MAAM,sBAAsB,CAAC;AAO3E,MAAM,OAAO,qBAAqB;IAChC,IAAW,kBAAkB,KAAK,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAIrD,cAAc,CAAC,OAAsB,EAAE,oBAA2C;;YAEtF,8DAA8D;YAC9D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACpC,IAAI,GAAG,CAAC,QAAQ,KAAK,KAAK,IAAI,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE;gBACrD,MAAM,IAAI,KAAK,CAAC,8BAA8B,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;aAC/D;YAED,uGAAuG;YACvG,MAAM,aAAa,GAAG,qBAAqB,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACtE,IAAI,CAAC,aAAa,EAAE;gBAClB,IAAI;oBACF,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,oBAAoB,CAAC,CAAC;oBACjF,MAAM,aAAa,GAAG,IAAI,GAAG,EAAE,CAAC;oBAChC,qBAAqB,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;iBAC5E;gBAAC,OAAM,KAAK,EAAE;oBACb,MAAM,IAAI,KAAK,CAAC,uBAAuB,GAAG,CAAC,IAAI,KAAM,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;iBACjF;aACF;YAED,MAAM,UAAU,GAAG,qBAAqB,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC;YACpE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,mBAAmB,EAAE,GAAG,OAAO,CAAC;YAE5D,IAAI,mBAAmB,EAAE;gBACvB,OAAO,qBAAqB,CAAC,mBAAmB,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC;aACvG;YAED,OAAO,qBAAqB,CAAC,cAAc,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QAC9E,CAAC;KAAA;IAEO,MAAM,CAAO,cAAc,CAAC,UAA4B,EAAE,MAAc,EAAE,OAAuB;;YACvG,MAAM,SAAS,GAAG,WAAW,CAAC,UAAU,EAAE,CAAC;YAC3C,MAAM,OAAO,GAAG,oBAAoB,CAAC,SAAS,EAAE,oBAAoB,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;YAE3F,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC;YAC9B,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAE/C,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC;YACnC,IAAI,KAAK,KAAK,SAAS,EAAE;gBACvB,MAAM,IAAI,KAAK,CAAC,8BAA8B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;aAChE;YAED,OAAO,MAAM,CAAC,KAAuB,CAAC;QACxC,CAAC;KAAA;IAEO,MAAM,CAAO,mBAAmB,CAAC,UAA4B,EAAE,MAAa,EAAE,OAAuB,EAAE,cAAsC;;YACnJ,MAAM,SAAS,GAAG,WAAW,CAAC,UAAU,EAAE,CAAC;YAC3C,MAAM,cAAc,GAAG,WAAW,CAAC,UAAU,EAAE,CAAC;YAChD,MAAM,OAAO,GAAG,gCAAgC,CAAC,SAAS,EAAE,oBAAoB,EAAE,cAAc,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;YAEvH,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,UAAU,CAAC;YAC7C,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE;gBACvE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC;gBACnC,IAAI,KAAK,EAAE;oBAET,iFAAiF;oBACjF,MAAM,YAAY,GAAG,aAAa,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;oBACvD,IAAI,YAAY,EAAE;wBAChB,YAAY,CAAC,KAAK,EAAE,CAAC;qBACtB;oBAED,aAAa,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;oBACrC,OAAO;iBACR;gBAED,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;gBACzB,cAAc,CAAC,KAAK,CAAC,CAAC;YACxB,CAAC,CAAC,CAAC;YAEH,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC;YACnC,IAAI,KAAK,EAAE;gBACT,MAAM,IAAI,KAAK,CAAC,2CAA2C,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;aAC7E;YAED,MAAM,EAAE,KAAK,EAAE,GAAG,MAAsC,CAAC;YACzD,IAAI,KAAK,CAAC,YAAY,IAAI,KAAK,EAAE;gBAC/B,aAAa,CAAC,GAAG,CAAC,cAAc,kCAAO,KAAK,CAAC,YAAY,KAAE,KAAK,IAAG,CAAC;gBACpE,KAAK,CAAC,YAAY,CAAC,KAAK,GAAG,KAAK,CAAC;aAClC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;KAAA;;AApFD,4CAA4C;AAC7B,iCAAW,GAAG,IAAI,GAAG,EAA4B,CAAC"}
@@ -0,0 +1,14 @@
export function hasDuplicateProperties(...objects) {
const propertySet = new Set();
const objectsWithoutUndefined = objects.filter(Boolean); // Remove any undefined values
for (const obj of objectsWithoutUndefined) {
for (const key in obj) {
if (propertySet.has(key)) {
return true; // Return true if a duplicate property is found
}
propertySet.add(key);
}
}
return false; // Return false if no duplicates are found
}
//# sourceMappingURL=object.js.map
@@ -0,0 +1 @@
{"version":3,"file":"object.js","sourceRoot":"","sources":["../../../../src/prototyping/common/object.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,sBAAsB,CAAC,GAAG,OAA+C;IACvF,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IACtC,MAAM,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,8BAA8B;IAEvF,KAAK,MAAM,GAAG,IAAI,uBAAuB,EAAE;QACzC,KAAK,MAAM,GAAG,IAAI,GAAG,EAAE;YACrB,IAAI,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;gBACxB,OAAO,IAAI,CAAC,CAAC,+CAA+C;aAC7D;YACD,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;SACtB;KACF;IAED,OAAO,KAAK,CAAC,CAAC,0CAA0C;AAC1D,CAAC"}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=type-utils.js.map
@@ -0,0 +1 @@
{"version":3,"file":"type-utils.js","sourceRoot":"","sources":["../../../../src/prototyping/common/type-utils.ts"],"names":[],"mappings":""}
@@ -0,0 +1,147 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { AesGcm, CryptoAlgorithm } from '@web5/crypto';
/**
* The `AesGcmAlgorithm` class provides a concrete implementation for cryptographic operations using
* the AES algorithm in Galois/Counter Mode (GCM). This class implements both
* {@link Cipher | `Cipher`} and { @link KeyGenerator | `KeyGenerator`} interfaces, providing
* key generation, encryption, and decryption features.
*
* This class is typically accessed through implementations that extend the
* {@link CryptoApi | `CryptoApi`} interface.
*/
export class AesGcmAlgorithm extends CryptoAlgorithm {
bytesToPrivateKey({ privateKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the byte array to a JWK.
const privateKey = yield AesGcm.bytesToPrivateKey({ privateKeyBytes });
// Set the `alg` property based on the key length.
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.
*/
decrypt(params) {
return __awaiter(this, void 0, void 0, function* () {
const plaintext = 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.
*/
encrypt(params) {
return __awaiter(this, void 0, void 0, function* () {
const ciphertext = 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.
*/
generateKey({ algorithm }) {
return __awaiter(this, void 0, void 0, function* () {
// Map algorithm name to key length.
const length = { A128GCM: 128, A192GCM: 192, A256GCM: 256 }[algorithm];
// Generate a random private key.
const privateKey = yield AesGcm.generateKey({ length });
// Set the `alg` property based on the specified algorithm.
privateKey.alg = algorithm;
return privateKey;
});
}
privateKeyToBytes({ privateKey }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the JWK to a byte array.
const privateKeyBytes = yield AesGcm.privateKeyToBytes({ privateKey });
return privateKeyBytes;
});
}
}
//# sourceMappingURL=aes-gcm.js.map
@@ -0,0 +1 @@
{"version":3,"file":"aes-gcm.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/algorithms/aes-gcm.ts"],"names":[],"mappings":";;;;;;;;;AAUA,OAAO,EAAE,MAAM,EAAuB,eAAe,EAAE,MAAM,cAAc,CAAC;AAqD5E;;;;;;;;GAQG;AACH,MAAM,OAAO,eAAgB,SAAQ,eAAe;IAKrC,iBAAiB,CAAC,EAAE,eAAe,EAA2B;;YACzE,mCAAmC;YACnC,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC;YAEvE,kDAAkD;YAClD,UAAU,CAAC,GAAG,GAAG,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;YAEzF,OAAO,UAAU,CAAC;QACpB,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACU,OAAO,CAAC,MACS;;YAE5B,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAEzC,OAAO,SAAS,CAAC;QACnB,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACU,OAAO,CAAC,MACS;;YAE5B,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE1C,OAAO,UAAU,CAAC;QACpB,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACU,WAAW,CAAC,EAAE,SAAS,EACX;;YAEvB,oCAAoC;YACpC,MAAM,MAAM,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAoB,CAAC;YAE1F,iCAAiC;YACjC,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;YAExD,2DAA2D;YAC3D,UAAU,CAAC,GAAG,GAAG,SAAS,CAAC;YAE3B,OAAO,UAAU,CAAC;QACpB,CAAC;KAAA;IAEY,iBAAiB,CAAC,EAAE,UAAU,EAA2B;;YACpE,mCAAmC;YACnC,MAAM,eAAe,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;YAEvE,OAAO,eAAe,CAAC;QACzB,CAAC;KAAA;CACF"}
@@ -0,0 +1,137 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { CryptoAlgorithm } from '@web5/crypto';
import { AesKw } from '../primitives/aes-kw.js';
/**
* The `AesKwAlgorithm` class provides a concrete implementation for cryptographic operations using
* the AES algorithm for key wrapping. This class implements both
* {@link KeyGenerator | `KeyGenerator`} and {@link KeyWrapper | `KeyWrapper`} interfaces, providing
* key generation, key wrapping, and key unwrapping features.
*
* This class is typically accessed through implementations that extend the
* {@link CryptoApi | `CryptoApi`} interface.
*/
export class AesKwAlgorithm extends CryptoAlgorithm {
bytesToPrivateKey({ privateKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the byte array to a JWK.
const privateKey = yield AesKw.bytesToPrivateKey({ privateKeyBytes });
// Set the `alg` property based on the key length.
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.
*/
generateKey({ algorithm }) {
return __awaiter(this, void 0, void 0, function* () {
// Map algorithm name to key length.
const length = { A128KW: 128, A192KW: 192, A256KW: 256 }[algorithm];
// Generate a random private key.
const privateKey = yield AesKw.generateKey({ length });
// Set the `alg` property based on the specified algorithm.
privateKey.alg = algorithm;
return privateKey;
});
}
privateKeyToBytes({ privateKey }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the JWK to a byte array.
const privateKeyBytes = yield 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.
*/
unwrapKey(params) {
return __awaiter(this, void 0, void 0, function* () {
const unwrappedKey = yield 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.
*/
wrapKey(params) {
return __awaiter(this, void 0, void 0, function* () {
const wrappedKeyBytes = AesKw.wrapKey(params);
return wrappedKeyBytes;
});
}
}
//# sourceMappingURL=aes-kw.js.map
@@ -0,0 +1 @@
{"version":3,"file":"aes-kw.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/algorithms/aes-kw.ts"],"names":[],"mappings":";;;;;;;;;AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAI/C,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAiBhD;;;;;;;;GAQG;AACH,MAAM,OAAO,cAAe,SAAQ,eAAe;IAKpC,iBAAiB,CAAC,EAAE,eAAe,EACS;;YAEvD,mCAAmC;YACnC,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,iBAAiB,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC;YAEtE,kDAAkD;YAClD,UAAU,CAAC,GAAG,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;YAEtF,OAAO,UAAU,CAAC;QACpB,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACU,WAAW,CAAC,EAAE,SAAS,EACZ;;YAEtB,oCAAoC;YACpC,MAAM,MAAM,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,SAAS,CAAoB,CAAC;YAEvF,iCAAiC;YACjC,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;YAEvD,2DAA2D;YAC3D,UAAU,CAAC,GAAG,GAAG,SAAS,CAAC;YAE3B,OAAO,UAAU,CAAC;QACpB,CAAC;KAAA;IAEY,iBAAiB,CAAC,EAAE,UAAU,EAClB;;YAEvB,mCAAmC;YACnC,MAAM,eAAe,GAAG,MAAM,KAAK,CAAC,iBAAiB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;YAEtE,OAAO,eAAe,CAAC;QACzB,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACU,SAAS,CAAC,MACN;;YAEf,MAAM,YAAY,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YAEnD,OAAO,YAAY,CAAC;QACtB,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACU,OAAO,CAAC,MACN;;YAEb,MAAM,eAAe,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE9C,OAAO,eAAe,CAAC;QACzB,CAAC;KAAA;CACF"}
@@ -0,0 +1,307 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { CryptoAlgorithm, isEcPrivateJwk, isEcPublicJwk, Secp256k1, Secp256r1 } from '@web5/crypto';
import { CryptoError, CryptoErrorCode } from '../crypto-error.js';
/**
* The `EcdsaAlgorithm` class provides a concrete implementation for cryptographic operations using
* the Elliptic Curve Digital Signature Algorithm (ECDSA). This class implements both
* {@link Signer | `Signer`} and { @link AsymmetricKeyGenerator | `AsymmetricKeyGenerator`}
* interfaces, providing private key generation, public key derivation, and creation/verification
* of signatures.
*
* This class is typically accessed through implementations that extend the
* {@link CryptoApi | `CryptoApi`} interface.
*/
export class EcdsaAlgorithm extends CryptoAlgorithm {
bytesToPrivateKey({ algorithm, privateKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
switch (algorithm) {
case 'ES256K':
case 'secp256k1': {
const privateKey = yield Secp256k1.bytesToPrivateKey({ privateKeyBytes });
privateKey.alg = 'EdDSA';
return privateKey;
}
case 'ES256':
case 'secp256r1': {
const privateKey = yield Secp256r1.bytesToPrivateKey({ privateKeyBytes });
privateKey.alg = 'EdDSA';
return privateKey;
}
default: {
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `Algorithm not supported: ${algorithm}`);
}
}
});
}
bytesToPublicKey({ algorithm, publicKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
switch (algorithm) {
case 'ES256K':
case 'secp256k1': {
const publicKey = yield Secp256k1.bytesToPublicKey({ publicKeyBytes });
publicKey.alg = 'EdDSA';
return publicKey;
}
case 'ES256':
case 'secp256r1': {
const publicKey = yield Secp256r1.bytesToPublicKey({ publicKeyBytes });
publicKey.alg = 'EdDSA';
return publicKey;
}
default: {
throw new CryptoError(CryptoErrorCode.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.
*/
computePublicKey({ key }) {
return __awaiter(this, void 0, void 0, function* () {
if (!isEcPrivateJwk(key))
throw new TypeError('Invalid key provided. Must be an elliptic curve (EC) private key.');
switch (key.crv) {
case 'secp256k1': {
const publicKey = yield Secp256k1.computePublicKey({ key });
publicKey.alg = 'ES256K';
return publicKey;
}
case 'P-256': {
const publicKey = yield 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.
*/
generateKey({ algorithm }) {
return __awaiter(this, void 0, void 0, function* () {
switch (algorithm) {
case 'ES256K':
case 'secp256k1': {
const privateKey = yield Secp256k1.generateKey();
privateKey.alg = 'ES256K';
return privateKey;
}
case 'ES256':
case 'secp256r1': {
const privateKey = yield 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.
*/
getPublicKey({ key }) {
return __awaiter(this, void 0, void 0, function* () {
if (!isEcPrivateJwk(key))
throw new TypeError('Invalid key provided. Must be an elliptic curve (EC) private key.');
switch (key.crv) {
case 'secp256k1': {
const publicKey = yield Secp256k1.getPublicKey({ key });
publicKey.alg = 'ES256K';
return publicKey;
}
case 'P-256': {
const publicKey = yield Secp256r1.getPublicKey({ key });
publicKey.alg = 'ES256';
return publicKey;
}
default: {
throw new Error(`Unsupported curve: ${key.crv}`);
}
}
});
}
privateKeyToBytes({ privateKey }) {
return __awaiter(this, void 0, void 0, function* () {
switch (privateKey.crv) {
case 'secp256k1': {
return yield Secp256k1.privateKeyToBytes({ privateKey });
}
case 'P-256': {
return yield Secp256r1.privateKeyToBytes({ privateKey });
}
default: {
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `Curve not supported: ${privateKey.crv}`);
}
}
});
}
publicKeyToBytes({ publicKey }) {
return __awaiter(this, void 0, void 0, function* () {
switch (publicKey.crv) {
case 'secp256k1': {
return yield Secp256k1.publicKeyToBytes({ publicKey });
}
case 'P-256': {
return yield Secp256r1.publicKeyToBytes({ publicKey });
}
default: {
throw new CryptoError(CryptoErrorCode.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`.
*/
sign({ key, data }) {
return __awaiter(this, void 0, void 0, function* () {
if (!isEcPrivateJwk(key))
throw new TypeError('Invalid key provided. Must be an elliptic curve (EC) private key.');
switch (key.crv) {
case 'secp256k1': {
return yield Secp256k1.sign({ key, data });
}
case 'P-256': {
return yield 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.
*/
verify({ key, signature, data }) {
return __awaiter(this, void 0, void 0, function* () {
if (!isEcPublicJwk(key))
throw new TypeError('Invalid key provided. Must be an elliptic curve (EC) public key.');
switch (key.crv) {
case 'secp256k1': {
return yield Secp256k1.verify({ key, signature, data });
}
case 'P-256': {
return yield Secp256r1.verify({ key, signature, data });
}
default: {
throw new Error(`Unsupported curve: ${key.crv}`);
}
}
});
}
}
//# sourceMappingURL=ecdsa.js.map
@@ -0,0 +1 @@
{"version":3,"file":"ecdsa.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/algorithms/ecdsa.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,264 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { CryptoAlgorithm, Ed25519, isOkpPrivateJwk, isOkpPublicJwk } from '@web5/crypto';
import { CryptoError, CryptoErrorCode } from '../crypto-error.js';
/**
* The `EdDsaAlgorithm` class provides a concrete implementation for cryptographic operations using
* the Edwards-curve Digital Signature Algorithm (EdDSA). This class implements both
* {@link Signer | `Signer`} and { @link AsymmetricKeyGenerator | `AsymmetricKeyGenerator`}
* interfaces, providing private key generation, public key derivation, and creation/verification
* of signatures.
*
* This class is typically accessed through implementations that extend the
* {@link CryptoApi | `CryptoApi`} interface.
*/
export class EdDsaAlgorithm extends CryptoAlgorithm {
bytesToPrivateKey({ algorithm, privateKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
switch (algorithm) {
case 'Ed25519': {
const privateKey = yield Ed25519.bytesToPrivateKey({ privateKeyBytes });
privateKey.alg = 'EdDSA';
return privateKey;
}
default: {
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `Algorithm not supported: ${algorithm}`);
}
}
});
}
bytesToPublicKey({ algorithm, publicKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
switch (algorithm) {
case 'Ed25519': {
const publicKey = yield Ed25519.bytesToPublicKey({ publicKeyBytes });
publicKey.alg = 'EdDSA';
return publicKey;
}
default: {
throw new CryptoError(CryptoErrorCode.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.
*/
computePublicKey({ key }) {
return __awaiter(this, void 0, void 0, function* () {
if (!isOkpPrivateJwk(key))
throw new TypeError('Invalid key provided. Must be an octet key pair (OKP) private key.');
switch (key.crv) {
case 'Ed25519': {
const publicKey = yield Ed25519.computePublicKey({ key });
publicKey.alg = 'EdDSA';
return publicKey;
}
default: {
throw new CryptoError(CryptoErrorCode.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.
*/
generateKey({ algorithm }) {
return __awaiter(this, void 0, void 0, function* () {
switch (algorithm) {
case 'Ed25519': {
const privateKey = yield 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.
*/
getPublicKey({ key }) {
return __awaiter(this, void 0, void 0, function* () {
if (!isOkpPrivateJwk(key))
throw new TypeError('Invalid key provided. Must be an octet key pair (OKP) private key.');
switch (key.crv) {
case 'Ed25519': {
const publicKey = yield Ed25519.getPublicKey({ key });
publicKey.alg = 'EdDSA';
return publicKey;
}
default: {
throw new Error(`Unsupported curve: ${key.crv}`);
}
}
});
}
privateKeyToBytes({ privateKey }) {
return __awaiter(this, void 0, void 0, function* () {
switch (privateKey.crv) {
case 'Ed25519': {
return yield Ed25519.privateKeyToBytes({ privateKey });
}
default: {
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `Curve not supported: ${privateKey.crv}`);
}
}
});
}
publicKeyToBytes({ publicKey }) {
return __awaiter(this, void 0, void 0, function* () {
switch (publicKey.crv) {
case 'Ed25519': {
return yield Ed25519.publicKeyToBytes({ publicKey });
}
default: {
throw new CryptoError(CryptoErrorCode.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`.
*/
sign({ key, data }) {
return __awaiter(this, void 0, void 0, function* () {
if (!isOkpPrivateJwk(key))
throw new TypeError('Invalid key provided. Must be an octet key pair (OKP) private key.');
switch (key.crv) {
case 'Ed25519': {
return yield Ed25519.sign({ key, data });
}
default: {
throw new CryptoError(CryptoErrorCode.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.
*/
verify({ key, signature, data }) {
return __awaiter(this, void 0, void 0, function* () {
if (!isOkpPublicJwk(key))
throw new TypeError('Invalid key provided. Must be an octet key pair (OKP) public key.');
switch (key.crv) {
case 'Ed25519': {
return yield Ed25519.verify({ key, signature, data });
}
default: {
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `Curve not supported: ${key.crv}`);
}
}
});
}
}
//# sourceMappingURL=eddsa.js.map
@@ -0,0 +1 @@
{"version":3,"file":"eddsa.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/algorithms/eddsa.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import { CryptoAlgorithm } from '@web5/crypto';
import { Hkdf } from '../primitives/hkdf.js';
export class HkdfAlgorithm extends CryptoAlgorithm {
deriveKeyBytes(_a) {
var { algorithm } = _a, params = __rest(_a, ["algorithm"]);
return __awaiter(this, void 0, void 0, function* () {
// Map algorithm name to hash function.
const hash = {
'HKDF-256': 'SHA-256',
'HKDF-384': 'SHA-384',
'HKDF-512': 'SHA-512'
}[algorithm];
// Derive a cryptographic byte array using HKDF.
const derivedKeyBytes = yield Hkdf.deriveKeyBytes(Object.assign(Object.assign({}, params), { hash }));
return derivedKeyBytes;
});
}
}
//# sourceMappingURL=hkdf.js.map
@@ -0,0 +1 @@
{"version":3,"file":"hkdf.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/algorithms/hkdf.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAI/C,OAAO,EAAE,IAAI,EAAc,MAAM,uBAAuB,CAAC;AAiBzD,MAAM,OAAO,aAAc,SAAQ,eAAe;IAGnC,cAAc,CAAC,EACyB;YADzB,EAAE,SAAS,OACc,EADT,MAAM,cAAtB,aAAwB,CAAF;;YAGhD,uCAAuC;YACvC,MAAM,IAAI,GAAG;gBACX,UAAU,EAAG,SAAkB;gBAC/B,UAAU,EAAG,SAAkB;gBAC/B,UAAU,EAAG,SAAkB;aAChC,CAAC,SAAS,CAAC,CAAC;YAEb,gDAAgD;YAChD,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,cAAc,iCAAM,MAAM,KAAE,IAAI,IAAG,CAAC;YAEvE,OAAO,eAAe,CAAC;;KACxB;CACF"}
@@ -0,0 +1,41 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import { CryptoAlgorithm } from '@web5/crypto';
import { Pbkdf2 } from '../primitives/pbkdf2.js';
export class Pbkdf2Algorithm extends CryptoAlgorithm {
deriveKeyBytes(_a) {
var { algorithm } = _a, params = __rest(_a, ["algorithm"]);
return __awaiter(this, void 0, void 0, function* () {
// Extract the hash function component of the `algorithm` parameter.
const [, hashFunction] = algorithm.split(/[-+]/);
// Map from JOSE algorithm name to "SHA" hash function identifier.
const hash = {
'HS256': 'SHA-256',
'HS384': 'SHA-384',
'HS512': 'SHA-512'
}[hashFunction];
// Derive a cryptographic byte array using PBKDF2.
const derivedKeyBytes = yield Pbkdf2.deriveKeyBytes(Object.assign(Object.assign({}, params), { hash }));
return derivedKeyBytes;
});
}
}
//# sourceMappingURL=pbkdf2.js.map
@@ -0,0 +1 @@
{"version":3,"file":"pbkdf2.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/algorithms/pbkdf2.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAM/C,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAgBjD,MAAM,OAAO,eAAgB,SAAQ,eAAe;IAGrC,cAAc,CAAC,EAC6B;YAD7B,EAAE,SAAS,OACkB,EADb,MAAM,cAAtB,aAAwB,CAAF;;YAGhD,oEAAoE;YACpE,MAAM,CAAC,EAAE,YAAY,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAEjD,kEAAkE;YAClE,MAAM,IAAI,GAAG;gBACX,OAAO,EAAG,SAAkB;gBAC5B,OAAO,EAAG,SAAkB;gBAC5B,OAAO,EAAG,SAAkB;aAC7B,CAAC,YAAY,CAAE,CAAC;YAEjB,kDAAkD;YAClD,MAAM,eAAe,GAAG,MAAM,MAAM,CAAC,cAAc,iCAAM,MAAM,KAAE,IAAI,IAAG,CAAC;YAEzE,OAAO,eAAe,CAAC;;KACxB;CACF"}
@@ -0,0 +1,41 @@
/**
* A custom error class for Crypto-related errors.
*/
export 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';
// Ensures that instanceof works properly, the correct prototype chain when using inheritance,
// and that V8 stack traces (like Chrome, Edge, and Node.js) are more readable and relevant.
Object.setPrototypeOf(this, new.target.prototype);
// Captures the stack trace in V8 engines (like Chrome, Edge, and Node.js).
// In non-V8 environments, the stack trace will still be captured.
if (Error.captureStackTrace) {
Error.captureStackTrace(this, CryptoError);
}
}
}
/**
* An enumeration of possible Crypto error codes.
*/
export var CryptoErrorCode;
(function (CryptoErrorCode) {
/** The supplied algorithm identifier is not supported by the implementation. */
CryptoErrorCode["AlgorithmNotSupported"] = "algorithmNotSupported";
/** The encoding operation (either encoding or decoding) failed. */
CryptoErrorCode["EncodingError"] = "encodingError";
/** The JWE supplied does not conform to valid syntax. */
CryptoErrorCode["InvalidJwe"] = "invalidJwe";
/** The JWK supplied does not conform to valid syntax. */
CryptoErrorCode["InvalidJwk"] = "invalidJwk";
/** The requested operation is not supported by the implementation. */
CryptoErrorCode["OperationNotSupported"] = "operationNotSupported";
})(CryptoErrorCode || (CryptoErrorCode = {}));
//# sourceMappingURL=crypto-error.js.map
@@ -0,0 +1 @@
{"version":3,"file":"crypto-error.js","sourceRoot":"","sources":["../../../../src/prototyping/crypto/crypto-error.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,WAAY,SAAQ,KAAK;IACpC;;;;;OAKG;IACH,YAAmB,IAAqB,EAAE,OAAe;QACvD,KAAK,CAAC,OAAO,CAAC,CAAC;QADE,SAAI,GAAJ,IAAI,CAAiB;QAEtC,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAE1B,8FAA8F;QAC9F,4FAA4F;QAC5F,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAElD,2EAA2E;QAC3E,kEAAkE;QAClE,IAAI,KAAK,CAAC,iBAAiB,EAAE;YAC3B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;SAC5C;IACH,CAAC;CACF;AAED;;GAEG;AACH,MAAM,CAAN,IAAY,eAeX;AAfD,WAAY,eAAe;IACzB,gFAAgF;IAChF,kEAA+C,CAAA;IAE/C,mEAAmE;IACnE,kDAA+B,CAAA;IAE/B,yDAAyD;IACzD,4CAAyB,CAAA;IAEzB,yDAAyD;IACzD,4CAAyB,CAAA;IAEzB,sEAAsE;IACtE,kEAA+C,CAAA;AACjD,CAAC,EAfW,eAAe,KAAf,eAAe,QAe1B"}
@@ -0,0 +1,236 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Sha2Algorithm, computeJwkThumbprint } from '@web5/crypto';
import { EcdsaAlgorithm } from './algorithms/ecdsa.js';
import { EdDsaAlgorithm } from './algorithms/eddsa.js';
import { CryptoError, CryptoErrorCode } from './crypto-error.js';
/**
* `supportedAlgorithms` is an object mapping algorithm names to their respective implementations
* Each entry in this map specifies the algorithm name and its associated properties, including the
* implementation class and any relevant names or identifiers for the algorithm. This structure
* allows for easy retrieval and instantiation of algorithm implementations based on the algorithm
* name or key specification. It facilitates the support of multiple algorithms within the
* `LocalKeyManager` class.
*/
const supportedAlgorithms = {
'Ed25519': {
implementation: EdDsaAlgorithm,
names: ['Ed25519'],
operations: ['bytesToPrivateKey', 'bytesToPublicKey', 'generateKey', 'sign', 'verify'],
},
'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: Sha2Algorithm,
names: ['SHA-256'],
operations: ['digest'],
}
};
export class Dsa {
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 = new Map();
}
bytesToPrivateKey({ algorithm: algorithmIdentifier, privateKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the given algorithm identifier.
const algorithm = this.getAlgorithmName({ algorithm: algorithmIdentifier });
// Get the key converter based on the algorithm name.
const keyConverter = this.getAlgorithm({ algorithm });
// Convert the byte array to a JWK.
const privateKey = yield keyConverter.bytesToPrivateKey({ algorithm: algorithmIdentifier, privateKeyBytes });
return privateKey;
});
}
bytesToPublicKey({ algorithm: algorithmIdentifier, publicKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the given algorithm identifier.
const algorithm = this.getAlgorithmName({ algorithm: algorithmIdentifier });
// Get the key converter based on the algorithm name.
const keyConverter = this.getAlgorithm({ algorithm });
// Convert the byte array to a JWK.
const publicKey = yield keyConverter.bytesToPublicKey({ algorithm: algorithmIdentifier, publicKeyBytes });
return publicKey;
});
}
/**
* 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 Dsa = new AgentDsa();
* const data = new Uint8Array([...]);
* const digest = await Dsa.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.
*/
digest({ algorithm, data }) {
return __awaiter(this, void 0, void 0, function* () {
// Get the hash function implementation based on the specified `algorithm` parameter.
const hasher = this.getAlgorithm({ algorithm });
// Compute the hash.
const hash = yield hasher.digest({ algorithm, data });
return hash;
});
}
generateKey(params) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the given algorithm identifier.
const algorithm = this.getAlgorithmName({ algorithm: params.algorithm });
// Get the key generator implementation based on the algorithm.
const keyGenerator = this.getAlgorithm({ algorithm });
// Generate the key.
const privateKey = yield keyGenerator.generateKey({ algorithm: params.algorithm });
// If the key ID is undefined, set it to the JWK thumbprint.
(_a = privateKey.kid) !== null && _a !== void 0 ? _a : (privateKey.kid = yield computeJwkThumbprint({ jwk: privateKey }));
return privateKey;
});
}
// ! TODO: Remove this once the `Dsa` interface is updated in @web5/crypto to remove KMS-specific methods.
getKeyUri(_params) {
return __awaiter(this, void 0, void 0, function* () {
throw new Error('Method not implemented.');
});
}
getPublicKey({ key }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the JWK's `alg` and `crv` properties.
const algorithm = this.getAlgorithmName({ key });
// Get the key generator based on the algorithm name.
const keyGenerator = this.getAlgorithm({ algorithm });
// Get the public key properties from the private JWK.
const publicKey = yield keyGenerator.getPublicKey({ key });
return publicKey;
});
}
privateKeyToBytes({ privateKey }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the JWK's `alg` property.
const algorithm = this.getAlgorithmName({ key: privateKey });
// Get the key converter based on the algorithm name.
const keyConverter = this.getAlgorithm({ algorithm });
// Convert the JWK to a byte array.
const privateKeyBytes = yield keyConverter.privateKeyToBytes({ privateKey });
return privateKeyBytes;
});
}
publicKeyToBytes({ publicKey }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the JWK's `alg` property.
const algorithm = this.getAlgorithmName({ key: publicKey });
// Get the key converter based on the algorithm name.
const keyConverter = this.getAlgorithm({ algorithm });
// Convert the JWK to a byte array.
const publicKeyBytes = yield keyConverter.publicKeyToBytes({ publicKey });
return publicKeyBytes;
});
}
sign({ key, data }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the JWK's `alg` and `crv` properties.
const algorithm = this.getAlgorithmName({ key });
// Get the signature algorithm based on the algorithm name.
const signer = this.getAlgorithm({ algorithm });
// Sign the data.
const signature = signer.sign({ data, key });
return signature;
});
}
verify({ key, signature, data }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the algorithm name based on the JWK's `alg` and `crv` properties.
const algorithm = this.getAlgorithmName({ key });
// Get the signature algorithm based on the algorithm name.
const signer = this.getAlgorithm({ algorithm });
// Verify the signature.
const isSignatureValid = signer.verify({ key, signature, data });
return isSignatureValid;
});
}
/**
* 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 }) {
var _a;
// Check if algorithm is supported.
const AlgorithmImplementation = (_a = supportedAlgorithms[algorithm]) === null || _a === void 0 ? void 0 : _a['implementation'];
if (!AlgorithmImplementation) {
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `Algorithm not supported: ${algorithm}`);
}
// Check if instance already exists for the `AlgorithmImplementation`.
if (!this._algorithmInstances.has(AlgorithmImplementation)) {
// If not, create a new instance and store it in the cache
this._algorithmInstances.set(AlgorithmImplementation, new AlgorithmImplementation());
}
// Return the cached instance
return this._algorithmInstances.get(AlgorithmImplementation);
}
getAlgorithmName({ algorithm, key }) {
var _a;
const algProperty = (_a = key === null || key === void 0 ? void 0 : key.alg) !== null && _a !== void 0 ? _a : algorithm;
const crvProperty = key === null || key === void 0 ? void 0 : 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(CryptoErrorCode.AlgorithmNotSupported, `Algorithm not supported based on provided input: alg=${algProperty}, crv=${crvProperty}. ` +
'Please check the documentation for the list of supported algorithms.');
}
}
//# sourceMappingURL=dsa.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,130 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { LocalKeyManager } from '@web5/crypto';
import { isValidJweHeader } from './jwe.js';
import { FlattenedJwe } from './jwe-flattened.js';
import { AgentCryptoApi } from '../../../crypto-api.js';
import { CryptoError, CryptoErrorCode } from '../crypto-error.js';
/**
* The `CompactJwe` class facilitates encryption and decryption processes using the JSON Web
* Encryption (JWE) Compact Serialization format. This class adheres to the specifications
* outlined in {@link https://datatracker.ietf.org/doc/html/rfc7516 | RFC 7516}, enabling secure
* data encapsulation through various cryptographic algorithms.
*
* Compact Serialization is a space-efficient representation of JWE, suitable for contexts
* where verbose data structures are impractical, such as HTTP headers. It provides mechanisms to
* encrypt content and protect its integrity with authenticated encryption, ensuring
* confidentiality, authenticity, and non-repudiation.
*
* This class supports the following operations:
* - Decrypting data from a compact serialized JWE string.
* - Encrypting data and producing a compact serialized JWE string.
*
* Usage involves specifying the cryptographic details, such as keys and algorithms, and the class
* handles the complexities of the JWE processing, including parsing, validating, and applying the
* cryptographic operations defined in the JWE specification.
*
* @example
* ```ts
* // Example usage of encrypt method
* const plaintext = new TextEncoder().encode("Secret Message");
* const key = { kty: "oct", k: "your-secret-key" }; // Example symmetric key
* const protectedHeader = { alg: "dir", enc: "A256GCM" };
* const encryptedJweString = await CompactJwe.encrypt({
* plaintext,
* protectedHeader,
* key,
* });
* console.log(encryptedJweString); // Outputs the JWE string in Compact Serialization format
* ```
*
* @example
* ```ts
* // Example usage of decrypt method
* const jweString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."; // A JWE in Compact Serialization
* const decryptionKey = { kty: "oct", k: "your-secret-key" }; // The key must match the one used for encryption
* const { plaintext, protectedHeader } = await CompactJwe.decrypt({
* jwe: jweString,
* key: decryptionKey,
* });
* console.log(new TextDecoder().decode(plaintext)); // Outputs the decrypted message
* ```
*/
export class CompactJwe {
/**
* 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 decrypt({ jwe, key, keyManager = new LocalKeyManager(), crypto = new AgentCryptoApi(), options = {} }) {
return __awaiter(this, void 0, void 0, function* () {
if (typeof jwe !== 'string') {
throw new CryptoError(CryptoErrorCode.InvalidJwe, 'Invalid JWE format. JWE must be a string.');
}
// Split the JWE into its constituent parts.
const { 0: protectedHeader, 1: encryptedKey, 2: initializationVector, 3: ciphertext, 4: authenticationTag, length, } = jwe.split('.');
// Ensure that the JWE has the required number of parts.
if (length !== 5) {
throw new CryptoError(CryptoErrorCode.InvalidJwe, 'Invalid JWE format. JWE must have 5 parts.');
}
// Decrypt the JWE using the provided Key URI.
const flattenedJwe = yield FlattenedJwe.decrypt({
jwe: {
ciphertext,
encrypted_key: encryptedKey || undefined,
iv: initializationVector || undefined,
protected: protectedHeader,
tag: authenticationTag || undefined,
},
key,
keyManager,
crypto,
options
});
if (!isValidJweHeader(flattenedJwe.protectedHeader)) {
throw new CryptoError(CryptoErrorCode.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 encrypt({ plaintext, protectedHeader, key, keyManager = new LocalKeyManager(), crypto = new AgentCryptoApi(), options = {} }) {
return __awaiter(this, void 0, void 0, function* () {
const jwe = yield FlattenedJwe.encrypt({ plaintext, protectedHeader, key, keyManager, crypto, options });
// Create the Compact Serialization, which is the string BASE64URL(UTF8(JWE Protected Header))
// || '.' || BASE64URL(JWE Encrypted Key) || '.' || BASE64URL(JWE Initialization Vector)
// || '.' || BASE64URL(JWE Ciphertext) || '.' || BASE64URL(JWE Authentication Tag).
return [jwe.protected, jwe.encrypted_key, jwe.iv, jwe.ciphertext, jwe.tag].join('.');
});
}
}
//# sourceMappingURL=jwe-compact.js.map
@@ -0,0 +1 @@
{"version":3,"file":"jwe-compact.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/jose/jwe-compact.ts"],"names":[],"mappings":";;;;;;;;;AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAM/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAoElE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,MAAM,OAAO,UAAU;IACrB;;;;;;;;;;;;;OAaG;IACI,MAAM,CAAO,OAAO,CAGzB,EACA,GAAG,EACH,GAAG,EACH,UAAU,GAAG,IAAI,eAAe,EAAE,EAClC,MAAM,GAAG,IAAI,cAAc,EAAE,EAC7B,OAAO,GAAG,EAAE,EACkC;;YAE9C,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE;gBAC3B,MAAM,IAAI,WAAW,CAAC,eAAe,CAAC,UAAU,EAAE,2CAA2C,CAAC,CAAC;aAChG;YAED,4CAA4C;YAC5C,MAAM,EACJ,CAAC,EAAE,eAAe,EAClB,CAAC,EAAE,YAAY,EACf,CAAC,EAAE,oBAAoB,EACvB,CAAC,EAAE,UAAU,EACb,CAAC,EAAE,iBAAiB,EACpB,MAAM,GACP,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAEnB,wDAAwD;YACxD,IAAI,MAAM,KAAK,CAAC,EAAE;gBAChB,MAAM,IAAI,WAAW,CAAC,eAAe,CAAC,UAAU,EAAE,4CAA4C,CAAC,CAAC;aACjG;YAED,8CAA8C;YAC9C,MAAM,YAAY,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC;gBAC9C,GAAG,EAAE;oBACH,UAAU;oBACV,aAAa,EAAG,YAAY,IAAI,SAAS;oBACzC,EAAE,EAAc,oBAAoB,IAAI,SAAS;oBACjD,SAAS,EAAO,eAAe;oBAC/B,GAAG,EAAa,iBAAiB,IAAI,SAAS;iBAC/C;gBACD,GAAG;gBACH,UAAU;gBACV,MAAM;gBACN,OAAO;aACR,CAAC,CAAC;YAEH,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,eAAe,CAAC,EAAE;gBACnD,MAAM,IAAI,WAAW,CAAC,eAAe,CAAC,UAAU,EAAE,2EAA2E,CAAC,CAAC;aAChI;YAED,OAAO,EAAE,SAAS,EAAE,YAAY,CAAC,SAAS,EAAE,eAAe,EAAE,YAAY,CAAC,eAAe,EAAE,CAAC;QAC9F,CAAC;KAAA;IAED;;;;;;;;;;;;;OAaG;IACI,MAAM,CAAO,OAAO,CAGzB,EACA,SAAS,EACT,eAAe,EACf,GAAG,EACH,UAAU,GAAG,IAAI,eAAe,EAAE,EAClC,MAAM,GAAG,IAAI,cAAc,EAAE,EAC7B,OAAO,GAAG,EAAE,EACkC;;YAE9C,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;YAEzG,8FAA8F;YAC9F,wFAAwF;YACxF,mFAAmF;YACnF,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACvF,CAAC;KAAA;CACF"}
@@ -0,0 +1,294 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Convert } from '@web5/common';
import { LocalKeyManager, utils as cryptoUtils } from '@web5/crypto';
import { isCipher } from '../utils.js';
import { AgentCryptoApi } from '../../../crypto-api.js';
import { JweKeyManagement, isValidJweHeader } from './jwe.js';
import { hasDuplicateProperties } from '../../common/object.js';
import { CryptoError, CryptoErrorCode } from '../crypto-error.js';
/**
* A helper utility function used internally to decode a JWE header parameter from a Base64 URL
* encoded string to a Uint8Array. It's designed to process individual JWE header parameter values,
* ensuring they are correctly formatted and decoded.
*
* @param param - The name of the JWE header parameter being decoded; used for error messaging.
* @param value - The Base64 URL encoded string value of the header parameter to decode.
* @returns The decoded parameter as a Uint8Array, or undefined if the input value is undefined.
* @throws {@link CryptoError} if the value is not a properly encoded Base64 URL string or if it's
* not a string.
*/
function decodeHeaderParam(param, value) {
// If the parameter value is not present, return undefined.
if (value === undefined)
return undefined;
try {
if (typeof value !== 'string')
throw new Error();
return Convert.base64Url(value).toUint8Array();
}
catch (_a) {
throw new CryptoError(CryptoErrorCode.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.');
}
}
/**
* The `FlattenedJwe` class handles the encryption and decryption of JSON Web Encryption (JWE)
* objects in the flattened serialization format. This format is a compact, URL-safe means of
* representing encrypted content, typically used when dealing with a single recipient or when
* bandwidth efficiency is important.
*
* This class provides methods to encrypt plaintext to a flattened JWE and decrypt a flattened JWE
* back to plaintext, utilizing a variety of supported cryptographic algorithms as specified in the
* JWE header parameters.
*
* @example
* ```ts
* // Example usage of encrypt method
* const plaintext = new TextEncoder().encode("Secret Message");
* const key = { kty: "oct", k: "your-secret-key" }; // Example symmetric key
* const protectedHeader = { alg: "dir", enc: "A256GCM" };
* const encryptedJwe = await FlattenedJwe.encrypt({
* plaintext,
* protectedHeader,
* key,
* });
* ```
*
* @example
* // Decryption example
* const { plaintext, protectedHeader } = await FlattenedJwe.decrypt({
* jwe: yourFlattenedJweObject,
* key: yourDecryptionKey,
* crypto: new YourCryptoApi(),
* });
*/
export class FlattenedJwe {
constructor(params) {
/** Base64URL encoded ciphertext. */
this.ciphertext = '';
Object.assign(this, params);
}
static decrypt({ jwe, key, keyManager = new LocalKeyManager(), crypto = new AgentCryptoApi(), options = {} }) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
// Verify that the provided Crypto API supports the decrypt operation before proceeding.
if (!isCipher(crypto)) {
throw new CryptoError(CryptoErrorCode.OperationNotSupported, 'Crypto API does not support the "encrypt" operation.');
}
// Verify that the provided Key Manager supports the decrypt operation before proceeding.
if (!isCipher(keyManager)) {
throw new CryptoError(CryptoErrorCode.OperationNotSupported, 'Key Manager does not support the "decrypt" operation.');
}
// Verify that at least one of the JOSE header objects is present.
if (!jwe.protected && !jwe.header && !jwe.unprotected) {
throw new CryptoError(CryptoErrorCode.InvalidJwe, 'JWE is missing the required JOSE header parameters. ' +
'Please provide at least one of the following: "protected", "header", or "unprotected"');
}
// Verify that the JWE Ciphertext is present.
if (typeof jwe.ciphertext !== 'string') {
throw new CryptoError(CryptoErrorCode.InvalidJwe, 'JWE Ciphertext is missing or not a string.');
}
// Parse the JWE Protected Header, if present.
let parsedProtectedHeader;
if (jwe.protected) {
try {
parsedProtectedHeader = Convert.base64Url(jwe.protected).toObject();
}
catch (_c) {
throw new Error('JWE Protected Header is invalid');
}
}
// Per {@link https://www.rfc-editor.org/rfc/rfc7516#section-5.2 | RFC7516 Section 5.2}
// the resulting JOSE Header MUST NOT contain duplicate Header Parameter names. In other words,
// the same Header Parameter name MUST NOT occur in the `header`, `protected`, and
// `unprotected` JSON object values that together comprise the JOSE Header.
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.');
}
// The JOSE Header is the union of the members of the JWE Protected Header (`protected`), the
// JWE Shared Unprotected Header (`unprotected`), and the corresponding JWE Per-Recipient
// Unprotected Header (`header`).
const joseHeader = Object.assign(Object.assign(Object.assign({}, 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
? Convert.base64Url(jwe.encrypted_key).toUint8Array()
: undefined;
cek = yield JweKeyManagement.decrypt({ key, encryptedKey, joseHeader, keyManager, crypto });
}
catch (error) {
// If the error is a CryptoError with code "InvalidJwe" or "AlgorithmNotSupported", re-throw.
if (error instanceof CryptoError
&& (error.code === CryptoErrorCode.InvalidJwe || error.code === CryptoErrorCode.AlgorithmNotSupported)) {
throw error;
}
// Otherwise, generate a random CEK and proceed to the next step.
// As noted in
// {@link https://datatracker.ietf.org/doc/html/rfc7516#section-11.5 | RFC 7516 Section 11.5},
// to mitigate the attacks described in
// {@link https://datatracker.ietf.org/doc/html/rfc3218 | RFC 3218}, the recipient MUST NOT
// distinguish between format, padding, and length errors of encrypted keys. It is strongly
// recommended, in the event of receiving an improperly formatted key, that the recipient
// substitute a randomly generated CEK and proceed to the next step, to mitigate timing
// attacks.
cek = typeof key === 'string'
? yield keyManager.generateKey({ algorithm: joseHeader.enc })
: yield crypto.generateKey({ algorithm: joseHeader.enc });
}
// If present, decode the JWE Initialization Vector (IV) and Authentication Tag.
const iv = decodeHeaderParam('iv', jwe.iv);
const tag = decodeHeaderParam('tag', jwe.tag);
// Decode the JWE Ciphertext to a byte array, and if present, append the Authentication Tag.
const ciphertext = tag !== undefined
? new Uint8Array([
...Convert.base64Url(jwe.ciphertext).toUint8Array(),
...(tag !== null && tag !== void 0 ? tag : [])
])
: Convert.base64Url(jwe.ciphertext).toUint8Array();
// If the JWE Additional Authenticated Data (AAD) is present, the Additional Authenticated Data
// input to the Content Encryption Algorithm is
// ASCII(Encoded Protected Header || '.' || BASE64URL(JWE AAD)). If the JWE AAD is absent, the
// Additional Authenticated Data is ASCII(BASE64URL(UTF8(JWE Protected Header))).
const additionalData = jwe.aad !== undefined
? new Uint8Array([
...Convert.string((_a = jwe.protected) !== null && _a !== void 0 ? _a : '').toUint8Array(),
...Convert.string('.').toUint8Array(),
...Convert.string(jwe.aad).toUint8Array()
])
: Convert.string((_b = jwe.protected) !== null && _b !== void 0 ? _b : '').toUint8Array();
// Decrypt the JWE using the Content Encryption Key (CEK) with:
// - Key Manager: If the CEK is a Key Identifier.
// - Crypto API: If the CEK is a JWK.
const plaintext = typeof cek === 'string'
? yield keyManager.decrypt({ keyUri: cek, data: ciphertext, iv, additionalData })
: yield crypto.decrypt({ key: cek, data: ciphertext, iv, additionalData });
return {
plaintext,
protectedHeader: parsedProtectedHeader,
additionalAuthenticatedData: decodeHeaderParam('aad', jwe.aad),
sharedUnprotectedHeader: jwe.unprotected,
unprotectedHeader: jwe.header
};
});
}
static encrypt({ key, plaintext, additionalAuthenticatedData, protectedHeader, sharedUnprotectedHeader, unprotectedHeader, keyManager = new LocalKeyManager(), crypto = new AgentCryptoApi(), }) {
return __awaiter(this, void 0, void 0, function* () {
// Verify that the provided Crypto API supports the decrypt operation before proceeding.
if (!isCipher(crypto)) {
throw new CryptoError(CryptoErrorCode.OperationNotSupported, 'Crypto API does not support the "encrypt" operation.');
}
// Verify that the provided Key Manager supports the decrypt operation before proceeding.
if (!isCipher(keyManager)) {
throw new CryptoError(CryptoErrorCode.OperationNotSupported, 'Key Manager does not support the "decrypt" operation.');
}
// Verify that at least one of the JOSE header objects is present.
if (!protectedHeader && !sharedUnprotectedHeader && !unprotectedHeader) {
throw new CryptoError(CryptoErrorCode.InvalidJwe, 'JWE is missing the required JOSE header parameters. ' +
'Please provide at least one of the following: "protectedHeader", "sharedUnprotectedHeader", or "unprotectedHeader"');
}
// Verify that the Plaintext is present.
if (!(plaintext instanceof Uint8Array)) {
throw new CryptoError(CryptoErrorCode.InvalidJwe, 'Plaintext is missing or not a byte array.');
}
// Per {@link https://www.rfc-editor.org/rfc/rfc7516#section-5.2 | RFC7516 Section 5.2}
// the resulting JOSE Header MUST NOT contain duplicate Header Parameter names. In other words,
// the same Header Parameter name MUST NOT occur in the `header`, `protected`, and
// `unprotected` JSON object values that together comprise the JOSE Header.
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.');
}
// The JOSE Header is the union of the members of the JWE Protected Header (`protectedHeader`),
// the JWE Shared Unprotected Header (`sharedUnprotectedHeader`), and the corresponding JWE
// Per-Recipient Unprotected Header (`unprotectedHeader`).
const joseHeader = Object.assign(Object.assign(Object.assign({}, 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 } = yield JweKeyManagement.encrypt({ key, joseHeader, keyManager, crypto });
// If required for the Content Encryption Algorithm, generate a random JWE Initialization
// Vector (IV) of the correct size; otherwise, let the JWE Initialization Vector be the empty
// octet sequence.
let iv;
switch (joseHeader.enc) {
case 'A128GCM':
case 'A192GCM':
case 'A256GCM':
iv = cryptoUtils.randomBytes(12);
break;
default:
iv = new Uint8Array(0);
}
// Compute the Encoded Protected Header value BASE64URL(UTF8(JWE Protected Header)). If the JWE
// Protected Header is not present, let this value be the empty string.
const encodedProtectedHeader = protectedHeader
? Convert.object(protectedHeader).toBase64Url()
: '';
// If the JWE Additional Authenticated Data (AAD) is present, the Additional Authenticated Data
// input to the Content Encryption Algorithm is
// ASCII(Encoded Protected Header || '.' || BASE64URL(JWE AAD)). If the JWE AAD is absent, the
// Additional Authenticated Data is ASCII(BASE64URL(UTF8(JWE Protected Header))).
let additionalData;
let encodedAad;
if (additionalAuthenticatedData) {
encodedAad = Convert.uint8Array(additionalAuthenticatedData).toBase64Url();
additionalData = Convert.string(encodedProtectedHeader + '.' + encodedAad).toUint8Array();
}
else {
additionalData = Convert.string(encodedProtectedHeader).toUint8Array();
}
// Encrypt the plaintext using the CEK, the JWE Initialization Vector, and the Additional
// Authenticated Data value using the specified content encryption algorithm to create the JWE
// Ciphertext value and the JWE Authentication Tag.
const ciphertextWithTag = typeof cek === 'string'
? yield keyManager.encrypt({ keyUri: cek, data: plaintext, iv, additionalData })
: yield crypto.encrypt({ key: cek, data: plaintext, iv, additionalData });
const ciphertext = ciphertextWithTag.slice(0, -16);
const authenticationTag = ciphertextWithTag.slice(-16);
// Create the Flattened JWE JSON Serialization output, which is based upon the General syntax,
// but flattens it, optimizing it for the single-recipient case. It flattens it by removing the
// "recipients" member and instead placing those members defined for use in the "recipients"
// array (the "header" and "encrypted_key" members) in the top-level JSON object (at the same
// level as the "ciphertext" member).
const jwe = new FlattenedJwe({
ciphertext: Convert.uint8Array(ciphertext).toBase64Url(),
});
if (encryptedKey)
jwe.encrypted_key = Convert.uint8Array(encryptedKey).toBase64Url();
if (protectedHeader)
jwe.protected = encodedProtectedHeader;
if (sharedUnprotectedHeader)
jwe.unprotected = sharedUnprotectedHeader;
if (unprotectedHeader)
jwe.header = unprotectedHeader;
if (iv)
jwe.iv = Convert.uint8Array(iv).toBase64Url();
if (encodedAad)
jwe.aad = encodedAad;
if (authenticationTag)
jwe.tag = Convert.uint8Array(authenticationTag).toBase64Url();
return jwe;
});
}
}
//# sourceMappingURL=jwe-flattened.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,308 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Convert } from '@web5/common';
import { CryptoError, CryptoErrorCode } from '../crypto-error.js';
/**
* Checks if the provided object is a valid JWE (JSON Web Encryption) header.
*
* This function evaluates whether the given object adheres to the structure expected for
* a JWE header, specifically looking for the presence and proper format of the "alg" (algorithm)
* and "enc" (encryption algorithm) properties, which are essential for defining the JWE's
* cryptographic operations.
*
* @example
* ```ts
* const header = {
* alg: 'dir',
* enc: 'A256GCM'
* };
*
* if (isValidJweHeader(header)) {
* console.log('The object is a valid JWE header.');
* } else {
* console.log('The object is not a valid JWE header.');
* }
* ```
*
* @param obj - The object to be validated as a JWE header.
* @returns Returns `true` if the object is a valid JWE header, otherwise `false`.
*/
export function isValidJweHeader(obj) {
return typeof obj === 'object' && obj !== null
&& 'alg' in obj && obj.alg !== undefined
&& 'enc' in obj && obj.enc !== undefined;
}
/**
* The `JweKeyManagement` class implements the key management aspects of JSON Web Encryption (JWE)
* as specified in {@link https://datatracker.ietf.org/doc/html/rfc7516 | RFC 7516}.
*
* It supports algorithms for encrypting and decrypting keys, thereby enabling the secure
* transmission of information where the payload is encrypted, and the encryption key is also
* encrypted or agreed upon using key agreement techniques.
*
* The choice of algorithm is determined by the "alg" parameter in the JWE
* header, and the class is designed to handle the intricacies associated with each algorithm,
* ensuring the secure handling of the encryption keys.
*
* Supported algorithms include:
* - `"dir"`: Direct Encryption Mode
* - `"PBES2-HS256+A128KW"`, `"PBES2-HS384+A192KW"`, `"PBES2-HS512+A256KW"`: Password-Based
* Encryption Mode with Key Wrapping (PBES2) using HMAC-SHA and AES Key Wrap algorithms for key
* wrapping and encryption.
*
* @example
* // To encrypt a key:
* const keyEncryptionKey = Convert.string(passphrase).toUint8Array()
* const { cek, encryptedKey: encryptedCek } = await JweKeyManagement.encrypt({
* key: keyEncryptionKey,
* joseHeader: {
* alg: 'PBES2-HS512+A256KW',
* enc: 'A256GCM',
* p2c : 210_000,
p2s : Convert.uint8Array(saltInput).toBase64Url()
* },
* crypto: new AgentCryptoApi(),
* });
*
* // To decrypt a key:
* const cek = await JweKeyManagement.decrypt({
* key: keyEncryptionKey,
* encryptedKey: encryptedCek,
* joseHeader: {
* alg: 'PBES2-HS512+A256KW',
* enc: 'A256GCM',
* p2c : 210_000,
p2s : Convert.uint8Array(saltInput).toBase64Url()
* },
* crypto: new AgentCryptoApi(),
* });
*/
export class JweKeyManagement {
/**
* 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 decrypt({ key, encryptedKey, joseHeader, crypto }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the Key Management Mode employed by the algorithm specified by the "alg"
// (algorithm) Header Parameter.
switch (joseHeader.alg) {
case 'dir': {
// In Direct Encryption mode, a JWE "Encrypted Key" is not provided. Instead, the
// provided key management `key` is directly used as the Content Encryption Key (CEK) to
// decrypt the JWE payload.
// Verify that the JWE Encrypted Key value is empty.
if (encryptedKey !== undefined) {
throw new CryptoError(CryptoErrorCode.InvalidJwe, 'JWE "encrypted_key" is not allowed when using "dir" (Direct Encryption Mode).');
}
// Verify the key management `key` is a Key Identifier or JWK.
if (key instanceof Uint8Array) {
throw new CryptoError(CryptoErrorCode.InvalidJwe, 'Key management "key" must be a Key URI or JWK when using "dir" (Direct Encryption Mode).');
}
// return the key management `key` as the CEK.
return key;
}
case 'PBES2-HS256+A128KW':
case 'PBES2-HS384+A192KW':
case 'PBES2-HS512+A256KW': {
// In Key Encryption mode (PBES2) with key wrapping (A128KW, A192KW, A256KW), the given
// passphrase, salt (p2s), and iteration count (p2c) are used with the PBKDF2 key derivation
// function to derive the Key Encryption Key (KEK). The KEK is then used to decrypt the JWE
// Encrypted Key to obtain the Content Encryption Key (CEK).
if (typeof joseHeader.p2c !== 'number') {
throw new CryptoError(CryptoErrorCode.InvalidJwe, 'JOSE Header "p2c" (PBES2 Count) is missing or not a number.');
}
if (typeof joseHeader.p2s !== 'string') {
throw new CryptoError(CryptoErrorCode.InvalidJwe, 'JOSE Header "p2s" (PBES2 salt) is missing or not a string.');
}
// Throw an error if the key management `key` is not a byte array. For PBES2, the key is
// expected to be a low-entropy passphrase as a byte array.
if (!(key instanceof Uint8Array)) {
throw new CryptoError(CryptoErrorCode.InvalidJwe, 'Key management "key" must be a Uint8Array when using "PBES2" (Key Encryption Mode).');
}
// Verify that the JWE Encrypted Key value is present.
if (encryptedKey === undefined) {
throw new CryptoError(CryptoErrorCode.InvalidJwe, 'JWE "encrypted_key" is required when using "PBES2" (Key Encryption Mode).');
}
// Per {@link https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.1 | RFC 7518, Section 4.8.1.1},
// the salt value used with PBES2 should be of the format (UTF8(Alg) || 0x00 || Salt Input),
// where Alg is the "alg" (algorithm) Header Parameter value. This reduces the potential for
// a precomputed dictionary attack (also known as a rainbow table attack).
let salt;
try {
salt = new Uint8Array([
...Convert.string(joseHeader.alg).toUint8Array(),
0x00,
...Convert.base64Url(joseHeader.p2s).toUint8Array()
]);
}
catch (_a) {
throw new CryptoError(CryptoErrorCode.EncodingError, 'Failed to decode the JOSE Header "p2s" (PBES2 salt) value.');
}
// Derive the Key Encryption Key (KEK) from the given passphrase, salt, and iteration count.
const kek = yield crypto.deriveKey({
algorithm: joseHeader.alg,
baseKeyBytes: key,
iterations: joseHeader.p2c,
salt
});
if (!(kek.alg && ['A128KW', 'A192KW', 'A256KW'].includes(kek.alg))) {
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `Unsupported Key Encryption Algorithm (alg) value: ${kek.alg}`);
}
// Decrypt the Content Encryption Key (CEK) with the derived KEK.
return yield crypto.unwrapKey({
decryptionKey: kek,
wrappedKeyBytes: encryptedKey,
wrappedKeyAlgorithm: joseHeader.enc
});
}
default: {
throw new CryptoError(CryptoErrorCode.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 encrypt({ key, joseHeader, crypto }) {
return __awaiter(this, void 0, void 0, function* () {
let cek;
let encryptedKey;
// Determine the Key Management Mode employed by the algorithm specified by the "alg"
// (algorithm) Header Parameter.
switch (joseHeader.alg) {
case 'dir': {
// In Direct Encryption mode (dir), a JWE "Encrypted Key" is not provided. Instead, the
// provided key management `key` is directly used as the Content Encryption Key (CEK) to
// decrypt the JWE payload.
// Verify that the JWE Encrypted Key value is empty.
if (encryptedKey !== undefined) {
throw new CryptoError(CryptoErrorCode.InvalidJwe, 'JWE "encrypted_key" is not allowed when using "dir" (Direct Encryption Mode).');
}
// Verify the key management `key` is a Key Identifier or JWK.
if (key instanceof Uint8Array) {
throw new CryptoError(CryptoErrorCode.InvalidJwe, 'Key management "key" must be a Key URI or JWK when using "dir" (Direct Encryption Mode).');
}
// Set the CEK to the key management `key`.
cek = key;
break;
}
case 'PBES2-HS256+A128KW':
case 'PBES2-HS384+A192KW':
case 'PBES2-HS512+A256KW': {
// In Key Encryption mode (PBES2) with key wrapping (A128KW, A192KW, A256KW), a randomly
// generated Content Encryption Key (CEK) is encrypted with a Key Encryption Key (KEK)
// derived from the given passphrase, salt (p2s), and iteration count (p2c) using the
// PBKDF2 key derivation function.
if (typeof joseHeader.p2c !== 'number') {
throw new CryptoError(CryptoErrorCode.InvalidJwe, 'JOSE Header "p2c" (PBES2 Count) is missing or not a number.');
}
if (typeof joseHeader.p2s !== 'string') {
throw new CryptoError(CryptoErrorCode.InvalidJwe, 'JOSE Header "p2s" (PBES2 salt) is missing or not a string.');
}
// Throw an error if the key management `key` is not a byte array.
if (!(key instanceof Uint8Array)) {
throw new CryptoError(CryptoErrorCode.InvalidJwe, 'Key management "key" must be a Uint8Array when using "PBES2" (Key Encryption Mode).');
}
// Generate a random Content Encryption Key (CEK) using the algorithm specified by the "enc"
// (encryption) Header Parameter.
cek = yield crypto.generateKey({ algorithm: joseHeader.enc });
// Per {@link https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.1 | RFC 7518, Section 4.8.1.1},
// the salt value used with PBES2 should be of the format (UTF8(Alg) || 0x00 || Salt Input),
// where Alg is the "alg" (algorithm) Header Parameter value. This reduces the potential for
// a precomputed dictionary attack (also known as a rainbow table attack).
let salt;
try {
salt = new Uint8Array([
...Convert.string(joseHeader.alg).toUint8Array(),
0x00,
...Convert.base64Url(joseHeader.p2s).toUint8Array()
]);
}
catch (_a) {
throw new CryptoError(CryptoErrorCode.EncodingError, 'Failed to decode the JOSE Header "p2s" (PBES2 salt) value.');
}
// Derive a Key Encryption Key (KEK) from the given passphrase, salt, and iteration count.
const kek = yield crypto.deriveKey({
algorithm: joseHeader.alg,
baseKeyBytes: key,
iterations: joseHeader.p2c,
salt
});
// Encrypt the randomly generated CEK with the derived Key Encryption Key (KEK).
encryptedKey = yield crypto.wrapKey({ encryptionKey: kek, unwrappedKey: cek });
break;
}
default: {
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `Unsupported "alg" (Algorithm) Header Parameter value: ${joseHeader.alg}`);
}
}
return { cek, encryptedKey };
});
}
}
//# sourceMappingURL=jwe.js.map
@@ -0,0 +1 @@
{"version":3,"file":"jwe.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/jose/jwe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,352 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import { Convert } from '@web5/common';
import { getWebcryptoSubtle } from '@noble/ciphers/webcrypto/utils';
import { computeJwkThumbprint, isOctPrivateJwk } from '@web5/crypto';
/**
* Const defining the AES-GCM initialization vector (IV) length in bits.
*
* @remarks
* NIST Special Publication 800-38D, Section 5.2.1.1 states that the IV length:
* > For IVs, it is recommended that implementations restrict support to the length of 96 bits, to
* > promote interoperability, efficiency, and simplicity of design.
*
* This implementation does not support IV lengths that are different from the value defined by
* this constant.
*
* @see {@link https://doi.org/10.6028/NIST.SP.800-38D | NIST SP 800-38D}
*/
const AES_GCM_IV_LENGTH = 96;
/**
* Constant defining the AES key length values in bits.
*
* @remarks
* NIST publication FIPS 197 states:
* > The AES algorithm is capable of using cryptographic keys of 128, 192, and 256 bits to encrypt
* > and decrypt data in blocks of 128 bits.
*
* This implementation does not support key lengths that are different from the three values
* defined by this constant.
*
* @see {@link https://doi.org/10.6028/NIST.FIPS.197-upd1 | NIST FIPS 197}
*/
const AES_KEY_LENGTHS = [128, 192, 256];
/**
* Constant defining the AES-GCM tag length values in bits.
*
* @remarks
* NIST Special Publication 800-38D, Section 5.2.1.2 states that the tag length:
* > may be any one of the following five values: 128, 120, 112, 104, or 96
*
* Although the NIST specification allows for tag lengths of 32 or 64 bits in certain applications,
* the use of shorter tag lengths can be problematic for GCM due to targeted forgery attacks. As a
* precaution, this implementation does not support tag lengths that are different from the five
* values defined by this constant. See Appendix C of the NIST SP 800-38D specification for
* additional guidance and details.
*
* @see {@link https://doi.org/10.6028/NIST.SP.800-38D | NIST SP 800-38D}
*/
export const AES_GCM_TAG_LENGTHS = [96, 104, 112, 120, 128];
/**
* The `AesGcm` class provides a comprehensive set of utilities for cryptographic operations
* using the Advanced Encryption Standard (AES) in Galois/Counter Mode (GCM). This class includes
* methods for key generation, encryption, decryption, and conversions between raw byte arrays
* and JSON Web Key (JWK) formats. It is designed to support AES-GCM, a symmetric key algorithm
* that is widely used for its efficiency, security, and provision of authenticated encryption.
*
* AES-GCM is particularly favored for scenarios that require both confidentiality and integrity
* of data. It integrates the counter mode of encryption with the Galois mode of authentication,
* offering high performance and parallel processing capabilities.
*
* Key Features:
* - Key Generation: Generate AES symmetric keys in JWK format.
* - Key Conversion: Transform keys between raw byte arrays and JWK formats.
* - Encryption: Encrypt data using AES-GCM with the provided symmetric key.
* - Decryption: Decrypt data encrypted with AES-GCM using the corresponding symmetric key.
*
* The methods in this class are asynchronous, returning Promises to accommodate various
* JavaScript environments.
*
* @example
* ```ts
* // Key Generation
* const length = 256; // Length of the key in bits (e.g., 128, 192, 256)
* const privateKey = await AesGcm.generateKey({ length });
*
* // Encryption
* const data = new TextEncoder().encode('Messsage');
* const iv = new Uint8Array(12); // 12-byte initialization vector
* const encryptedData = await AesGcm.encrypt({
* data,
* iv,
* key: privateKey
* });
*
* // Decryption
* const decryptedData = await AesGcm.decrypt({
* data: encryptedData,
* iv,
* key: privateKey
* });
*
* // Key Conversion
* const privateKeyBytes = await AesGcm.privateKeyToBytes({ privateKey });
* ```
*/
export class AesGcm {
/**
* Converts a raw private key in bytes to its corresponding JSON Web Key (JWK) format.
*
* @remarks
* This method accepts a symmetric key represented as a byte array (Uint8Array) and
* converts it into a JWK object for use with AES-GCM (Advanced Encryption Standard -
* Galois/Counter Mode). 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 AesGcm.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 bytesToPrivateKey({ privateKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Construct the private key in JWK format.
const privateKey = {
k: Convert.uint8Array(privateKeyBytes).toBase64Url(),
kty: 'oct'
};
// Compute the JWK thumbprint and set as the key ID.
privateKey.kid = yield computeJwkThumbprint({ jwk: privateKey });
// Add algorithm identifier based on key length.
const lengthInBits = privateKeyBytes.length * 8;
privateKey.alg = { 128: 'A128GCM', 192: 'A192GCM', 256: 'A256GCM' }[lengthInBits];
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 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.
* @param params.key - The key to use for decryption, represented in JWK format.
* @param params.data - The encrypted data to decrypt, represented as a Uint8Array.
* @param params.iv - The initialization vector, represented as a Uint8Array.
* @param params.additionalData - Optional additional authenticated data. Optional.
* @param params.tagLength - The length of the authentication tag in bits. Optional.
*
* @returns A Promise that resolves to the decrypted data as a Uint8Array.
*/
static decrypt({ key, data, iv, additionalData, tagLength }) {
return __awaiter(this, void 0, void 0, function* () {
// Validate the initialization vector length.
if (iv.byteLength !== AES_GCM_IV_LENGTH / 8) {
throw new TypeError(`The initialization vector must be ${AES_GCM_IV_LENGTH} bits in length`);
}
// Validate the tag length.
if (tagLength && !AES_GCM_TAG_LENGTHS.includes(tagLength)) {
throw new RangeError(`The tag length is invalid: Must be ${AES_GCM_TAG_LENGTHS.join(', ')} bits`);
}
// Get the Web Crypto API interface.
const webCrypto = getWebcryptoSubtle();
// Import the JWK into the Web Crypto API to use for the decrypt operation.
const webCryptoKey = yield webCrypto.importKey('jwk', key, { name: 'AES-GCM' }, true, ['decrypt']);
// Browser implementations of the Web Crypto API throw an error if additionalData is undefined.
const algorithm = (additionalData === undefined)
? { name: 'AES-GCM', iv, tagLength }
: { name: 'AES-GCM', additionalData, iv, tagLength };
// Decrypt the data.
const plaintextBuffer = yield webCrypto.decrypt(algorithm, webCryptoKey, data);
// Convert from ArrayBuffer to Uint8Array.
const plaintext = new Uint8Array(plaintextBuffer);
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 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.
* @param params.key - The key to use for encryption, represented in JWK format.
* @param params.data - The data to encrypt, represented as a Uint8Array.
* @param params.iv - The initialization vector, represented as a Uint8Array.
* @param params.additionalData - Optional additional authenticated data. Optional.
* @param params.tagLength - The length of the authentication tag in bits. Optional.
*
* @returns A Promise that resolves to the encrypted data as a Uint8Array.
*/
static encrypt({ data, iv, key, additionalData, tagLength }) {
return __awaiter(this, void 0, void 0, function* () {
// Validate the initialization vector length.
if (iv.byteLength !== AES_GCM_IV_LENGTH / 8) {
throw new TypeError(`The initialization vector must be ${AES_GCM_IV_LENGTH} bits in length`);
}
// Validate the tag length.
if (tagLength && !AES_GCM_TAG_LENGTHS.includes(tagLength)) {
throw new RangeError(`The tag length is invalid: Must be ${AES_GCM_TAG_LENGTHS.join(', ')} bits`);
}
// Get the Web Crypto API interface.
const webCrypto = getWebcryptoSubtle();
// Import the JWK into the Web Crypto API to use for the encrypt operation.
const webCryptoKey = yield webCrypto.importKey('jwk', key, { name: 'AES-GCM' }, true, ['encrypt']);
// Browser implementations of the Web Crypto API throw an error if additionalData is undefined.
const algorithm = (additionalData === undefined)
? { name: 'AES-GCM', iv, tagLength }
: { name: 'AES-GCM', additionalData, iv, tagLength };
// Encrypt the data.
const ciphertextBuffer = yield webCrypto.encrypt(algorithm, webCryptoKey, data);
// Convert from ArrayBuffer to Uint8Array.
const ciphertext = new Uint8Array(ciphertextBuffer);
return ciphertext;
});
}
/**
* Generates a symmetric key for AES in Galois/Counter Mode (GCM) in JSON Web Key (JWK) format.
*
* @remarks
* This method creates a new symmetric key of a specified length suitable for use with
* AES-GCM encryption. It leverages cryptographically secure random number generation
* to ensure the uniqueness and security of the key. The generated key adheres to the JWK
* format, facilitating compatibility with common cryptographic standards and ease of use
* in various cryptographic applications.
*
* The generated key includes these components:
* - `kty`: Key Type, set to 'oct' for Octet Sequence, indicating a symmetric key.
* - `k`: The symmetric key component, base64url-encoded.
* - `kid`: Key ID, generated based on the JWK thumbprint, providing a unique identifier.
*
* @example
* ```ts
* const length = 256; // Length of the key in bits (e.g., 128, 192, 256)
* const privateKey = await AesGcm.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 generateKey({ length }) {
return __awaiter(this, void 0, void 0, function* () {
// Validate the key length.
if (!AES_KEY_LENGTHS.includes(length)) {
throw new RangeError(`The key length is invalid: Must be ${AES_KEY_LENGTHS.join(', ')} bits`);
}
// Get the Web Crypto API interface.
const webCrypto = getWebcryptoSubtle();
// Generate a random private key.
// See https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#usage_notes for
// an explanation for why Web Crypto generateKey() is used instead of getRandomValues().
const webCryptoKey = yield webCrypto.generateKey({ name: 'AES-GCM', length }, true, ['encrypt']);
// Export the private key in JWK format.
const _a = yield webCrypto.exportKey('jwk', webCryptoKey), { ext, key_ops } = _a, privateKey = __rest(_a, ["ext", "key_ops"]);
// Compute the JWK thumbprint and set as the key ID.
privateKey.kid = yield 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 focuses on the 'k' parameter of the JWK, which represents the symmetric key component
* in base64url encoding. The method decodes this value into a byte array, providing
* the symmetric key in its raw binary form.
*
* @example
* ```ts
* const privateKey = { ... }; // A symmetric key in JWK format
* const privateKeyBytes = await AesGcm.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 privateKeyToBytes({ privateKey }) {
return __awaiter(this, void 0, void 0, function* () {
// Verify the provided JWK represents a valid oct private key.
if (!isOctPrivateJwk(privateKey)) {
throw new Error(`AesGcm: The provided key is not a valid oct private key.`);
}
// Decode the provided private key to bytes.
const privateKeyBytes = Convert.base64Url(privateKey.k).toUint8Array();
return privateKeyBytes;
});
}
}
//# sourceMappingURL=aes-gcm.js.map
@@ -0,0 +1 @@
{"version":3,"file":"aes-gcm.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/primitives/aes-gcm.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,247 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
// ! TODO : Make sure I remove `@noble/ciphers` from the Agent package.json once this is moved to the `@web5/crypto` package.
import { getWebcryptoSubtle } from '@noble/ciphers/webcrypto/utils';
import { Convert } from '@web5/common';
import { computeJwkThumbprint, isOctPrivateJwk } from '@web5/crypto';
import { CryptoError, CryptoErrorCode } from '../crypto-error.js';
/**
* Constant defining the AES key length values in bits.
*
* @remarks
* NIST publication FIPS 197 states:
* > The AES algorithm is capable of using cryptographic keys of 128, 192, and 256 bits to encrypt
* > and decrypt data in blocks of 128 bits.
*
* This implementation does not support key lengths that are different from the three values
* defined by this constant.
*
* @see {@link https://doi.org/10.6028/NIST.FIPS.197-upd1 | NIST FIPS 197}
*/
const AES_KEY_LENGTHS = [128, 192, 256];
export class AesKw {
/**
* 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 bytesToPrivateKey({ privateKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Construct the private key in JWK format.
const privateKey = {
k: Convert.uint8Array(privateKeyBytes).toBase64Url(),
kty: 'oct'
};
// Compute the JWK thumbprint and set as the key ID.
privateKey.kid = yield computeJwkThumbprint({ jwk: privateKey });
// Add algorithm identifier based on key length.
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 generateKey({ length }) {
return __awaiter(this, void 0, void 0, function* () {
// Validate the key length.
if (!AES_KEY_LENGTHS.includes(length)) {
throw new RangeError(`The key length is invalid: Must be ${AES_KEY_LENGTHS.join(', ')} bits`);
}
// Get the Web Crypto API interface.
const webCrypto = getWebcryptoSubtle();
// Generate a random private key.
// See https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#usage_notes for
// an explanation for why Web Crypto generateKey() is used instead of getRandomValues().
const webCryptoKey = yield webCrypto.generateKey({ name: 'AES-KW', length }, true, ['wrapKey', 'unwrapKey']);
// Export the private key in JWK format.
const _a = yield webCrypto.exportKey('jwk', webCryptoKey), { ext, key_ops } = _a, privateKey = __rest(_a, ["ext", "key_ops"]);
// Compute the JWK thumbprint and set as the key ID.
privateKey.kid = yield 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 privateKeyToBytes({ privateKey }) {
return __awaiter(this, void 0, void 0, function* () {
// Verify the provided JWK represents a valid oct private key.
if (!isOctPrivateJwk(privateKey)) {
throw new Error(`AesKw: The provided key is not a valid oct private key.`);
}
// Decode the provided private key to bytes.
const privateKeyBytes = Convert.base64Url(privateKey.k).toUint8Array();
return privateKeyBytes;
});
}
static unwrapKey({ wrappedKeyBytes, wrappedKeyAlgorithm, decryptionKey }) {
return __awaiter(this, void 0, void 0, function* () {
if (!('alg' in decryptionKey && decryptionKey.alg)) {
throw new CryptoError(CryptoErrorCode.InvalidJwk, `The decryption key is missing the 'alg' property.`);
}
if (!['A128KW', 'A192KW', 'A256KW'].includes(decryptionKey.alg)) {
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `The 'decryptionKey' algorithm is not supported: ${decryptionKey.alg}`);
}
// Get the Web Crypto API interface.
const webCrypto = getWebcryptoSubtle();
// Import the decryption key for use with the Web Crypto API.
const decryptionCryptoKey = yield webCrypto.importKey('jwk', // key format
decryptionKey, // key data
{ name: 'AES-KW' }, // algorithm identifier
true, // key is extractable
['unwrapKey'] // key usages
);
// Map the private key's JOSE algorithm name to the Web Crypto API algorithm identifier.
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(CryptoErrorCode.AlgorithmNotSupported, `The 'wrappedKeyAlgorithm' is not supported: ${wrappedKeyAlgorithm}`);
}
// Unwrap the key using the Web Crypto API.
const unwrappedCryptoKey = yield 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
);
// Export the unwrapped key in JWK format.
const _a = yield webCrypto.exportKey('jwk', unwrappedCryptoKey), { ext, key_ops } = _a, unwrappedJsonWebKey = __rest(_a, ["ext", "key_ops"]);
const unwrappedKey = unwrappedJsonWebKey;
// Compute the JWK thumbprint and set as the key ID.
unwrappedKey.kid = yield computeJwkThumbprint({ jwk: unwrappedKey });
return unwrappedKey;
});
}
static wrapKey({ unwrappedKey, encryptionKey }) {
return __awaiter(this, void 0, void 0, function* () {
if (!('alg' in encryptionKey && encryptionKey.alg)) {
throw new CryptoError(CryptoErrorCode.InvalidJwk, `The encryption key is missing the 'alg' property.`);
}
if (!['A128KW', 'A192KW', 'A256KW'].includes(encryptionKey.alg)) {
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `The 'encryptionKey' algorithm is not supported: ${encryptionKey.alg}`);
}
if (!('alg' in unwrappedKey && unwrappedKey.alg)) {
throw new CryptoError(CryptoErrorCode.InvalidJwk, `The private key to wrap is missing the 'alg' property.`);
}
// Get the Web Crypto API interface.
const webCrypto = getWebcryptoSubtle();
// Import the encryption key for use with the Web Crypto API.
const encryptionCryptoKey = yield webCrypto.importKey('jwk', // key format
encryptionKey, // key data
{ name: 'AES-KW' }, // algorithm identifier
true, // key is extractable
['wrapKey'] // key usages
);
// Map the private key's JOSE algorithm name to the Web Crypto API algorithm identifier.
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(CryptoErrorCode.AlgorithmNotSupported, `The 'unwrappedKey' algorithm is not supported: ${unwrappedKey.alg}`);
}
// Import the private key to wrap for use with the Web Crypto API.
const unwrappedCryptoKey = yield webCrypto.importKey('jwk', // key format
unwrappedKey, // key data
{ name: webCryptoAlgorithm }, // algorithm identifier
true, // key is extractable
['unwrapKey'] // key usages
);
// Wrap the key using the Web Crypto API.
const wrappedKeyBuffer = yield webCrypto.wrapKey('raw', // output format
unwrappedCryptoKey, // key to wrap
encryptionCryptoKey, // wrapping key
'AES-KW' // algorithm identifier
);
// Convert from ArrayBuffer to Uint8Array.
const wrappedKeyBytes = new Uint8Array(wrappedKeyBuffer);
return wrappedKeyBytes;
});
}
}
//# sourceMappingURL=aes-kw.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,80 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
// ! TODO : Make sure I remove `@noble/ciphers` from the Agent package.json once this is moved to the `@web5/crypto` package.
import { getWebcryptoSubtle } from '@noble/ciphers/webcrypto/utils';
import { Convert } from '@web5/common';
/**
* The `Hkdf` class provides an interface for HMAC-based Extract-and-Expand Key Derivation Function (HKDF)
* as defined in RFC 5869.
*
* 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
* });
* ```
*/
export class Hkdf {
/**
* 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 deriveKeyBytes({ baseKeyBytes, length, hash, salt, info = new Uint8Array() }) {
return __awaiter(this, void 0, void 0, function* () {
// Get the Web Crypto API interface.
const webCrypto = getWebcryptoSubtle();
// Import the baseKeyBytes into the Web Crypto API to use for the key derivation operation.
const webCryptoKey = yield webCrypto.importKey('raw', baseKeyBytes, { name: 'HKDF' }, false, ['deriveBits']);
// Convert the salt and info to Uint8Array if they are provided as strings.
salt = typeof salt === 'string' ? Convert.string(salt).toUint8Array() : salt;
info = typeof info === 'string' ? Convert.string(info).toUint8Array() : info;
// Derive the bytes using the Web Crypto API.
const derivedKeyBuffer = yield crypto.subtle.deriveBits({ name: 'HKDF', hash, salt, info }, webCryptoKey, length);
// Convert from ArrayBuffer to Uint8Array.
const derivedKeyBytes = new Uint8Array(derivedKeyBuffer);
return derivedKeyBytes;
});
}
}
//# sourceMappingURL=hkdf.js.map
@@ -0,0 +1 @@
{"version":3,"file":"hkdf.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/primitives/hkdf.ts"],"names":[],"mappings":";;;;;;;;;AAAA,6HAA6H;AAC7H,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AA0CvC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,IAAI;IACf;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACI,MAAM,CAAO,cAAc,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,UAAU,EAAE,EAC3D;;YAEjC,oCAAoC;YACpC,MAAM,SAAS,GAAG,kBAAkB,EAAkB,CAAC;YAEvD,2FAA2F;YAC3F,MAAM,YAAY,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;YAE7G,2EAA2E;YAC3E,IAAI,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAC7E,IAAI,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAE7E,6CAA6C;YAC7C,MAAM,gBAAgB,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CACrD,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAClC,YAAY,EACZ,MAAM,CACP,CAAC;YAEF,0CAA0C;YAC1C,MAAM,eAAe,GAAG,IAAI,UAAU,CAAC,gBAAgB,CAAC,CAAC;YAEzD,OAAO,eAAe,CAAC;QACzB,CAAC;KAAA;CACF"}
@@ -0,0 +1,85 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
// ! TODO : Make sure I remove `@noble/ciphers` from the Agent package.json once this is moved to the `@web5/crypto` package.
import { getWebcryptoSubtle } from '@noble/ciphers/webcrypto/utils';
/**
* The `Pbkdf2` class provides a secure way to derive cryptographic keys from a password
* using the PBKDF2 (Password-Based Key Derivation Function 2) algorithm.
*
* The PBKDF2 algorithm is widely used for generating keys from passwords, as it applies
* a pseudorandom function to the input password along with a salt value and iterates the
* process multiple times to increase the key's resistance to brute-force attacks.
*
* Notes:
* - The `baseKeyBytes` that will be the input key material for PBKDF2 is expected to be a low-entropy
* value, such as a password or passphrase. It should be kept confidential.
* - In 2023, {@link https://web.archive.org/web/20230123232056/https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2 | OWASP recommended}
* a minimum of 600,000 iterations for PBKDF2-HMAC-SHA256 and 210,000 for PBKDF2-HMAC-SHA512.
*
* @example
* ```ts
* // Key Derivation
* 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
* });
* ```
*
* @remarks
* This class relies on the availability of the Web Crypto API.
*/
export class Pbkdf2 {
/**
* 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 deriveKeyBytes({ baseKeyBytes, hash, salt, iterations, length }) {
return __awaiter(this, void 0, void 0, function* () {
// Get the Web Crypto API interface.
const webCrypto = getWebcryptoSubtle();
// Import the password as a raw key for use with the Web Crypto API.
const webCryptoKey = yield 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
);
// Derive the bytes using the Web Crypto API.
const derivedKeyBuffer = yield webCrypto.deriveBits({ name: 'PBKDF2', hash, salt, iterations }, webCryptoKey, length);
// Convert from ArrayBuffer to Uint8Array.
const derivedKeyBytes = new Uint8Array(derivedKeyBuffer);
return derivedKeyBytes;
});
}
}
//# sourceMappingURL=pbkdf2.js.map
@@ -0,0 +1 @@
{"version":3,"file":"pbkdf2.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/primitives/pbkdf2.ts"],"names":[],"mappings":";;;;;;;;;AAAA,6HAA6H;AAC7H,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAiCpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,OAAO,MAAM;IACjB;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACI,MAAM,CAAO,cAAc,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAC5C;;YAEnC,oCAAoC;YACpC,MAAM,SAAS,GAAG,kBAAkB,EAAkB,CAAC;YAEvD,oEAAoE;YACpE,MAAM,YAAY,GAAG,MAAM,SAAS,CAAC,SAAS,CAC5C,KAAK,EAAe,0BAA0B;YAC9C,YAAY,EAAQ,qBAAqB;YACzC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,uBAAuB;YAC3C,KAAK,EAAe,yBAAyB;YAC7C,CAAC,YAAY,CAAC,CAAM,aAAa;aAClC,CAAC;YAEF,6CAA6C;YAC7C,MAAM,gBAAgB,GAAG,MAAM,SAAS,CAAC,UAAU,CACjD,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,EAC1C,YAAY,EACZ,MAAM,CACP,CAAC;YAEF,0CAA0C;YAC1C,MAAM,eAAe,GAAG,IAAI,UAAU,CAAC,gBAAgB,CAAC,CAAC;YAEzD,OAAO,eAAe,CAAC;QACzB,CAAC;KAAA;CACF"}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=cipher.js.map
@@ -0,0 +1 @@
{"version":3,"file":"cipher.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/types/cipher.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=crypto-api.js.map
@@ -0,0 +1 @@
{"version":3,"file":"crypto-api.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/types/crypto-api.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=key-converter.js.map
@@ -0,0 +1 @@
{"version":3,"file":"key-converter.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/types/key-converter.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=key-deriver.js.map
@@ -0,0 +1 @@
{"version":3,"file":"key-deriver.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/types/key-deriver.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=key-io.js.map
@@ -0,0 +1 @@
{"version":3,"file":"key-io.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/types/key-io.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=key-manager.js.map
@@ -0,0 +1 @@
{"version":3,"file":"key-manager.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/types/key-manager.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=key-wrapper.js.map
@@ -0,0 +1 @@
{"version":3,"file":"key-wrapper.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/types/key-wrapper.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=params-direct.js.map
@@ -0,0 +1 @@
{"version":3,"file":"params-direct.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/types/params-direct.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=params-kms.js.map
@@ -0,0 +1 @@
{"version":3,"file":"params-kms.js","sourceRoot":"","sources":["../../../../../src/prototyping/crypto/types/params-kms.ts"],"names":[],"mappings":""}
@@ -0,0 +1,19 @@
export function isCipher(obj) {
return (obj !== null && typeof obj === 'object'
&& 'encrypt' in obj && typeof obj.encrypt === 'function'
&& 'decrypt' in obj && typeof obj.decrypt === 'function');
}
export function isKeyExporter(obj) {
return (obj !== null && typeof obj === 'object'
&& 'exportKey' in obj && typeof obj.exportKey === 'function');
}
export function isKeyImporter(obj) {
return (obj !== null && typeof obj === 'object'
&& 'importKey' in obj && typeof obj.importKey === 'function');
}
export function isKeyWrapper(obj) {
return (obj !== null && typeof obj === 'object'
&& 'wrapKey' in obj && typeof obj.wrapKey === 'function'
&& 'unwrapKey' in obj && typeof obj.unwrapKey === 'function');
}
//# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../../src/prototyping/crypto/utils.ts"],"names":[],"mappings":"AAIA,MAAM,UAAU,QAAQ,CACtB,GAAY;IAEZ,OAAO,CACL,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;WACpC,SAAS,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,UAAU;WACrD,SAAS,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,UAAU,CACzD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,GAAY;IAEZ,OAAO,CACL,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;WACpC,WAAW,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,UAAU,CAC7D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,GAAY;IAEZ,OAAO,CACL,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;WACpC,WAAW,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,UAAU,CAC7D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,GAAY;IAEZ,OAAO,CACL,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;WACpC,SAAS,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,UAAU;WACrD,WAAW,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,UAAU,CAC7D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,77 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import ms from 'ms';
import { TtlCache } from '@web5/common';
export class DidResolverCacheMemory {
constructor({ ttl = '15m' } = {}) {
this.cache = new TtlCache({ ttl: ms(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.
*/
get(didUri) {
return __awaiter(this, void 0, void 0, function* () {
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.
*/
set(didUri, resolutionResult) {
return __awaiter(this, void 0, void 0, function* () {
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.
*/
delete(didUri) {
return __awaiter(this, void 0, void 0, function* () {
this.cache.delete(didUri);
});
}
/**
* Clears all entries from the cache.
*
* @returns A promise that resolves when the operation is complete.
*/
clear() {
return __awaiter(this, void 0, void 0, function* () {
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.
*/
close() {
return __awaiter(this, void 0, void 0, function* () {
// No-op since there is no underlying store to close.
});
}
}
//# sourceMappingURL=resolver-cache-memory.js.map
@@ -0,0 +1 @@
{"version":3,"file":"resolver-cache-memory.js","sourceRoot":"","sources":["../../../../src/prototyping/dids/resolver-cache-memory.ts"],"names":[],"mappings":";;;;;;;;;AAEA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAiBxC,MAAM,OAAO,sBAAsB;IAGjC,YAAY,EAAE,GAAG,GAAG,KAAK,KAAmC,EAAE;QAC5D,IAAI,CAAC,KAAK,GAAG,IAAI,QAAQ,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED;;;;;;;OAOG;IACU,GAAG,CAAC,MAAc;;YAC7B,IAAI,CAAC,MAAM,EAAE;gBACX,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;aACpD;YAED,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;KAAA;IAED;;;;;;OAMG;IACU,GAAG,CAAC,MAAc,EAAE,gBAAqC;;YACpE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;QAC3C,CAAC;KAAA;IAED;;;;;OAKG;IACU,MAAM,CAAC,MAAc;;YAChC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;KAAA;IAED;;;;OAIG;IACU,KAAK;;YAChB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;KAAA;IAED;;;;;OAKG;IACU,KAAK;;YAChB,qDAAqD;QACvD,CAAC;KAAA;CACF"}
@@ -0,0 +1,9 @@
export function isPortableDid(obj) {
// Validate that the given value is an object that has the necessary properties of PortableDid.
return !(!obj || typeof obj !== 'object' || obj === null)
&& 'uri' in obj
&& 'document' in obj
&& 'metadata' in obj
&& (!('keyManager' in obj) || obj.keyManager === undefined);
}
//# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../../src/prototyping/dids/utils.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,+FAA+F;IAC/F,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,CAAC;WACpD,KAAK,IAAI,GAAG;WACZ,UAAU,IAAI,GAAG;WACjB,UAAU,IAAI,GAAG;WACjB,CAAC,CAAC,CAAC,YAAY,IAAI,GAAG,CAAC,IAAI,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC;AAChE,CAAC"}
+123
View File
@@ -0,0 +1,123 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { utils as cryptoUtils } from '@web5/crypto';
import { createJsonRpcRequest } from './prototyping/clients/json-rpc.js';
import { HttpDwnRpcClient } from './prototyping/clients/http-dwn-rpc-client.js';
import { WebSocketDwnRpcClient } from './prototyping/clients/web-socket-clients.js';
export var DidRpcMethod;
(function (DidRpcMethod) {
DidRpcMethod["Create"] = "did.create";
DidRpcMethod["Resolve"] = "did.resolve";
})(DidRpcMethod || (DidRpcMethod = {}));
/**
* Client used to communicate with Dwn Servers
*/
export class Web5RpcClient {
constructor(clients = []) {
this.transportClients = new Map();
// include http and socket clients as default.
// can be overwritten for 'http:', 'https:', 'ws: or ':wss' if instantiated with other clients.
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());
}
sendDidRequest(request) {
return __awaiter(this, void 0, void 0, function* () {
// URL() will throw if provided `url` is invalid.
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) {
// will throw if url is invalid
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);
}
getServerInfo(dwnUrl) {
return __awaiter(this, void 0, void 0, function* () {
// will throw if url is invalid
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);
});
}
}
export class HttpWeb5RpcClient extends HttpDwnRpcClient {
sendDidRequest(request) {
return __awaiter(this, void 0, void 0, function* () {
const requestId = cryptoUtils.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 = yield fetch(httpRequest);
if (response.ok) {
jsonRpcResponse = yield response.json();
// If the response is an error, throw an error.
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;
});
}
}
export class WebSocketWeb5RpcClient extends WebSocketDwnRpcClient {
sendDidRequest(_request) {
return __awaiter(this, void 0, void 0, function* () {
throw new Error(`not implemented for transports [${this.transportProtocols.join(', ')}]`);
});
}
getServerInfo(_dwnUrl) {
return __awaiter(this, void 0, void 0, function* () {
throw new Error(`not implemented for transports [${this.transportProtocols.join(', ')}]`);
});
}
}
//# sourceMappingURL=rpc-client.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"rpc-client.js","sourceRoot":"","sources":["../../src/rpc-client.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,KAAK,IAAI,WAAW,EAAE,MAAM,cAAc,CAAC;AAOpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AAWpF,MAAM,CAAN,IAAY,YAGX;AAHD,WAAY,YAAY;IACtB,qCAAqB,CAAA;IACrB,uCAAuB,CAAA;AACzB,CAAC,EAHW,YAAY,KAAZ,YAAY,QAGvB;AAqBD;;GAEG;AACH,MAAM,OAAO,aAAa;IAGxB,YAAY,UAAqB,EAAE;QACjC,IAAI,CAAC,gBAAgB,GAAG,IAAI,GAAG,EAAE,CAAC;QAElC,8CAA8C;QAC9C,+FAA+F;QAC/F,OAAO,GAAG,CAAC,IAAI,iBAAiB,EAAE,EAAE,IAAI,sBAAsB,EAAE,EAAE,GAAG,OAAO,CAAC,CAAC;QAE9E,KAAK,IAAI,MAAM,IAAI,OAAO,EAAE;YAC1B,KAAK,IAAI,eAAe,IAAI,MAAM,CAAC,kBAAkB,EAAE;gBACrD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;aACpD;SACF;IACH,CAAC;IAED,IAAI,kBAAkB;QACpB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;IAEK,cAAc,CAAC,OAAsB;;YACzC,iDAAiD;YACjD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAEjC,MAAM,eAAe,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAChE,IAAI,CAAC,eAAe,EAAE;gBACpB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,QAAQ,6BAA6B,CAAC,CAAC;gBACzE,KAAK,CAAC,IAAI,GAAG,qBAAqB,CAAC;gBAEnC,MAAM,KAAK,CAAC;aACb;YAED,OAAO,eAAe,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QACjD,CAAC;KAAA;IAED,cAAc,CAAC,OAAsB;QACnC,+BAA+B;QAC/B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAEpC,MAAM,eAAe,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAChE,IAAI,CAAC,eAAe,EAAE;YACpB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,QAAQ,6BAA6B,CAAC,CAAC;YACzE,KAAK,CAAC,IAAI,GAAG,qBAAqB,CAAC;YAEnC,MAAM,KAAK,CAAC;SACb;QAED,OAAO,eAAe,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IAEK,aAAa,CAAC,MAAc;;YAChC,+BAA+B;YAC/B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;YAE5B,MAAM,eAAe,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAChE,IAAG,CAAC,eAAe,EAAE;gBACnB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,QAAQ,6BAA6B,CAAC,CAAC;gBACzE,KAAK,CAAC,IAAI,GAAG,qBAAqB,CAAC;gBAEnC,MAAM,KAAK,CAAC;aACb;YAED,OAAO,eAAe,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QAC/C,CAAC;KAAA;CACF;AAED,MAAM,OAAO,iBAAkB,SAAQ,gBAAgB;IAC/C,cAAc,CAAC,OAAsB;;YACzC,MAAM,SAAS,GAAG,WAAW,CAAC,UAAU,EAAE,CAAC;YAC3C,MAAM,cAAc,GAAG,oBAAoB,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,EAAE;gBACrE,IAAI,EAAE,OAAO,CAAC,IAAI;aACnB,CAAC,CAAC;YAEH,MAAM,WAAW,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE;gBAC3C,MAAM,EAAI,MAAM;gBAChB,OAAO,EAAG;oBACR,cAAc,EAAE,kBAAkB;iBACnC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC;aACrC,CAAC,CAAC;YAEH,IAAI,eAAgC,CAAC;YAErC,IAAI;gBACF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,CAAC;gBAE1C,IAAI,QAAQ,CAAC,EAAE,EAAE;oBACf,eAAe,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;oBAExC,+CAA+C;oBAC/C,IAAI,eAAe,CAAC,KAAK,EAAE;wBACzB,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,eAAe,CAAC,KAAK,CAAC;wBAChD,MAAM,IAAI,KAAK,CAAC,aAAa,IAAI,OAAO,OAAO,EAAE,CAAC,CAAC;qBACpD;iBACF;qBAAM;oBACL,MAAM,IAAI,KAAK,CAAC,SAAS,QAAQ,CAAC,MAAM,OAAO,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;iBACvE;aACF;YAAC,OAAO,KAAU,EAAE;gBACnB,MAAM,IAAI,KAAK,CAAC,oDAAoD,OAAO,CAAC,GAAG,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;aACtG;YAED,OAAO,eAAe,CAAC,MAAwB,CAAC;QAClD,CAAC;KAAA;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,qBAAqB;IACzD,cAAc,CAAC,QAAuB;;YAC1C,MAAM,IAAI,KAAK,CAAC,mCAAmC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5F,CAAC;KAAA;IAEK,aAAa,CAAC,OAAe;;YACjC,MAAM,IAAI,KAAK,CAAC,mCAAmC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5F,CAAC;KAAA;CACF"}
+228
View File
@@ -0,0 +1,228 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import ms from 'ms';
import { Convert, NodeStream, TtlCache } from '@web5/common';
import { TENANT_SEPARATOR } from './utils-internal.js';
import { getDataStoreTenant } from './utils-internal.js';
import { DwnInterface } from './types/dwn.js';
export class DwnDataStore {
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 TtlCache({ ttl: ms('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 TtlCache({ ttl: ms('2 hours'), max: 1000 });
/**
* 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'
};
}
delete({ id, agent, tenant }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the tenant identifier (DID) for the delete operation.
const tenantDid = yield getDataStoreTenant({ agent, tenant, didUri: id });
// Look up the DWN record ID of the object in the store with the given `id`.
let matchingRecordId = yield this.lookupRecordId({ id, tenantDid, agent });
// Return false if the given ID was not found in the store.
if (!matchingRecordId)
return false;
// If a record for the given ID was found, attempt to delete it.
const { reply: { status } } = yield agent.dwn.processRequest({
author: tenantDid,
target: tenantDid,
messageType: DwnInterface.RecordsDelete,
messageParams: { recordId: matchingRecordId }
});
// If the record was successfully deleted, update the index/cache and return true;
if (status.code === 202) {
this._index.delete(`${tenantDid}${TENANT_SEPARATOR}${id}`);
this._cache.delete(matchingRecordId);
return true;
}
// If the Delete operation failed, throw an error.
throw new Error(`${this.name}: Failed to delete '${id}' from store: (${status.code}) ${status.detail}`);
});
}
get({ id, agent, tenant, useCache = false }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the tenant identifier (DID) for the list operation.
const tenantDid = yield getDataStoreTenant({ agent, tenant, didUri: id });
// Look up the DWN record ID of the object in the store with the given `id`.
let matchingRecordId = yield this.lookupRecordId({ id, tenantDid, agent });
// Return undefined if no matches were found.
if (!matchingRecordId)
return undefined;
// Retrieve and return the stored object.
return yield this.getRecord({ recordId: matchingRecordId, tenantDid, agent, useCache });
});
}
list({ agent, tenant }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the tenant identifier (DID) for the list operation.
const tenantDid = yield getDataStoreTenant({ tenant, agent });
// Query the DWN for all stored record objects.
const storedRecords = yield this.getAllRecords({ agent, tenantDid });
return storedRecords;
});
}
set({ id, data, tenant, agent, preventDuplicates = true, useCache = false }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the tenant identifier (DID) for the set operation.
const tenantDid = yield getDataStoreTenant({ agent, tenant, didUri: id });
// If enabled, check if a record with the given `id` is already present in the store.
if (preventDuplicates) {
// Look up the DWN record ID of the object in the store with the given `id`.
const matchingRecordId = yield this.lookupRecordId({ id, tenantDid, agent });
if (matchingRecordId) {
throw new Error(`${this.name}: Import failed due to duplicate entry for: ${id}`);
}
}
// Convert the store object to a byte array, which will be the data payload of the DWN record.
const dataBytes = Convert.object(data).toUint8Array();
// Store the record in the DWN.
const { message, reply: { status } } = yield agent.dwn.processRequest({
author: tenantDid,
target: tenantDid,
messageType: DwnInterface.RecordsWrite,
messageParams: Object.assign({}, this._recordProperties),
dataStream: new Blob([dataBytes], { type: 'application/json' })
});
// If the write fails, throw an error.
if (!(message && status.code === 202)) {
throw new Error(`${this.name}: Failed to write data to store for: ${id}`);
}
// Add the ID of the newly created record to the index.
this._index.set(`${tenantDid}${TENANT_SEPARATOR}${id}`, message.recordId);
// If caching is enabled, add the store object to the cache.
if (useCache) {
this._cache.set(message.recordId, data);
}
});
}
getAllRecords(_params) {
return __awaiter(this, void 0, void 0, function* () {
throw new Error('Not implemented: Classes extending DwnDataStore must implement getAllRecords()');
});
}
getRecord({ recordId, tenantDid, agent, useCache }) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
// If caching is enabled, check the cache for the record ID.
if (useCache) {
const record = this._cache.get(recordId);
// If the record ID was present in the cache, return the associated store object.
if (record)
return record;
// Otherwise, continue to read from the store.
}
// Read the record from the store.
const { reply: readReply } = yield agent.dwn.processRequest({
author: tenantDid,
target: tenantDid,
messageType: DwnInterface.RecordsRead,
messageParams: { filter: { recordId } }
});
if (!((_a = readReply.record) === null || _a === void 0 ? void 0 : _a.data)) {
throw new Error(`${this.name}: Failed to read data from DWN for: ${recordId}`);
}
// If the record was found, convert back to store object format.
const storeObject = yield NodeStream.consumeToJson({ readable: readReply.record.data });
// If caching is enabled, add the store object to the cache.
if (useCache) {
this._cache.set(recordId, storeObject);
}
return storeObject;
});
}
lookupRecordId({ id, tenantDid, agent }) {
return __awaiter(this, void 0, void 0, function* () {
// Check the index for a matching ID and extend the index TTL.
let recordId = this._index.get(`${tenantDid}${TENANT_SEPARATOR}${id}`, { updateAgeOnGet: true });
// If no matching record ID was found in the index...
if (!recordId) {
// Query the DWN for all stored objects, which rebuilds the index.
yield this.getAllRecords({ agent, tenantDid });
// Check the index again for a matching ID.
recordId = this._index.get(`${tenantDid}${TENANT_SEPARATOR}${id}`);
}
return recordId;
});
}
}
export class InMemoryDataStore {
constructor() {
this.name = 'InMemoryDataStore';
/**
* A private field that contains the Map used as the in-memory data store.
*/
this.store = new Map();
}
delete({ id, agent, tenant }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the tenant identifier (DID) for the delete operation.
const tenantDid = yield getDataStoreTenant({ agent, tenant, didUri: id });
if (this.store.has(`${tenantDid}${TENANT_SEPARATOR}${id}`)) {
// Record with given identifier exists so proceed with delete.
this.store.delete(`${tenantDid}${TENANT_SEPARATOR}${id}`);
return true;
}
// Record with given identifier not present so delete operation not possible.
return false;
});
}
get({ id, agent, tenant }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the tenant identifier (DID) for the get operation.
const tenantDid = yield getDataStoreTenant({ agent, tenant, didUri: id });
return this.store.get(`${tenantDid}${TENANT_SEPARATOR}${id}`);
});
}
list({ agent, tenant }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the tenant identifier (DID) for the list operation.
const tenantDid = yield getDataStoreTenant({ tenant, agent });
const result = [];
for (const [key, storedRecord] of this.store.entries()) {
if (key.startsWith(`${tenantDid}${TENANT_SEPARATOR}`)) {
result.push(storedRecord);
}
}
return result;
});
}
set({ id, data, tenant, agent, preventDuplicates }) {
return __awaiter(this, void 0, void 0, function* () {
// Determine the tenant identifier (DID) for the set operation.
const tenantDid = yield getDataStoreTenant({ agent, tenant, didUri: id });
// If enabled, check if a record with the given `id` is already present in the store.
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}`);
}
}
// Make a deep copy so that the object stored does not share the same references as the input.
const clonedData = structuredClone(data);
this.store.set(`${tenantDid}${TENANT_SEPARATOR}${id}`, clonedData);
});
}
}
//# sourceMappingURL=store-data.js.map
File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More