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
@@ -0,0 +1,124 @@
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 { AesCtr } from '../primitives/aes-ctr.js';
import { CryptoAlgorithm } from './crypto-algorithm.js';
/**
* The `AesCtrAlgorithm` class provides a concrete implementation for cryptographic operations using
* the AES algorithm in Counter (CTR) mode. 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 AesCtrAlgorithm extends CryptoAlgorithm {
/**
* Decrypts the provided data using AES-CTR.
*
* @remarks
* This method performs AES-CTR decryption on the given encrypted data using the specified key.
* Similar to the encryption process, it requires an initial counter block and the length
* of the counter block, along with the encrypted data and the decryption key. The method
* returns the decrypted data as a Uint8Array.
*
* @example
* ```ts
* const aesCtr = new AesCtrAlgorithm();
* const encryptedData = new Uint8Array([...]); // Encrypted data
* const counter = new Uint8Array(16); // 16-byte (128-bit) counter block used during encryption
* const key = { ... }; // A Jwk object representing the same AES key used for encryption
* const decryptedData = await aesCtr.decrypt({
* data: encryptedData,
* counter,
* key,
* length: 128 // Length of the counter 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 = AesCtr.decrypt(params);
return plaintext;
});
}
/**
* Encrypts the provided data using AES-CTR.
*
* @remarks
* This method performs AES-CTR encryption on the given data using the specified key.
* It requires the initial counter block and the length of the counter block, alongside
* the data and key. The method is designed to work asynchronously and returns the
* encrypted data as a Uint8Array.
*
* @example
* ```ts
* const aesCtr = new AesCtrAlgorithm();
* const data = new TextEncoder().encode('Messsage');
* const counter = new Uint8Array(16); // 16-byte (128-bit) counter block
* const key = { ... }; // A Jwk object representing an AES key
* const encryptedData = await aesCtr.encrypt({
* data,
* counter,
* key,
* length: 128 // Length of the counter 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 = AesCtr.encrypt(params);
return ciphertext;
});
}
/**
* Generates a symmetric key for AES in Counter (CTR) mode in JSON Web Key (JWK) format.
*
* @remarks
* This method generates a symmetric AES key for use in CTR 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 aesCtr = new AesCtrAlgorithm();
* const privateKey = await aesCtr.generateKey({ algorithm: 'A256CTR' });
* ```
*
* @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 = { A128CTR: 128, A192CTR: 192, A256CTR: 256 }[algorithm];
// Generate a random private key.
const privateKey = yield AesCtr.generateKey({ length });
// Set the `alg` property based on the specified algorithm.
privateKey.alg = algorithm;
return privateKey;
});
}
}
//# sourceMappingURL=aes-ctr.js.map
@@ -0,0 +1 @@
{"version":3,"file":"aes-ctr.js","sourceRoot":"","sources":["../../../src/algorithms/aes-ctr.ts"],"names":[],"mappings":";;;;;;;;;AAKA,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AA4BxD;;;;;;;;GAQG;AACH,MAAM,OAAO,eAAgB,SAAQ,eAAe;IAIlD;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACU,OAAO,CAAC,MACS;;YAE5B,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAEzC,OAAO,SAAS,CAAC;QACnB,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;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;CACF"}
@@ -0,0 +1,132 @@
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 './crypto-algorithm.js';
import { AesGcm } from '../primitives/aes-gcm.js';
/**
* 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 {
/**
* 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;
});
}
}
//# sourceMappingURL=aes-gcm.js.map
@@ -0,0 +1 @@
{"version":3,"file":"aes-gcm.js","sourceRoot":"","sources":["../../../src/algorithms/aes-gcm.ts"],"names":[],"mappings":";;;;;;;;;AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAuB,MAAM,0BAA0B,CAAC;AAmDvE;;;;;;;;GAQG;AACH,MAAM,OAAO,eAAgB,SAAQ,eAAe;IAIlD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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;CACF"}
@@ -0,0 +1,6 @@
/**
* Base class for all cryptographic algorithm implementations.
*/
export class CryptoAlgorithm {
}
//# sourceMappingURL=crypto-algorithm.js.map
@@ -0,0 +1 @@
{"version":3,"file":"crypto-algorithm.js","sourceRoot":"","sources":["../../../src/algorithms/crypto-algorithm.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAgB,eAAe;CAAG"}
+237
View File
@@ -0,0 +1,237 @@
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 { Secp256k1 } from '../primitives/secp256k1.js';
import { Secp256r1 } from '../primitives/secp256r1.js';
import { CryptoAlgorithm } from './crypto-algorithm.js';
import { isEcPrivateJwk, isEcPublicJwk } from '../jose/jwk.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 {
/**
* 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}`);
}
}
});
}
/**
* 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/algorithms/ecdsa.ts"],"names":[],"mappings":"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"}
+213
View File
@@ -0,0 +1,213 @@
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 { Ed25519 } from '../primitives/ed25519.js';
import { CryptoAlgorithm } from './crypto-algorithm.js';
import { isOkpPrivateJwk, isOkpPublicJwk } from '../jose/jwk.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 {
/**
* 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 Error(`Unsupported curve: ${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}`);
}
}
});
}
/**
* 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 Error(`Unsupported curve: ${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 Error(`Unsupported curve: ${key.crv}`);
}
}
});
}
}
//# sourceMappingURL=eddsa.js.map
@@ -0,0 +1 @@
{"version":3,"file":"eddsa.js","sourceRoot":"","sources":["../../../src/algorithms/eddsa.ts"],"names":[],"mappings":"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"}
+57
View File
@@ -0,0 +1,57 @@
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 { Sha256 } from '../primitives/sha256.js';
import { CryptoAlgorithm } from './crypto-algorithm.js';
/**
* The `Sha2Algorithm` class is an implementation of the {@link Hasher | `Hasher`} interface for the
* SHA-2 family of cryptographic hash functions. The `digest` method takes the algorithm identifier
* of the hash function and arbitrary data as input and returns the hash digest of the data.
*
* This class is typically accessed through implementations that extend the
* {@link CryptoApi | `CryptoApi`} interface.
*/
export class Sha2Algorithm extends CryptoAlgorithm {
/**
* 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 sha2 = new Sha2Algorithm();
* const data = new TextEncoder().encode('Messsage');
* const digest = await sha2.digest({ 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* () {
switch (algorithm) {
case 'SHA-256': {
const hash = yield Sha256.digest({ data });
return hash;
}
}
});
}
}
//# sourceMappingURL=sha-2.js.map
@@ -0,0 +1 @@
{"version":3,"file":"sha-2.js","sourceRoot":"","sources":["../../../src/algorithms/sha-2.ts"],"names":[],"mappings":";;;;;;;;;AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAcxD;;;;;;;GAOG;AACH,MAAM,OAAO,aAAc,SAAQ,eAAe;IAGhD;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACU,MAAM,CAAC,EAAE,SAAS,EAAE,IAAI,EAAoB;;YACvD,QAAQ,SAAS,EAAE;gBAEjB,KAAK,SAAS,CAAC,CAAC;oBACd,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;oBAC3C,OAAO,IAAI,CAAC;iBACb;aACF;QAEH,CAAC;KAAA;CACF"}
+25
View File
@@ -0,0 +1,25 @@
export * from './local-key-manager.js';
export * as utils from './utils.js';
export * from './algorithms/aes-ctr.js';
export * from './algorithms/aes-gcm.js';
export * from './algorithms/crypto-algorithm.js';
export * from './algorithms/ecdsa.js';
export * from './algorithms/eddsa.js';
export * from './algorithms/sha-2.js';
export * from './jose/jwe.js';
export * from './jose/jwk.js';
export * from './jose/jws.js';
export * from './jose/jwt.js';
export * from './jose/utils.js';
export * from './primitives/aes-ctr.js';
export * from './primitives/aes-gcm.js';
export * from './primitives/concat-kdf.js';
export * from './primitives/ed25519.js';
export * from './primitives/secp256r1.js';
export * from './primitives/pbkdf2.js';
export * from './primitives/secp256k1.js';
export * from './primitives/sha256.js';
export * from './primitives/x25519.js';
export * from './primitives/xchacha20.js';
export * from './primitives/xchacha20-poly1305.js';
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AAEpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kCAAkC,CAAC;AACjD,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AAEtC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAEhC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oCAAoC,CAAC"}
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=jwe.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"jwe.js","sourceRoot":"","sources":["../../../src/jose/jwe.ts"],"names":[],"mappings":""}
+241
View File
@@ -0,0 +1,241 @@
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, removeUndefinedProperties } from '@web5/common';
import { canonicalize } from './utils.js';
import { Sha256 } from '../primitives/sha256.js';
/**
* Constant defining the prefix for JSON Web Keys (JWK) key URIs in this library.
*
* The prefix 'urn:jwk:' makes it explicit that a string represents a JWK, referenced by a
* {@link https://datatracker.ietf.org/doc/html/rfc3986 | URI} (Uniform Resource Identifier),
* which ensures consistent key referencing across all Web5 Key Management System (KMS)
* implementations.
*
* These key URIs take the form `urn:jwk:<JWK thumbprint>`, where the
* {@link https://datatracker.ietf.org/doc/html/rfc7638 | JWK thumbprint}, derived from the JWK, is
* unique to the key's material, unaffected by the order or optional properties in the JWK.
*/
export const KEY_URI_PREFIX_JWK = 'urn:jwk:';
/**
* Computes the thumbprint of a JSON Web Key (JWK) using the method
* specified in RFC 7638. This function accepts RSA, EC, OKP, and oct keys
* and returns the thumbprint as a base64url encoded SHA-256 hash of the
* JWK's required members, serialized and sorted lexicographically.
*
* Purpose:
* - Uniquely Identifying Keys: The thumbprint allows the unique
* identification of a specific JWK within a set of JWKs. It provides a
* deterministic way to generate a value that can be used as a key
* identifier (kid) or to match a specific key.
*
* - Simplifying Key Management: In systems where multiple keys are used,
* managing and identifying individual keys can become complex. The
* thumbprint method simplifies this by creating a standardized, unique
* identifier for each key.
*
* - Enabling Interoperability: By standardizing the method to compute a
* thumbprint, different systems can compute the same thumbprint value for
* a given JWK. This enables interoperability among systems that use JWKs.
*
* - Secure Comparison: The thumbprint provides a way to securely compare
* JWKs to determine if they are equivalent.
*
* @example
* ```ts
* const jwk: PublicKeyJwk = {
* 'kty': 'EC',
* 'crv': 'secp256k1',
* 'x': '61iPYuGefxotzBdQZtDvv6cWHZmXrTTscY-u7Y2pFZc',
* 'y': '88nPCVLfrAY9i-wg5ORcwVbHWC_tbeAd1JE2e0co0lU'
* };
*
* const thumbprint = jwkThumbprint(jwk);
* console.log(`JWK thumbprint: ${thumbprint}`);
* ```
*
* @see {@link https://datatracker.ietf.org/doc/html/rfc7638 | RFC7638} for
* the specification of JWK thumbprint computation.
*
* @param jwk - The JSON Web Key for which the thumbprint will be computed.
* This must be an RSA, EC, OKP, or oct key.
* @returns The thumbprint as a base64url encoded string.
* @throws Throws an `Error` if the provided key type is unsupported.
*/
export function computeJwkThumbprint({ jwk }) {
return __awaiter(this, void 0, void 0, function* () {
/** Step 1 - Normalization: The JWK is normalized to include only specific
* members and in lexicographic order.
*/
const keyType = jwk.kty;
let normalizedJwk;
if (keyType === 'EC') {
normalizedJwk = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y };
}
else if (keyType === 'oct') {
normalizedJwk = { k: jwk.k, kty: jwk.kty };
}
else if (keyType === 'OKP') {
normalizedJwk = { crv: jwk.crv, kty: jwk.kty, x: jwk.x };
}
else if (keyType === 'RSA') {
normalizedJwk = { e: jwk.e, kty: jwk.kty, n: jwk.n };
}
else {
throw new Error(`Unsupported key type: ${keyType}`);
}
removeUndefinedProperties(normalizedJwk);
/** Step 2 - Serialization: The normalized JWK is serialized to a UTF-8
* representation of its JSON encoding. */
const serializedJwk = canonicalize(normalizedJwk);
/** Step 3 - Digest Calculation: A cryptographic hash function
* (SHA-256 is recommended) is applied to the serialized JWK,
* resulting in the thumbprint. */
const utf8Bytes = Convert.string(serializedJwk).toUint8Array();
const digest = yield Sha256.digest({ data: utf8Bytes });
// Encode as Base64Url.
const thumbprint = Convert.uint8Array(digest).toBase64Url();
return thumbprint;
});
}
/**
* Checks if the provided object is a valid elliptic curve private key in JWK format.
*
* @param obj - The object to check.
* @returns True if the object is a valid EC private JWK; otherwise, false.
*/
export function isEcPrivateJwk(obj) {
if (!obj || typeof obj !== 'object')
return false;
if (!('kty' in obj && 'crv' in obj && 'x' in obj && 'd' in obj))
return false;
if (obj.kty !== 'EC')
return false;
if (typeof obj.d !== 'string')
return false;
if (typeof obj.x !== 'string')
return false;
return true;
}
/**
* Checks if the provided object is a valid elliptic curve public key in JWK format.
*
* @param obj - The object to check.
* @returns True if the object is a valid EC public JWK; otherwise, false.
*/
export function isEcPublicJwk(obj) {
if (!obj || typeof obj !== 'object')
return false;
if (!('kty' in obj && 'crv' in obj && 'x' in obj))
return false;
if ('d' in obj)
return false;
if (obj.kty !== 'EC')
return false;
if (typeof obj.x !== 'string')
return false;
return true;
}
/**
* Checks if the provided object is a valid octet sequence (symmetric key) in JWK format.
*
* @param obj - The object to check.
* @returns True if the object is a valid oct private JWK; otherwise, false.
*/
export function isOctPrivateJwk(obj) {
if (!obj || typeof obj !== 'object')
return false;
if (!('kty' in obj && 'k' in obj))
return false;
if (obj.kty !== 'oct')
return false;
if (typeof obj.k !== 'string')
return false;
return true;
}
/**
* Checks if the provided object is a valid octet key pair private key in JWK format.
*
* @param obj - The object to check.
* @returns True if the object is a valid OKP private JWK; otherwise, false.
*/
export function isOkpPrivateJwk(obj) {
if (!obj || typeof obj !== 'object')
return false;
if (!('kty' in obj && 'crv' in obj && 'x' in obj && 'd' in obj))
return false;
if (obj.kty !== 'OKP')
return false;
if (typeof obj.d !== 'string')
return false;
if (typeof obj.x !== 'string')
return false;
return true;
}
/**
* Checks if the provided object is a valid octet key pair public key in JWK format.
*
* @param obj - The object to check.
* @returns True if the object is a valid OKP public JWK; otherwise, false.
*/
export function isOkpPublicJwk(obj) {
if (!obj || typeof obj !== 'object')
return false;
if ('d' in obj)
return false;
if (!('kty' in obj && 'crv' in obj && 'x' in obj))
return false;
if (obj.kty !== 'OKP')
return false;
if (typeof obj.x !== 'string')
return false;
return true;
}
/**
* Checks if the provided object is a valid private key in JWK format of any supported type.
*
* @param obj - The object to check.
* @returns True if the object is a valid private JWK; otherwise, false.
*/
export function isPrivateJwk(obj) {
if (!obj || typeof obj !== 'object')
return false;
const kty = obj.kty;
switch (kty) {
case 'EC':
case 'OKP':
case 'RSA':
return 'd' in obj;
case 'oct':
return 'k' in obj;
default:
return false;
}
}
/**
* Checks if the provided object is a valid public key in JWK format of any supported type.
*
* @param obj - The object to check.
* @returns True if the object is a valid public JWK; otherwise, false.
*/
export function isPublicJwk(obj) {
if (!obj || typeof obj !== 'object')
return false;
const kty = obj.kty;
switch (kty) {
case 'EC':
case 'OKP':
return 'x' in obj && !('d' in obj);
case 'RSA':
return 'n' in obj && 'e' in obj && !('d' in obj);
default:
return false;
}
}
//# sourceMappingURL=jwk.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"jwk.js","sourceRoot":"","sources":["../../../src/jose/jwk.ts"],"names":[],"mappings":"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"}
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=jws.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"jws.js","sourceRoot":"","sources":["../../../src/jose/jws.ts"],"names":[],"mappings":""}
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=jwt.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../../src/jose/jwt.ts"],"names":[],"mappings":""}
+34
View File
@@ -0,0 +1,34 @@
/**
* Canonicalizes a given object according to RFC 8785 (https://tools.ietf.org/html/rfc8785),
* which describes JSON Canonicalization Scheme (JCS). This function sorts the keys of the
* object and its nested objects alphabetically and then returns a stringified version of it.
* This method handles nested objects, array values, and null values appropriately.
*
* @param obj - The object to canonicalize.
* @returns The stringified version of the input object with its keys sorted alphabetically
* per RFC 8785.
*/
export function canonicalize(obj) {
/**
* Recursively sorts the keys of an object.
*
* @param obj - The object whose keys are to be sorted.
* @returns A new object with sorted keys.
*/
const sortObjKeys = (obj) => {
if (obj !== null && typeof obj === 'object' && !Array.isArray(obj)) {
const sortedKeys = Object.keys(obj).sort();
const sortedObj = {};
for (const key of sortedKeys) {
// Recursively sort keys of nested objects.
sortedObj[key] = sortObjKeys(obj[key]);
}
return sortedObj;
}
return obj;
};
// Stringify and return the final sorted object.
const sortedObj = sortObjKeys(obj);
return JSON.stringify(sortedObj);
}
//# sourceMappingURL=utils.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/jose/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,GAA2B;IACtD;;;;;OAKG;IACH,MAAM,WAAW,GAAG,CAAC,GAA2B,EAA0B,EAAE;QAC1E,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;YAClE,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;YAC3C,MAAM,SAAS,GAA2B,EAAE,CAAC;YAC7C,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE;gBAC5B,2CAA2C;gBAC3C,SAAS,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;aACxC;YACD,OAAO,SAAS,CAAC;SAClB;QACD,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IAEF,gDAAgD;IAChD,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IACnC,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;AACnC,CAAC"}
+417
View File
@@ -0,0 +1,417 @@
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 { MemoryStore } from '@web5/common';
import { Sha2Algorithm } from './algorithms/sha-2.js';
import { EcdsaAlgorithm } from './algorithms/ecdsa.js';
import { EdDsaAlgorithm } from './algorithms/eddsa.js';
import { computeJwkThumbprint, isPrivateJwk, KEY_URI_PREFIX_JWK } from './jose/jwk.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'],
},
'secp256k1': {
implementation: EcdsaAlgorithm,
names: ['ES256K', 'secp256k1'],
},
'secp256r1': {
implementation: EcdsaAlgorithm,
names: ['ES256', 'secp256r1'],
},
'SHA-256': {
implementation: Sha2Algorithm,
names: ['SHA-256']
}
};
export class LocalKeyManager {
constructor(params) {
var _a;
/**
* 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._keyStore = (_a = params === null || params === void 0 ? void 0 : params.keyStore) !== null && _a !== void 0 ? _a : new MemoryStore();
}
/**
* 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 keyManager = new LocalKeyManager();
* const data = new Uint8Array([...]);
* const digest = await keyManager.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;
});
}
/**
* 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 }) {
return __awaiter(this, void 0, void 0, function* () {
// Get the key generator implementation based on the specified `algorithm` parameter.
const keyGenerator = this.getAlgorithm({ algorithm });
// Generate the key.
const key = yield keyGenerator.generateKey({ algorithm });
if ((key === null || key === void 0 ? void 0 : key.kid) === undefined) {
throw new Error('Generated key is missing a required property: kid');
}
// Construct the key URI.
const keyUri = `${KEY_URI_PREFIX_JWK}${key.kid}`;
// Store the key in the key store.
yield this._keyStore.set(keyUri, key);
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(keyUri, privateKey);
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;
});
}
/**
* 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;
});
}
/**
* 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 Error(`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 name of the algorithm 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 name of the algorithm associated with the key.
*
* @throws Error if the algorithm cannot be determined from the provided input.
*/
getAlgorithmName({ key }) {
const algProperty = key.alg;
const crvProperty = key.crv;
for (const algName in supportedAlgorithms) {
const algorithmInfo = supportedAlgorithms[algName];
if (algProperty && algorithmInfo.names.includes(algProperty)) {
return algName;
}
else if (crvProperty && algorithmInfo.names.includes(crvProperty)) {
return algName;
}
}
throw new Error(`Unable to determine algorithm based on provided input: alg=${algProperty}, crv=${crvProperty}`);
}
/**
* 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(keyUri);
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,327 @@
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 '../jose/jwk.js';
/**
* Constant defining the AES block size in bits.
*
* @remarks
* In AES Counter (CTR) mode, the counter length must match the block size of the AES algorithm,
* which is 128 bits. NIST publication 800-38A, which provides guidelines for block cipher modes of
* operation, specifies this requirement. Maintaining a counter length of 128 bits is essential for
* the correct operation and security of AES-CTR.
*
* This implementation does not support counter lengths that are different from the value defined by
* this constant.
*
* @see {@link https://doi.org/10.6028/NIST.SP.800-38A | NIST SP 800-38A}
*/
const AES_BLOCK_SIZE = 128;
/**
* 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 maximum length of the counter in bits.
*
* @remarks
* The rightmost bits of the counter block are used as the actual counter value, while the leftmost
* bits are used as the nonce. The maximum length of the counter is 128 bits, which is the same as
* the AES block size.
*/
const COUNTER_MAX_LENGTH = AES_BLOCK_SIZE;
/**
* The `AesCtr` class provides a comprehensive set of utilities for cryptographic operations
* using the Advanced Encryption Standard (AES) in Counter (CTR) mode. 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-CTR, a symmetric key algorithm
* that is widely used in various cryptographic applications for its efficiency and security.
*
* AES-CTR mode operates as a stream cipher using a block cipher (AES) and is well-suited for
* scenarios where parallel processing is beneficial or where the same key is required to
* encrypt multiple data blocks. The class adheres to standard cryptographic practices, ensuring
* compatibility and security in its implementations.
*
* 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-CTR with the provided symmetric key.
* - Decryption: Decrypt data encrypted with AES-CTR 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 AesCtr.generateKey({ length });
*
* // Encryption
* const data = new TextEncoder().encode('Messsage');
* const counter = new Uint8Array(16); // 16-byte (128-bit) counter block
* const encryptedData = await AesCtr.encrypt({
* data,
* counter,
* key: privateKey,
* length: 64 // Length of the counter in bits
* });
*
* // Decryption
* const decryptedData = await AesCtr.decrypt({
* data: encryptedData,
* counter,
* key: privateKey,
* length: 64 // Length of the counter in bits
* });
*
* // Key Conversion
* const privateKeyBytes = await AesCtr.privateKeyToBytes({ privateKey });
* ```
*/
export class AesCtr {
/**
* 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)
* in Counter (CTR) 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 AesCtr.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 });
return privateKey;
});
}
/**
* Decrypts the provided data using AES in Counter (CTR) mode.
*
* @remarks
* This method performs AES-CTR decryption on the given encrypted data using the specified key.
* Similar to the encryption process, it requires an initial counter block and the length
* of the counter block, along with the encrypted data and the decryption key. The method
* returns the decrypted data as a Uint8Array.
*
* @example
* ```ts
* const encryptedData = new Uint8Array([...]); // Encrypted data
* const counter = new Uint8Array(16); // 16-byte (128-bit) counter block used during encryption
* const key = { ... }; // A Jwk object representing the same AES key used for encryption
* const decryptedData = await AesCtr.decrypt({
* data: encryptedData,
* counter,
* key,
* length: 64 // Length of the counter 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, as a Uint8Array.
* @param params.counter - The initial value of the counter block.
* @param params.length - The number of bits in the counter block that are used for the actual counter.
*
* @returns A Promise that resolves to the decrypted data as a Uint8Array.
*/
static decrypt({ key, data, counter, length }) {
return __awaiter(this, void 0, void 0, function* () {
// Validate the initial counter block length matches the AES block size.
if (counter.byteLength !== AES_BLOCK_SIZE / 8) {
throw new TypeError(`The counter must be ${AES_BLOCK_SIZE} bits in length`);
}
// Validate the length of the counter.
if (length === 0 || length > COUNTER_MAX_LENGTH) {
throw new TypeError(`The 'length' property must be in the range 1 to ${COUNTER_MAX_LENGTH}`);
}
// 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-CTR' }, true, ['decrypt']);
// Decrypt the data.
const plaintextBuffer = yield webCrypto.decrypt({ name: 'AES-CTR', counter, length }, webCryptoKey, data);
// Convert from ArrayBuffer to Uint8Array.
const plaintext = new Uint8Array(plaintextBuffer);
return plaintext;
});
}
/**
* Encrypts the provided data using AES in Counter (CTR) mode.
*
* @remarks
* This method performs AES-CTR encryption on the given data using the specified key.
* It requires the initial counter block and the length of the counter block, alongside
* the data and key. The method is designed to work asynchronously and returns the
* encrypted data as a Uint8Array.
*
* @example
* ```ts
* const data = new TextEncoder().encode('Messsage');
* const counter = new Uint8Array(16); // 16-byte (128-bit) counter block
* const key = { ... }; // A Jwk object representing an AES key
* const encryptedData = await AesCtr.encrypt({
* data,
* counter,
* key,
* length: 64 // Length of the counter 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.counter - The initial value of the counter block.
* @param params.length - The number of bits in the counter block that are used for the actual counter.
*
* @returns A Promise that resolves to the encrypted data as a Uint8Array.
*/
static encrypt({ key, data, counter, length }) {
return __awaiter(this, void 0, void 0, function* () {
// Validate the initial counter block value length.
if (counter.byteLength !== AES_BLOCK_SIZE / 8) {
throw new TypeError(`The counter must be ${AES_BLOCK_SIZE} bits in length`);
}
// Validate the length of the counter.
if (length === 0 || length > COUNTER_MAX_LENGTH) {
throw new TypeError(`The 'length' property must be in the range 1 to ${COUNTER_MAX_LENGTH}`);
}
// 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-CTR' }, true, ['encrypt', 'decrypt']);
// Encrypt the data.
const ciphertextBuffer = yield webCrypto.encrypt({ name: 'AES-CTR', counter, length }, webCryptoKey, data);
// Convert from ArrayBuffer to Uint8Array.
const ciphertext = new Uint8Array(ciphertextBuffer);
return ciphertext;
});
}
/**
* Generates a symmetric key for AES in Counter (CTR) mode in JSON Web Key (JWK) format.
*
* @remarks
* This method creates a new symmetric key of a specified length suitable for use with
* AES-CTR encryption. 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.
*
* @example
* ```ts
* const length = 256; // Length of the key in bits (e.g., 128, 192, 256)
* const privateKey = await AesCtr.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-CTR', 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 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 AesCtr.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(`AesCtr: 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-ctr.js.map
@@ -0,0 +1 @@
{"version":3,"file":"aes-ctr.js","sourceRoot":"","sources":["../../../src/primitives/aes-ctr.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,347 @@
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 '../jose/jwk.js';
/**
* 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 });
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']);
// Note: Some browser implementations of the Web Crypto API throw an error if additionalData or
// tagLength are undefined, so only include them in the algorithm object if they are defined.
const algorithm = Object.assign(Object.assign({ name: 'AES-GCM', iv }, (tagLength && { tagLength })), (additionalData && { additionalData }));
// 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']);
// Note: Some browser implementations of the Web Crypto API throw an error if additionalData or
// tagLength are undefined, so only include them in the algorithm object if they are defined.
const algorithm = Object.assign(Object.assign({ name: 'AES-GCM', iv }, (tagLength && { tagLength })), (additionalData && { additionalData }));
// 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/primitives/aes-gcm.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,185 @@
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 { sha256 } from '@noble/hashes/sha256';
import { Convert, universalTypeOf } from '@web5/common';
import { concatBytes } from '@noble/hashes/utils';
/**
* An implementation of the Concatenation Key Derivation Function (ConcatKDF)
* as specified in NIST.800-56A, a single-step key-derivation function (SSKDF).
* ConcatKDF produces a derived key from a secret key (like a shared secret
* from ECDH), and other optional public information. This implementation
* specifically uses SHA-256 as the pseudorandom function (PRF).
*
* Note: This implementation allows for only a single round / repetition using the function
* `K(1) = H(counter || Z || FixedInfo)`, where:
* - `K(1)` is the derived key material after one round
* - `H` is the SHA-256 hashing function
* - `counter` is a 32-bit, big-endian bit string counter set to 0x00000001
* - `Z` is the shared secret value obtained from a key agreement protocol
* - `FixedInfo` is a bit string used to ensure that the derived keying material is adequately
* "bound" to the key-agreement transaction.
*
* @example
* ```ts
* // Key Derivation
* const derivedKeyingMaterial = await ConcatKdf.deriveKey({
* sharedSecret: utils.randomBytes(32),
* keyDataLen: 128,
* fixedInfo: {
* algorithmId: "A128GCM",
* partyUInfo: "Alice",
* partyVInfo: "Bob",
* suppPubInfo: 128,
* },
* });
* ```
*
* Additional Information:
*
* `Z`, or "shared secret":
* The shared secret value obtained from a key agreement protocol, such as
* Diffie-Hellman, ECDH (Elliptic Curve Diffie-Hellman). Importantly, this
* shared secret is not directly used as the encryption or authentication
* key, but as an input to a key derivation function (KDF), such as Concat
* KDF, to generate the actual key. This adds an extra layer of security, as
* even if the shared secret gets compromised, the actual encryption or
* authentication key stays safe. This shared secret `Z` value is kept
* confidential between the two parties in the key agreement protocol.
*
* @see {@link https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar3.pdf | NIST.800-56A}
* @see {@link https://datatracker.ietf.org/doc/html/rfc7518#section-4.6.2 | RFC 7518, Section 4.6.2}
*/
export class ConcatKdf {
/**
* Derives a key of a specified length from the input parameters.
*
* @example
* ```ts
* // Key Derivation
* const derivedKeyingMaterial = await ConcatKdf.deriveKey({
* sharedSecret: utils.randomBytes(32),
* keyDataLen: 128,
* fixedInfo: {
* algorithmId: "A128GCM",
* partyUInfo: "Alice",
* partyVInfo: "Bob",
* suppPubInfo: 128,
* },
* });
* ```
*
* @param params - Input parameters for key derivation.
* @param params.keyDataLen - The desired length of the derived key in bits.
* @param params.sharedSecret - The shared secret key to derive from.
* @param params.fixedInfo - Additional public information to use in key derivation.
* @returns The derived key as a Uint8Array.
*
* @throws {Error} If the `keyDataLen` would require multiple rounds.
*/
static deriveKey({ keyDataLen, fixedInfo, sharedSecret }) {
return __awaiter(this, void 0, void 0, function* () {
// RFC 7518 Section 4.6.2 specifies using SHA-256 for ECDH key agreement:
// "Key derivation is performed using the Concat KDF, as defined in
// Section 5.8.1 of [NIST.800-56A], where the Digest Method is SHA-256."
// Reference: https://tools.ietf.org/html/rfc7518#section-4.6.2
const hashLen = 256;
// This implementation only supports single round Concat KDF.
const roundCount = Math.ceil(keyDataLen / hashLen);
if (roundCount !== 1) {
throw new Error(`Concat KDF with ${roundCount} rounds not supported.`);
}
// Initialize a 32-bit, big-endian bit string counter as 0x00000001.
const counter = new Uint8Array(4);
new DataView(counter.buffer).setUint32(0, roundCount);
// Compute the FixedInfo bit-string.
const fixedInfoBytes = ConcatKdf.computeFixedInfo(fixedInfo);
// Compute K(i) = H(counter || Z || FixedInfo)
// return concatBytes(counter, sharedSecretZ, fixedInfo);
const derivedKeyingMaterial = sha256(concatBytes(counter, sharedSecret, fixedInfoBytes));
// Return the bit string of derived keying material of length keyDataLen bits.
return derivedKeyingMaterial.slice(0, keyDataLen / 8);
});
}
/**
* Computes the `FixedInfo` parameter for Concat KDF, which binds the derived key material to the
* context of the key agreement transaction.
*
* @remarks
* This implementation follows the recommended format for `FixedInfo` specified in section
* 5.8.1.2.1 of the NIST.800-56A publication.
*
* `FixedInfo` is a bit string equal to the following concatenation:
* `AlgorithmID || PartyUInfo || PartyVInfo {|| SuppPubInfo }{|| SuppPrivInfo }`.
*
* `SuppPubInfo` is the key length in bits, big endian encoded as a 32-bit number. For example,
* 128 would be [0, 0, 0, 128] and 256 would be [0, 0, 1, 0].
*
* @param params - Input data to construct FixedInfo.
* @returns FixedInfo as a Uint8Array.
*/
static computeFixedInfo(params) {
// Required sub-fields.
const algorithmId = ConcatKdf.toDataLenData({ data: params.algorithmId });
const partyUInfo = ConcatKdf.toDataLenData({ data: params.partyUInfo });
const partyVInfo = ConcatKdf.toDataLenData({ data: params.partyVInfo });
// Optional sub-fields.
const suppPubInfo = ConcatKdf.toDataLenData({ data: params.suppPubInfo, variableLength: false });
const suppPrivInfo = ConcatKdf.toDataLenData({ data: params.suppPrivInfo });
// Concatenate AlgorithmID || PartyUInfo || PartyVInfo || SuppPubInfo || SuppPrivInfo.
const fixedInfo = concatBytes(algorithmId, partyUInfo, partyVInfo, suppPubInfo, suppPrivInfo);
return fixedInfo;
}
/**
* Encodes input data as a length-prefixed byte string, or
* as a fixed-length bit string if specified.
*
* If variableLength = true, return the data in the form Datalen || Data,
* where Data is a variable-length string of zero or more (eight-bit)
* bytes, and Datalen is a fixed-length, big-endian counter that
* indicates the length (in bytes) of Data.
*
* If variableLength = false, return the data formatted as a
* fixed-length bit string.
*
* @param params - Input data and options for the conversion.
* @param params.data - The input data to encode. Must be a type convertible to Uint8Array by the Convert class.
* @param params.variableLength - Whether to output the data as variable length. Default is true.
*
* @returns The input data encoded as a Uint8Array.
*
* @throws {TypeError} If fixed-length data is not a number.
*/
static toDataLenData({ data, variableLength = true }) {
let encodedData;
const dataType = universalTypeOf(data);
// Return an emtpy octet sequence if data is not specified.
if (dataType === 'Undefined') {
return new Uint8Array(0);
}
if (variableLength) {
const dataU8A = (dataType === 'Uint8Array')
? data
: new Convert(data, dataType).toUint8Array();
const bufferLength = dataU8A.length;
encodedData = new Uint8Array(4 + bufferLength);
new DataView(encodedData.buffer).setUint32(0, bufferLength);
encodedData.set(dataU8A, 4);
}
else {
if (typeof data !== 'number') {
throw TypeError('Fixed length input must be a number.');
}
encodedData = new Uint8Array(4);
new DataView(encodedData.buffer).setUint32(0, data);
}
return encodedData;
}
}
//# sourceMappingURL=concat-kdf.js.map
@@ -0,0 +1 @@
{"version":3,"file":"concat-kdf.js","sourceRoot":"","sources":["../../../src/primitives/concat-kdf.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,521 @@
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 { ed25519, edwardsToMontgomeryPub, edwardsToMontgomeryPriv, x25519 } from '@noble/curves/ed25519';
import { computeJwkThumbprint, isOkpPrivateJwk, isOkpPublicJwk } from '../jose/jwk.js';
/**
* The `Ed25519` class provides a comprehensive suite of utilities for working with the Ed25519
* elliptic curve, widely used in modern cryptographic applications. This class includes methods for
* key generation, conversion, signing, verification, and public key derivation.
*
* The class supports conversions between raw byte formats and JSON Web Key (JWK) formats. It
* follows the guidelines and specifications outlined in RFC8032 for EdDSA (Edwards-curve Digital
* Signature Algorithm) operations.
*
* Key Features:
* - Key Generation: Generate Ed25519 private keys in JWK format.
* - Key Conversion: Transform keys between raw byte arrays and JWK formats.
* - Public Key Derivation: Derive public keys from private keys.
* - Signing and Verification: Sign data and verify signatures with Ed25519 keys.
* - Key Validation: Validate the mathematical correctness of Ed25519 keys.
*
* The methods in this class are asynchronous, returning Promises to accommodate various
* JavaScript environments, and use `Uint8Array` for binary data handling.
*
* @example
* ```ts
* // Key Generation
* const privateKey = await Ed25519.generateKey();
*
* // Public Key Derivation
* const publicKey = await Ed25519.computePublicKey({ key: privateKey });
* console.log(publicKey === await Ed25519.getPublicKey({ key: privateKey })); // Output: true
*
* // EdDSA Signing
* const signature = await Ed25519.sign({
* key: privateKey,
* data: new TextEncoder().encode('Message')
* });
*
* // EdDSA Signature Verification
* const isValid = await Ed25519.verify({
* key: publicKey,
* signature: signature,
* data: new TextEncoder().encode('Message')
* });
*
* // Key Conversion
* const privateKeyBytes = await Ed25519.privateKeyToBytes({ privateKey });
* const publicKeyBytes = await Ed25519.publicKeyToBytes({ publicKey });
*
* // Key Validation
* const isPublicKeyValid = await Ed25519.validatePublicKey({ publicKeyBytes });
* ```
*/
export class Ed25519 {
/**
* Converts a raw private key in bytes to its corresponding JSON Web Key (JWK) format.
*
* @remarks
* This method accepts a private key as a byte array (Uint8Array) for the Curve25519 curve in
* Twisted Edwards form and transforms it into a JWK object. The process involves first deriving
* the public key from the private key, then encoding both the private and public keys into
* base64url format.
*
* The resulting JWK object includes the following properties:
* - `kty`: Key Type, set to 'OKP' for Octet Key Pair.
* - `crv`: Curve Name, set to 'Ed25519'.
* - `d`: The private key component, base64url-encoded.
* - `x`: The computed public key, base64url-encoded.
*
* @example
* ```ts
* const privateKeyBytes = new Uint8Array([...]); // Replace with actual private key bytes
* const privateKey = await Ed25519.bytesToPrivateKey({ privateKeyBytes });
* ```
*
* @param params - The parameters for the private key conversion.
* @param params.privateKeyBytes - The raw private key as a Uint8Array.
*
* @returns A Promise that resolves to the private key in JWK format.
*/
static bytesToPrivateKey({ privateKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Derive the public key from the private key.
const publicKeyBytes = ed25519.getPublicKey(privateKeyBytes);
// Construct the private key in JWK format.
const privateKey = {
crv: 'Ed25519',
d: Convert.uint8Array(privateKeyBytes).toBase64Url(),
kty: 'OKP',
x: Convert.uint8Array(publicKeyBytes).toBase64Url(),
};
// Compute the JWK thumbprint and set as the key ID.
privateKey.kid = yield computeJwkThumbprint({ jwk: privateKey });
return privateKey;
});
}
/**
* Converts a raw private key in bytes to its corresponding JSON Web Key (JWK) format.
*
* @remarks
* This method accepts a public key as a byte array (Uint8Array) for the Curve25519 curve in
* Twisted Edwards form and transforms it into a JWK object. The process involves encoding the
* public key bytes into base64url format.
*
* The resulting JWK object includes the following properties:
* - `kty`: Key Type, set to 'OKP' for Octet Key Pair.
* - `crv`: Curve Name, set to 'X25519'.
* - `x`: The public key, base64url-encoded.
*
* @example
* ```ts
* const publicKeyBytes = new Uint8Array([...]); // Replace with actual public key bytes
* const publicKey = await X25519.bytesToPublicKey({ publicKeyBytes });
* ```
*
* @param params - The parameters for the public key conversion.
* @param params.publicKeyBytes - The raw public key as a `Uint8Array`.
*
* @returns A Promise that resolves to the public key in JWK format.
*/
static bytesToPublicKey({ publicKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Construct the public key in JWK format.
const publicKey = {
kty: 'OKP',
crv: 'Ed25519',
x: Convert.uint8Array(publicKeyBytes).toBase64Url(),
};
// Compute the JWK thumbprint and set as the key ID.
publicKey.kid = yield computeJwkThumbprint({ jwk: publicKey });
return publicKey;
});
}
/**
* Derives the public key in JWK format from a given Ed25519 private key.
*
* @remarks
* This method takes a private key in JWK format and derives its corresponding public key,
* also in JWK format. The derivation process involves converting the private key to a
* raw byte array and then computing the corresponding public key on the Curve25519 curve in
* Twisted Edwards form. The public key is then encoded into base64url format to construct
* a JWK representation.
*
* @example
* ```ts
* const privateKey = { ... }; // A Jwk object representing an Ed25519 private key
* const publicKey = await Ed25519.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 computed public key in JWK format.
*/
static computePublicKey({ key }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the provided private key to a byte array.
const privateKeyBytes = yield Ed25519.privateKeyToBytes({ privateKey: key });
// Derive the public key from the private key.
const publicKeyBytes = ed25519.getPublicKey(privateKeyBytes);
// Construct the public key in JWK format.
const publicKey = {
kty: 'OKP',
crv: 'Ed25519',
x: Convert.uint8Array(publicKeyBytes).toBase64Url()
};
// Compute the JWK thumbprint and set as the key ID.
publicKey.kid = yield computeJwkThumbprint({ jwk: publicKey });
return publicKey;
});
}
/**
* Converts an Ed25519 private key to its X25519 counterpart.
*
* @remarks
* This method enables the use of the same key pair for both digital signature (Ed25519)
* and key exchange (X25519) operations. It takes an Ed25519 private key and converts it
* to the corresponding X25519 format, facilitating interoperability between signing
* and encryption protocols.
*
* @example
* ```ts
* const ed25519PrivateKey = { ... }; // An Ed25519 private key in JWK format
* const x25519PrivateKey = await Ed25519.convertPrivateKeyToX25519({
* privateKey: ed25519PrivateKey
* });
* ```
*
* @param params - The parameters for the private key conversion.
* @param params.privateKey - The Ed25519 private key to convert, in JWK format.
*
* @returns A Promise that resolves to the X25519 private key in JWK format.
*/
static convertPrivateKeyToX25519({ privateKey }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the provided Ed25519 private key to bytes.
const ed25519PrivateKeyBytes = yield Ed25519.privateKeyToBytes({ privateKey });
// Convert the Ed25519 private key to an X25519 private key.
const x25519PrivateKeyBytes = edwardsToMontgomeryPriv(ed25519PrivateKeyBytes);
// Derive the X25519 public key from the X25519 private key.
const x25519PublicKeyBytes = x25519.getPublicKey(x25519PrivateKeyBytes);
// Construct the X25519 private key in JWK format.
const x25519PrivateKey = {
kty: 'OKP',
crv: 'X25519',
d: Convert.uint8Array(x25519PrivateKeyBytes).toBase64Url(),
x: Convert.uint8Array(x25519PublicKeyBytes).toBase64Url(),
};
// Compute the JWK thumbprint and set as the key ID.
x25519PrivateKey.kid = yield computeJwkThumbprint({ jwk: x25519PrivateKey });
return x25519PrivateKey;
});
}
/**
* Converts an Ed25519 public key to its X25519 counterpart.
*
* @remarks
* This method enables the use of the same key pair for both digital signature (Ed25519)
* and key exchange (X25519) operations. It takes an Ed25519 public key and converts it
* to the corresponding X25519 format, facilitating interoperability between signing
* and encryption protocols.
*
* @example
* ```ts
* const ed25519PublicKey = { ... }; // An Ed25519 public key in JWK format
* const x25519PublicKey = await Ed25519.convertPublicKeyToX25519({
* publicKey: ed25519PublicKey
* });
* ```
*
* @param params - The parameters for the public key conversion.
* @param params.publicKey - The Ed25519 public key to convert, in JWK format.
*
* @returns A Promise that resolves to the X25519 public key in JWK format.
*/
static convertPublicKeyToX25519({ publicKey }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the provided private key to a byte array.
const ed25519PublicKeyBytes = yield Ed25519.publicKeyToBytes({ publicKey });
// Verify Edwards public key is valid.
const isValid = yield Ed25519.validatePublicKey({ publicKeyBytes: ed25519PublicKeyBytes });
if (!isValid) {
throw new Error('Ed25519: Invalid public key.');
}
// Convert the Ed25519 public key to an X25519 private key.
const x25519PublicKeyBytes = edwardsToMontgomeryPub(ed25519PublicKeyBytes);
// Construct the X25519 private key in JWK format.
const x25519PublicKey = {
kty: 'OKP',
crv: 'X25519',
x: Convert.uint8Array(x25519PublicKeyBytes).toBase64Url(),
};
// Compute the JWK thumbprint and set as the key ID.
x25519PublicKey.kid = yield computeJwkThumbprint({ jwk: x25519PublicKey });
return x25519PublicKey;
});
}
/**
* Generates an Ed25519 private key in JSON Web Key (JWK) format.
*
* @remarks
* This method creates a new private key suitable for use with the Curve25519 elliptic curve in
* Twisted Edwards form. The key generation process involves using cryptographically secure
* random number generation to ensure the uniqueness and security of the key. The resulting
* private key adheres to the JWK format making it compatible with common cryptographic
* standards and easy to use in various cryptographic processes.
*
* The generated private key in JWK format includes the following components:
* - `kty`: Key Type, set to 'OKP' for Octet Key Pair.
* - `crv`: Curve Name, set to 'Ed25519'.
* - `d`: The private key component, base64url-encoded.
* - `x`: The derived public key, base64url-encoded.
*
* @example
* ```ts
* const privateKey = await Ed25519.generateKey();
* ```
*
* @returns A Promise that resolves to the generated private key in JWK format.
*/
static generateKey() {
return __awaiter(this, void 0, void 0, function* () {
// Generate a random private key.
const privateKeyBytes = ed25519.utils.randomPrivateKey();
// Convert private key from bytes to JWK format.
const privateKey = yield Ed25519.bytesToPrivateKey({ privateKeyBytes });
// Compute the JWK thumbprint and set as the key ID.
privateKey.kid = yield computeJwkThumbprint({ jwk: privateKey });
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 Ed25519 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. The method sets the 'kid' (key ID) property using the JWK thumbprint if it is not
* already defined. This approach is used under the assumption that a private key in JWK format
* always contains the corresponding public key properties.
*
* 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 privateKey = { ... }; // A Jwk object representing an Ed25519 private key
* const publicKey = await Ed25519.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.
*/
static getPublicKey({ key }) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
// Verify the provided JWK represents an octet key pair (OKP) Ed25519 private key.
if (!(isOkpPrivateJwk(key) && key.crv === 'Ed25519')) {
throw new Error(`Ed25519: The provided key is not an Ed25519 private JWK.`);
}
// Remove the private key property ('d') and make a shallow copy of the provided key.
let { d } = key, publicKey = __rest(key, ["d"]);
// If the key ID is undefined, set it to the JWK thumbprint.
(_a = publicKey.kid) !== null && _a !== void 0 ? _a : (publicKey.kid = yield computeJwkThumbprint({ jwk: publicKey }));
return publicKey;
});
}
/**
* Converts a private key from JSON Web Key (JWK) format to a raw byte array (Uint8Array).
*
* @remarks
* This method accepts a private key in JWK format and extracts its raw byte representation.
*
* This method accepts a public key in JWK format and converts it into its raw binary
* form. The conversion process involves decoding the 'd' parameter of the JWK
* from base64url format into a byte array.
*
* @example
* ```ts
* const privateKey = { ... }; // An Ed25519 private key in JWK format
* const privateKeyBytes = await Ed25519.privateKeyToBytes({ privateKey });
* ```
*
* @param params - The parameters for the private key conversion.
* @param params.privateKey - The private key in JWK format.
*
* @returns A Promise that resolves to the private key as a Uint8Array.
*/
static privateKeyToBytes({ privateKey }) {
return __awaiter(this, void 0, void 0, function* () {
// Verify the provided JWK represents a valid OKP private key.
if (!isOkpPrivateJwk(privateKey)) {
throw new Error(`Ed25519: The provided key is not a valid OKP private key.`);
}
// Decode the provided private key to bytes.
const privateKeyBytes = Convert.base64Url(privateKey.d).toUint8Array();
return privateKeyBytes;
});
}
/**
* Converts a public key from JSON Web Key (JWK) format to a raw byte array (Uint8Array).
*
* @remarks
* This method accepts a public key in JWK format and converts it into its raw binary form.
* The conversion process involves decoding the 'x' parameter of the JWK (which represent the
* x coordinate of the elliptic curve point) from base64url format into a byte array.
*
* @example
* ```ts
* const publicKey = { ... }; // An Ed25519 public key in JWK format
* const publicKeyBytes = await Ed25519.publicKeyToBytes({ publicKey });
* ```
*
* @param params - The parameters for the public key conversion.
* @param params.publicKey - The public key in JWK format.
*
* @returns A Promise that resolves to the public key as a Uint8Array.
*/
static publicKeyToBytes({ publicKey }) {
return __awaiter(this, void 0, void 0, function* () {
// Verify the provided JWK represents a valid OKP public key.
if (!isOkpPublicJwk(publicKey)) {
throw new Error(`Ed25519: The provided key is not a valid OKP public key.`);
}
// Decode the provided public key to bytes.
const publicKeyBytes = Convert.base64Url(publicKey.x).toUint8Array();
return publicKeyBytes;
});
}
/**
* Generates an RFC8032-compliant EdDSA signature of given data using an Ed25519 private key.
*
* @remarks
* This method signs the provided data with a specified private key using the EdDSA
* (Edwards-curve Digital Signature Algorithm) as defined in RFC8032. It
* involves converting the private key from JWK format to a byte array and then employing
* the Ed25519 algorithm to sign the data. The output is a digital signature in the form
* of a Uint8Array, uniquely corresponding to both the data and the private key used for
* signing.
*
* @example
* ```ts
* const data = new TextEncoder().encode('Messsage'); // Data to be signed
* const privateKey = { ... }; // A Jwk object representing an Ed25519 private key
* const signature = await Ed25519.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, represented as a Uint8Array.
*
* @returns A Promise that resolves to the signature as a Uint8Array.
*/
static sign({ key, data }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the private key from JWK format to bytes.
const privateKeyBytes = yield Ed25519.privateKeyToBytes({ privateKey: key });
// Sign the provided data using the EdDSA algorithm.
const signature = ed25519.sign(data, privateKeyBytes);
return signature;
});
}
/**
* Validates a given public key to confirm its mathematical correctness on the Edwards curve.
*
* @remarks
* This method decodes the Edwards points from the key bytes and asserts their validity on the
* Curve25519 curve in Twisted Edwards form. If the points are not valid, the method returns
* false. If the points are valid, the method returns true.
*
* Note that this validation strictly pertains to the key's format and numerical validity; it does
* not assess whether the key corresponds to a known entity or its security status (e.g., whether
* it has been compromised).
*
* @example
* ```ts
* const publicKeyBytes = new Uint8Array([...]); // A public key in byte format
* const isValid = await Ed25519.validatePublicKey({ publicKeyBytes });
* console.log(isValid); // true if the key is valid on the Edwards curve, false otherwise
* ```
*
* @param params - The parameters for the public key validation.
* @param params.publicKeyBytes - The public key to validate, represented as a Uint8Array.
*
* @returns A Promise that resolves to a boolean indicating whether the key
* corresponds to a valid point on the Edwards curve.
*/
static validatePublicKey({ publicKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
try {
// Decode Edwards points from key bytes.
const point = ed25519.ExtendedPoint.fromHex(publicKeyBytes);
// Check if points are on the Twisted Edwards curve.
point.assertValidity();
}
catch (error) {
return false;
}
return true;
});
}
/**
* Verifies an RFC8032-compliant EdDSA signature against given data using an Ed25519 public key.
*
* @remarks
* This method validates a digital signature to ensure its authenticity and integrity.
* It uses the EdDSA (Edwards-curve Digital Signature Algorithm) as specified in RFC8032.
* The verification process involves converting the public key from JWK format to a raw
* byte array and using the Ed25519 algorithm to validate the signature against the provided data.
*
* @example
* ```ts
* const data = new TextEncoder().encode('Messsage'); // Data that was signed
* const publicKey = { ... }; // A Jwk object representing an Ed25519 public key
* const signature = new Uint8Array([...]); // Signature to verify
* const isValid = await Ed25519.verify({ key: publicKey, signature, data });
* console.log(isValid); // true if the signature is valid, false otherwise
* ```
*
* @param params - The parameters for the signature verification.
* @param params.key - The public key in JWK format used for verification.
* @param params.signature - The signature to verify, represented as a Uint8Array.
* @param params.data - The data that was signed, represented as a Uint8Array.
*
* @returns A Promise that resolves to a boolean indicating whether the signature is valid.
*/
static verify({ key, signature, data }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the public key from JWK format to bytes.
const publicKeyBytes = yield Ed25519.publicKeyToBytes({ publicKey: key });
// Perform the verification of the signature.
const isValid = ed25519.verify(signature, data, publicKeyBytes);
return isValid;
});
}
}
//# sourceMappingURL=ed25519.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,78 @@
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 { crypto } from '@noble/hashes/crypto';
/**
* 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.
*
* This class offers a single static method `deriveKey` to perform key derivation.
*
* @example
* ```ts
* // Key Derivation
* const derivedKey = await Pbkdf2.deriveKey({
* hash: 'SHA-256', // The hash function to use ('SHA-256', 'SHA-384', 'SHA-512')
* password: new TextEncoder().encode('password'), // The password as a Uint8Array
* salt: new Uint8Array([...]), // The salt value
* iterations: 1000, // 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 derivedKey = await Pbkdf2.deriveKey({
* hash: 'SHA-256',
* password: new TextEncoder().encode('password'),
* salt: new Uint8Array([...]),
* iterations: 1000,
* length: 256
* });
* ```
*
* @param params - The parameters for key derivation.
* @param params.hash - The hash function to use, such as 'SHA-256', 'SHA-384', or 'SHA-512'.
* @param params.password - The password from which to derive the key, represented as a Uint8Array.
* @param params.salt - The salt value to use in the derivation process, as a Uint8Array.
* @param params.iterations - The number of iterations to apply in the PBKDF2 algorithm.
* @param params.length - The desired length of the derived key in bits.
*
* @returns A Promise that resolves to the derived key as a Uint8Array.
*/
static deriveKey({ hash, password, salt, iterations, length }) {
return __awaiter(this, void 0, void 0, function* () {
// Import the password as a raw key for use with the Web Crypto API.
const webCryptoKey = yield crypto.subtle.importKey('raw', password, { name: 'PBKDF2' }, false, ['deriveBits']);
const derivedKeyBuffer = yield crypto.subtle.deriveBits({ name: 'PBKDF2', hash, salt, iterations }, webCryptoKey, length);
// Convert from ArrayBuffer to Uint8Array.
const derivedKey = new Uint8Array(derivedKeyBuffer);
return derivedKey;
});
}
}
//# sourceMappingURL=pbkdf2.js.map
@@ -0,0 +1 @@
{"version":3,"file":"pbkdf2.js","sourceRoot":"","sources":["../../../src/primitives/pbkdf2.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AA0C9C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,OAAO,MAAM;IACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACI,MAAM,CAAO,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EACjD;;YAErB,oEAAoE;YACpE,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAChD,KAAK,EACL,QAAQ,EACR,EAAE,IAAI,EAAE,QAAQ,EAAE,EAClB,KAAK,EACL,CAAC,YAAY,CAAC,CACf,CAAC;YAEF,MAAM,gBAAgB,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CACrD,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,EAC1C,YAAY,EACZ,MAAM,CACP,CAAC;YAEF,0CAA0C;YAC1C,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC,gBAAgB,CAAC,CAAC;YAEpD,OAAO,UAAU,CAAC;QACpB,CAAC;KAAA;CACF"}
@@ -0,0 +1,805 @@
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 { sha256 } from '@noble/hashes/sha256';
import { secp256k1 } from '@noble/curves/secp256k1';
import { numberToBytesBE } from '@noble/curves/abstract/utils';
import { computeJwkThumbprint, isEcPrivateJwk, isEcPublicJwk } from '../jose/jwk.js';
/**
* The `Secp256k1` class provides a comprehensive suite of utilities for working with
* the secp256k1 elliptic curve, commonly used in blockchain and cryptographic applications.
* This class includes methods for key generation, conversion, signing, verification, and
* Elliptic Curve Diffie-Hellman (ECDH) key agreement.
*
* The class supports conversions between raw byte formats and JSON Web Key (JWK) formats. It
* adheres to RFC6979 for ECDSA signing and verification and RFC6090 for ECDH.
*
* Key Features:
* - Key Generation: Generate secp256k1 private keys in JWK format.
* - Key Conversion: Transform keys between raw byte arrays and JWK formats.
* - Public Key Derivation: Derive public keys from private keys.
* - ECDH Shared Secret Computation: Securely derive shared secrets using private and public keys.
* - ECDSA Signing and Verification: Sign data and verify signatures with secp256k1 keys.
* - Key Validation: Validate the mathematical correctness of secp256k1 keys.
*
* The methods in this class are asynchronous, returning Promises to accommodate various
* JavaScript environments, and use `Uint8Array` for binary data handling.
*
* @example
* ```ts
* // Key Generation
* const privateKey = await Secp256k1.generateKey();
*
* // Public Key Derivation
* const publicKey = await Secp256k1.computePublicKey({ key: privateKey });
* console.log(publicKey === await Secp256k1.getPublicKey({ key: privateKey })); // Output: true
*
* // ECDH Shared Secret Computation
* const sharedSecret = await Secp256k1.sharedSecret({
* privateKeyA: privateKey,
* publicKeyB: anotherPublicKey
* });
*
* // ECDSA Signing
* const signature = await Secp256k1.sign({
* key: privateKey,
* data: new TextEncoder().encode('Message')
* });
*
* // ECDSA Signature Verification
* const isValid = await Secp256k1.verify({
* key: publicKey,
* signature: signature,
* data: new TextEncoder().encode('Message')
* });
*
* // Key Conversion
* const publicKeyBytes = await Secp256k1.publicKeyToBytes({ publicKey });
* const privateKeyBytes = await Secp256k1.privateKeyToBytes({ privateKey });
* const compressedPublicKey = await Secp256k1.compressPublicKey({ publicKeyBytes });
* const uncompressedPublicKey = await Secp256k1.decompressPublicKey({ publicKeyBytes });
*
* // Key Validation
* const isPrivateKeyValid = await Secp256k1.validatePrivateKey({ privateKeyBytes });
* const isPublicKeyValid = await Secp256k1.validatePublicKey({ publicKeyBytes });
* ```
*/
export class Secp256k1 {
/**
* Adjusts an ECDSA signature to a normalized, low-S form.
*
* @remarks
* All ECDSA signatures, regardless of the curve, consist of two components, `r` and `s`, both of
* which are integers. The curve's order (the total number of points on the curve) is denoted by
* `n`. In a valid ECDSA signature, both `r` and `s` must be in the range [1, n-1]. However, due
* to the mathematical properties of ECDSA, if `(r, s)` is a valid signature, then `(r, n - s)` is
* also a valid signature for the same message and public key. In other words, for every
* signature, there's a "mirror" signature that's equally valid. For these elliptic curves:
*
* - Low S Signature: A signature where the `s` component is in the lower half of the range,
* specifically less than or equal to `n/2`.
*
* - High S Signature: This is where the `s` component is in the upper half of the range, greater
* than `n/2`.
*
* The practical implication is that a third-party can forge a second valid signature for the same
* message by negating the `s` component of the original signature, without any knowledge of the
* private key. This is known as a "signature malleability" attack.
*
* This type of forgery is not a problem in all systems, but it can be an issue in systems that
* rely on digital signature uniqueness to ensure transaction integrity. For example, in Bitcoin,
* transaction malleability is an issue because it allows for the modification of transaction
* identifiers (and potentially, transactions themselves) after they're signed but before they're
* confirmed in a block. By enforcing low `s` values, the Bitcoin network reduces the likelihood of
* this occurring, making the system more secure and predictable.
*
* For this reason, it's common practice to normalize ECDSA signatures to a low-S form. This
* form is considered standard and preferable in some systems and is known as the "normalized"
* form of the signature.
*
* This method takes a signature, and if it's high-S, returns the normalized low-S form. If the
* signature is already low-S, it's returned unmodified. It's important to note that this
* method does not change the validity of the signature but makes it compliant with systems that
* enforce low-S signatures.
*
* @example
* ```ts
* const signature = new Uint8Array([...]); // Your ECDSA signature
* const adjustedSignature = await Secp256k1.adjustSignatureToLowS({ signature });
* // Now 'adjustedSignature' is in the low-S form.
* ```
*
* @param params - The parameters for the signature adjustment.
* @param params.signature - The ECDSA signature as a `Uint8Array`.
*
* @returns A Promise that resolves to the adjusted signature in low-S form as a `Uint8Array`.
*/
static adjustSignatureToLowS({ signature }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the signature to a `secp256k1.Signature` object.
const signatureObject = secp256k1.Signature.fromCompact(signature);
if (signatureObject.hasHighS()) {
// Adjust the signature to low-S format if it's high-S.
const adjustedSignatureObject = signatureObject.normalizeS();
// Convert the adjusted signature object back to a byte array.
const adjustedSignature = adjustedSignatureObject.toCompactRawBytes();
return adjustedSignature;
}
else {
// Return the unmodified signature if it is already in low-S format.
return signature;
}
});
}
/**
* Converts a raw private key in bytes to its corresponding JSON Web Key (JWK) format.
*
* @remarks
* This method takes a private key represented as a byte array (Uint8Array) and
* converts it into a JWK object. The conversion involves extracting the
* elliptic curve point (x and y coordinates) from the private key and encoding
* them into base64url format, alongside other JWK parameters.
*
* The resulting JWK object includes the following properties:
* - `kty`: Key Type, set to 'EC' for Elliptic Curve.
* - `crv`: Curve Name, set to 'secp256k1'.
* - `d`: The private key component, base64url-encoded.
* - `x`: The x-coordinate of the public key point, base64url-encoded.
* - `y`: The y-coordinate of the public key point, base64url-encoded.
*
* This method is useful for converting raw public keys into a standardized
* JSON format, facilitating their use in cryptographic operations and making
* them easy to share and store.
*
* @example
* ```ts
* const privateKeyBytes = new Uint8Array([...]); // Replace with actual private key bytes
* const privateKey = await Secp256k1.bytesToPrivateKey({ privateKeyBytes });
* ```
*
* @param params - The parameters for the private key conversion.
* @param params.privateKeyBytes - The raw private key as a Uint8Array.
*
* @returns A Promise that resolves to the private key in JWK format.
*/
static bytesToPrivateKey({ privateKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Get the elliptic curve point (x and y coordinates) for the provided private key.
const point = yield Secp256k1.getCurvePoint({ keyBytes: privateKeyBytes });
// Construct the private key in JWK format.
const privateKey = {
kty: 'EC',
crv: 'secp256k1',
d: Convert.uint8Array(privateKeyBytes).toBase64Url(),
x: Convert.uint8Array(point.x).toBase64Url(),
y: Convert.uint8Array(point.y).toBase64Url()
};
// Compute the JWK thumbprint and set as the key ID.
privateKey.kid = yield computeJwkThumbprint({ jwk: privateKey });
return privateKey;
});
}
/**
* Converts a raw public key in bytes to its corresponding JSON Web Key (JWK) format.
*
* @remarks
* This method accepts a public key in a byte array (Uint8Array) format and
* transforms it to a JWK object. It involves decoding the elliptic curve point
* (x and y coordinates) from the raw public key bytes and encoding them into
* base64url format, along with setting appropriate JWK parameters.
*
* The resulting JWK object includes the following properties:
* - `kty`: Key Type, set to 'EC' for Elliptic Curve.
* - `crv`: Curve Name, set to 'secp256k1'.
* - `x`: The x-coordinate of the public key point, base64url-encoded.
* - `y`: The y-coordinate of the public key point, base64url-encoded.
*
* This method is useful for converting raw public keys into a standardized
* JSON format, facilitating their use in cryptographic operations and making
* them easy to share and store.
*
* @example
* ```ts
* const publicKeyBytes = new Uint8Array([...]); // Replace with actual public key bytes
* const publicKey = await Secp256k1.bytesToPublicKey({ publicKeyBytes });
* ```
*
* @param params - The parameters for the public key conversion.
* @param params.publicKeyBytes - The raw public key as a Uint8Array.
*
* @returns A Promise that resolves to the public key in JWK format.
*/
static bytesToPublicKey({ publicKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Get the elliptic curve point (x and y coordinates) for the provided public key.
const point = yield Secp256k1.getCurvePoint({ keyBytes: publicKeyBytes });
// Construct the public key in JWK format.
const publicKey = {
kty: 'EC',
crv: 'secp256k1',
x: Convert.uint8Array(point.x).toBase64Url(),
y: Convert.uint8Array(point.y).toBase64Url()
};
// Compute the JWK thumbprint and set as the key ID.
publicKey.kid = yield computeJwkThumbprint({ jwk: publicKey });
return publicKey;
});
}
/**
* Converts a public key to its compressed form.
*
* @remarks
* This method takes a public key represented as a byte array and compresses it. Public key
* compression is a process that reduces the size of the public key by removing the y-coordinate,
* making it more efficient for storage and transmission. The compressed key retains the same
* level of security as the uncompressed key.
*
* @example
* ```ts
* const uncompressedPublicKeyBytes = new Uint8Array([...]); // Replace with actual uncompressed public key bytes
* const compressedPublicKey = await Secp256k1.compressPublicKey({
* publicKeyBytes: uncompressedPublicKeyBytes
* });
* ```
*
* @param params - The parameters for the public key compression.
* @param params.publicKeyBytes - The public key as a Uint8Array.
*
* @returns A Promise that resolves to the compressed public key as a Uint8Array.
*/
static compressPublicKey({ publicKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Decode Weierstrass points from the public key byte array.
const point = secp256k1.ProjectivePoint.fromHex(publicKeyBytes);
// Return the compressed form of the public key.
return point.toRawBytes(true);
});
}
/**
* 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 derivation process involves converting the private key to a raw
* byte array, then computing the elliptic curve point (x and y coordinates) from this private
* key. These coordinates are then encoded into base64url format to construct the public key in
* JWK format.
*
* The process ensures that the derived public key correctly corresponds to the given private key,
* adhering to the secp256k1 elliptic curve standards. This method is useful in cryptographic
* operations where a public key is needed for operations like signature verification, but only
* the private key is available.
*
* @example
* ```ts
* const privateKey = { ... }; // A Jwk object representing a secp256k1 private key
* const publicKey = await Secp256k1.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.
*/
static computePublicKey({ key }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the provided private key to a byte array.
const privateKeyBytes = yield Secp256k1.privateKeyToBytes({ privateKey: key });
// Get the elliptic curve point (x and y coordinates) for the provided private key.
const point = yield Secp256k1.getCurvePoint({ keyBytes: privateKeyBytes });
// Construct the public key in JWK format.
const publicKey = {
kty: 'EC',
crv: 'secp256k1',
x: Convert.uint8Array(point.x).toBase64Url(),
y: Convert.uint8Array(point.y).toBase64Url()
};
// Compute the JWK thumbprint and set as the key ID.
publicKey.kid = yield computeJwkThumbprint({ jwk: publicKey });
return publicKey;
});
}
/**
* Converts an ASN.1 DER encoded ECDSA signature to a compact R+S format.
*
* @remarks
* This method is used for converting an ECDSA signature from the ASN.1 DER encoding to the more
* compact R+S format. This conversion is often required when dealing with ECDSA signatures in
* certain cryptographic standards such as JWS (JSON Web Signature).
*
* The method decodes the DER-encoded signature, extracts the R and S values, and concatenates
* them into a single byte array. This process involves handling the ASN.1 structure to correctly
* parse the R and S values, considering padding and integer encoding specifics of DER.
*
* @example
* ```ts
* const derSignature = new Uint8Array([...]); // Replace with your DER-encoded signature
* const signature = await Secp256k1.convertDerToCompactSignature({ derSignature });
* ```
*
* @param params - The parameters for the signature conversion.
* @param params.derSignature - The signature in ASN.1 DER format as a `Uint8Array`.
*
* @returns A Promise that resolves to the signature in compact R+S format as a `Uint8Array`.
*/
static convertDerToCompactSignature({ derSignature }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the DER-encoded signature into a `secp256k1.Signature` object.
// This involves parsing the ASN.1 DER structure to extract the R and S components.
const signatureObject = secp256k1.Signature.fromDER(derSignature);
// Convert the signature object into compact R+S format, which concatenates the R and S values
// into a single byte array.
const compactSignature = signatureObject.toCompactRawBytes();
return compactSignature;
});
}
/**
* Converts a public key to its uncompressed form.
*
* @remarks
* This method takes a compressed public key represented as a byte array and decompresses it.
* Public key decompression involves reconstructing the y-coordinate from the x-coordinate,
* resulting in the full public key. This method is used when the uncompressed key format is
* required for certain cryptographic operations or interoperability.
*
* @example
* ```ts
* const compressedPublicKeyBytes = new Uint8Array([...]); // Replace with actual compressed public key bytes
* const decompressedPublicKey = await Secp256k1.decompressPublicKey({
* publicKeyBytes: compressedPublicKeyBytes
* });
* ```
*
* @param params - The parameters for the public key decompression.
* @param params.publicKeyBytes - The public key as a Uint8Array.
*
* @returns A Promise that resolves to the uncompressed public key as a Uint8Array.
*/
static decompressPublicKey({ publicKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Decode Weierstrass points from the public key byte array.
const point = secp256k1.ProjectivePoint.fromHex(publicKeyBytes);
// Return the uncompressed form of the public key.
return point.toRawBytes(false);
});
}
/**
* Generates a secp256k1 private key in JSON Web Key (JWK) format.
*
* @remarks
* This method creates a new private key suitable for use with the secp256k1
* elliptic curve. The key is generated using cryptographically secure random
* number generation to ensure its uniqueness and security. The resulting
* private key adheres to the JWK format, specifically tailored for secp256k1,
* making it compatible with common cryptographic standards and easy to use in
* various cryptographic processes.
*
* The private key generated by this method includes the following components:
* - `kty`: Key Type, set to 'EC' for Elliptic Curve.
* - `crv`: Curve Name, set to 'secp256k1'.
* - `d`: The private key component, base64url-encoded.
* - `x`: The x-coordinate of the public key point, derived from the private key, base64url-encoded.
* - `y`: The y-coordinate of the public key point, derived from the private key, base64url-encoded.
*
* The key is returned in a format suitable for direct use in signin and key agreement operations.
*
* @example
* ```ts
* const privateKey = await Secp256k1.generateKey();
* ```
*
* @returns A Promise that resolves to the generated private key in JWK format.
*/
static generateKey() {
return __awaiter(this, void 0, void 0, function* () {
// Generate a random private key.
const privateKeyBytes = secp256k1.utils.randomPrivateKey();
// Convert private key from bytes to JWK format.
const privateKey = yield Secp256k1.bytesToPrivateKey({ privateKeyBytes });
// Compute the JWK thumbprint and set as the key ID.
privateKey.kid = yield computeJwkThumbprint({ jwk: privateKey });
return privateKey;
});
}
/**
* Retrieves the public key properties from a given private key in JWK format.
*
* @remarks
* This method extracts the public key portion from a secp256k1 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. The method sets the 'kid' (key ID) property using the JWK thumbprint if it is not
* already defined. This approach is used under the assumption that a private key in JWK format
* always contains the corresponding public key properties.
*
* 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 privateKey = { ... }; // A Jwk object representing a secp256k1 private key
* const publicKey = await Secp256k1.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.
*/
static getPublicKey({ key }) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
// Verify the provided JWK represents an elliptic curve (EC) secp256k1 private key.
if (!(isEcPrivateJwk(key) && key.crv === 'secp256k1')) {
throw new Error(`Secp256k1: The provided key is not a secp256k1 private JWK.`);
}
// Remove the private key property ('d') and make a shallow copy of the provided key.
let { d } = key, publicKey = __rest(key, ["d"]);
// If the key ID is undefined, set it to the JWK thumbprint.
(_a = publicKey.kid) !== null && _a !== void 0 ? _a : (publicKey.kid = yield computeJwkThumbprint({ jwk: publicKey }));
return publicKey;
});
}
/**
* Converts a private key from JSON Web Key (JWK) format to a raw byte array (Uint8Array).
*
* @remarks
* This method takes a private key in JWK format and extracts its raw byte representation.
* It specifically focuses on the 'd' parameter of the JWK, which represents the private
* key component in base64url encoding. The method decodes this value into a byte array.
*
* This conversion is essential for operations that require the private key in its raw
* binary form, such as certain low-level cryptographic operations or when interfacing
* with systems and libraries that expect keys in a byte array format.
*
* @example
* ```ts
* const privateKey = { ... }; // An X25519 private key in JWK format
* const privateKeyBytes = await Secp256k1.privateKeyToBytes({ privateKey });
* ```
*
* @param params - The parameters for the private key conversion.
* @param params.privateKey - The private key in JWK format.
*
* @returns A Promise that resolves to the private key as a Uint8Array.
*/
static privateKeyToBytes({ privateKey }) {
return __awaiter(this, void 0, void 0, function* () {
// Verify the provided JWK represents a valid EC secp256k1 private key.
if (!isEcPrivateJwk(privateKey)) {
throw new Error(`Secp256k1: The provided key is not a valid EC private key.`);
}
// Decode the provided private key to bytes.
const privateKeyBytes = Convert.base64Url(privateKey.d).toUint8Array();
return privateKeyBytes;
});
}
/**
* Converts a public key from JSON Web Key (JWK) format to a raw byte array (Uint8Array).
*
* @remarks
* This method accepts a public key in JWK format and converts it into its raw binary
* form. The conversion process involves decoding the 'x' and 'y' parameters of the JWK
* (which represent the x and y coordinates of the elliptic curve point, respectively)
* from base64url format into a byte array. The method then concatenates these values,
* along with a prefix indicating the key format, to form the full public key.
*
* This function is particularly useful for use cases where the public key is needed
* in its raw byte format, such as for certain cryptographic operations or when
* interfacing with systems that require raw key formats.
*
* @example
* ```ts
* const publicKey = { ... }; // A Jwk public key object
* const publicKeyBytes = await Secp256k1.publicKeyToBytes({ publicKey });
* ```
*
* @param params - The parameters for the public key conversion.
* @param params.publicKey - The public key in JWK format.
*
* @returns A Promise that resolves to the public key as a Uint8Array.
*/
static publicKeyToBytes({ publicKey }) {
return __awaiter(this, void 0, void 0, function* () {
// Verify the provided JWK represents a valid EC secp256k1 public key, which must have a 'y' value.
if (!(isEcPublicJwk(publicKey) && publicKey.y)) {
throw new Error(`Secp256k1: The provided key is not a valid EC public key.`);
}
// Decode the provided public key to bytes.
const prefix = new Uint8Array([0x04]); // Designates an uncompressed key.
const x = Convert.base64Url(publicKey.x).toUint8Array();
const y = Convert.base64Url(publicKey.y).toUint8Array();
// Concatenate the prefix, x-coordinate, and y-coordinate as a single byte array.
const publicKeyBytes = new Uint8Array([...prefix, ...x, ...y]);
return publicKeyBytes;
});
}
/**
* Computes an RFC6090-compliant Elliptic Curve Diffie-Hellman (ECDH) shared secret
* using secp256k1 private and public keys in JSON Web Key (JWK) format.
*
* @remarks
* This method facilitates the ECDH key agreement protocol, which is a method of securely
* deriving a shared secret between two parties based on their private and public keys.
* It takes the private key of one party (privateKeyA) and the public key of another
* party (publicKeyB) to compute a shared secret. The shared secret is derived from the
* x-coordinate of the elliptic curve point resulting from the multiplication of the
* public key with the private key.
*
* Note: When performing Elliptic Curve Diffie-Hellman (ECDH) key agreement,
* the resulting shared secret is a point on the elliptic curve, which
* consists of an x-coordinate and a y-coordinate. With a 256-bit curve like
* secp256k1, each of these coordinates is 32 bytes (256 bits) long. However,
* in the ECDH process, it's standard practice to use only the x-coordinate
* of the shared secret point as the resulting shared key. This is because
* the y-coordinate does not add to the entropy of the key, and both parties
* can independently compute the x-coordinate. Consquently, this implementation
* omits the y-coordinate for simplicity and standard compliance.
*
* @example
* ```ts
* const privateKeyA = { ... }; // A Jwk private key object for party A
* const publicKeyB = { ... }; // A Jwk public key object for party B
* const sharedSecret = await Secp256k1.sharedSecret({
* privateKeyA,
* publicKeyB
* });
* ```
*
* @param params - The parameters for the shared secret computation.
* @param params.privateKeyA - The private key in JWK format of one party.
* @param params.publicKeyB - The public key in JWK format of the other party.
*
* @returns A Promise that resolves to the computed shared secret as a Uint8Array.
*/
static sharedSecret({ privateKeyA, publicKeyB }) {
return __awaiter(this, void 0, void 0, function* () {
// Ensure that keys from the same key pair are not specified.
if ('x' in privateKeyA && 'x' in publicKeyB && privateKeyA.x === publicKeyB.x) {
throw new Error(`Secp256k1: ECDH shared secret cannot be computed from a single key pair's public and private keys.`);
}
// Convert the provided private and public keys to bytes.
const privateKeyABytes = yield Secp256k1.privateKeyToBytes({ privateKey: privateKeyA });
const publicKeyBBytes = yield Secp256k1.publicKeyToBytes({ publicKey: publicKeyB });
// Compute the compact representation shared secret between the public and private keys.
const sharedSecret = secp256k1.getSharedSecret(privateKeyABytes, publicKeyBBytes, true);
// Remove the leading byte that indicates the sign of the y-coordinate
// of the point on the elliptic curve. See note above.
return sharedSecret.slice(1);
});
}
/**
* Generates an RFC6979-compliant ECDSA signature of given data using a secp256k1 private key.
*
* @remarks
* This method signs the provided data with a specified private key using the ECDSA
* (Elliptic Curve Digital Signature Algorithm) signature algorithm, as defined in RFC6979.
* The data to be signed is first hashed using the SHA-256 algorithm, and this hash is then
* signed using the private key. The output is a digital signature in the form of a
* Uint8Array, which uniquely corresponds to both the data and the private key used for signing.
*
* This method is commonly used in cryptographic applications to ensure data integrity and
* authenticity. 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 data = new TextEncoder().encode('Messsage'); // Data to be signed
* const privateKey = { ... }; // A Jwk object representing a secp256k1 private key
* const signature = await Secp256k1.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, represented as a Uint8Array.
*
* @returns A Promise that resolves to the signature as a Uint8Array.
*/
static sign({ data, key }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the private key from JWK format to bytes.
const privateKeyBytes = yield Secp256k1.privateKeyToBytes({ privateKey: key });
// Generate a digest of the data using the SHA-256 hash function.
const digest = sha256(data);
// Sign the provided data using the ECDSA algorithm.
// The `secp256k1.sign` operation returns a signature object with { r, s, recovery } properties.
const signatureObject = secp256k1.sign(digest, privateKeyBytes);
// Convert the signature object to Uint8Array.
const signature = signatureObject.toCompactRawBytes();
return signature;
});
}
/**
* Validates a given private key to ensure its compliance with the secp256k1 curve standards.
*
* @remarks
* This method checks whether a provided private key is a valid 32-byte number and falls within
* the range defined by the secp256k1 curve's order. It is essential for ensuring the private
* key's mathematical correctness in the context of secp256k1-based cryptographic operations.
*
* Note that this validation strictly pertains to the key's format and numerical validity; it does
* not assess whether the key corresponds to a known entity or its security status (e.g., whether
* it has been compromised).
*
* @example
* ```ts
* const privateKeyBytes = new Uint8Array([...]); // A 32-byte private key
* const isValid = await Secp256k1.validatePrivateKey({ privateKeyBytes });
* console.log(isValid); // true or false based on the key's validity
* ```
*
* @param params - The parameters for the key validation.
* @param params.privateKeyBytes - The private key to validate, represented as a Uint8Array.
*
* @returns A Promise that resolves to a boolean indicating whether the private key is valid.
*/
static validatePrivateKey({ privateKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
return secp256k1.utils.isValidPrivateKey(privateKeyBytes);
});
}
/**
* Validates a given public key to confirm its mathematical correctness on the secp256k1 curve.
*
* @remarks
* This method checks if the provided public key represents a valid point on the secp256k1 curve.
* It decodes the key's Weierstrass points (x and y coordinates) and verifies their validity
* against the curve's parameters. A valid point must lie on the curve and meet specific
* mathematical criteria defined by the curve's equation.
*
* It's important to note that this method does not verify the key's ownership or whether it has
* been compromised; it solely focuses on the key's adherence to the curve's mathematical
* principles.
*
* @example
* ```ts
* const publicKeyBytes = new Uint8Array([...]); // A public key in byte format
* const isValid = await Secp256k1.validatePublicKey({ publicKeyBytes });
* console.log(isValid); // true if the key is valid on the secp256k1 curve, false otherwise
* ```
*
* @param params - The parameters for the key validation.
* @param params.publicKeyBytes - The public key to validate, represented as a Uint8Array.
*
* @returns A Promise that resolves to a boolean indicating the public key's validity on
* the secp256k1 curve.
*/
static validatePublicKey({ publicKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
try {
// Decode Weierstrass points from key bytes.
const point = secp256k1.ProjectivePoint.fromHex(publicKeyBytes);
// Check if points are on the Short Weierstrass curve.
point.assertValidity();
}
catch (error) {
return false;
}
return true;
});
}
/**
* Verifies an RFC6979-compliant ECDSA signature against given data and a secp256k1 public key.
*
* @remarks
* This method validates a digital signature to ensure that it was generated by the holder of the
* corresponding private key and that the signed data has not been altered. The signature
* verification is performed using the ECDSA (Elliptic Curve Digital Signature Algorithm) as
* specified in RFC6979. The data to be verified is first hashed using the SHA-256 algorithm, and
* this hash is then used along with the public key to verify the signature.
*
* The method returns a boolean value indicating whether the signature is valid. A valid signature
* proves that the signed data was indeed signed by the owner of the private key corresponding to
* the provided public key and that the data has not been tampered with since it was signed.
*
* Note: The verification process does not consider the malleability of low-s signatures, which
* may be relevant in certain contexts, such as Bitcoin transactions.
*
* @example
* ```ts
* const data = new TextEncoder().encode('Messsage'); // Data that was signed
* const publicKey = { ... }; // Public key in JWK format corresponding to the private key that signed the data
* const signature = new Uint8Array([...]); // Signature to verify
* const isSignatureValid = await Secp256k1.verify({
* key: publicKey,
* signature,
* data
* });
* console.log(isSignatureValid); // true if the signature is valid, false otherwise
* ```
*
* @param params - The parameters for the signature verification.
* @param params.key - The public key used for verification, represented in JWK format.
* @param params.signature - The signature to verify, represented as a Uint8Array.
* @param params.data - The data that was signed, represented as a Uint8Array.
*
* @returns A Promise that resolves to a boolean indicating whether the signature is valid.
*/
static verify({ key, signature, data }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the public key from JWK format to bytes.
const publicKeyBytes = yield Secp256k1.publicKeyToBytes({ publicKey: key });
// Generate a digest of the data using the SHA-256 hash function.
const digest = sha256(data);
/** Perform the verification of the signature.
* This verify operation has the malleability check disabled. Guaranteed support
* for low-s signatures across languages is unlikely especially in the context
* of SSI. Notable Cloud KMS providers do not natively support it either. It is
* also worth noting that low-s signatures are a requirement for Bitcoin. */
const isValid = secp256k1.verify(signature, digest, publicKeyBytes, { lowS: false });
return isValid;
});
}
/**
* Returns the elliptic curve point (x and y coordinates) for a given secp256k1 key.
*
* @remarks
* This method extracts the elliptic curve point from a given secp256k1 key, whether
* it's a private or a public key. For a private key, the method first computes the
* corresponding public key and then extracts the x and y coordinates. For a public key,
* it directly returns these coordinates. The coordinates are represented as Uint8Array.
*
* The x and y coordinates represent the key's position on the elliptic curve and can be
* used in various cryptographic operations, such as digital signatures or key agreement
* protocols.
*
* @example
* ```ts
* // For a private key
* const privateKey = new Uint8Array([...]); // A 32-byte private key
* const { x: xFromPrivateKey, y: yFromPrivateKey } = await Secp256k1.getCurvePoint({ keyBytes: privateKey });
*
* // For a public key
* const publicKey = new Uint8Array([...]); // A 33-byte or 65-byte public key
* const { x: xFromPublicKey, y: yFromPublicKey } = await Secp256k1.getCurvePoint({ keyBytes: publicKey });
* ```
*
* @param params - The parameters for the curve point decoding operation.
* @param params.keyBytes - The key for which to get the elliptic curve point.
* Can be either a private key or a public key.
* The key should be passed as a `Uint8Array`.
*
* @returns A Promise that resolves to an object with properties 'x' and 'y',
* each being a Uint8Array representing the x and y coordinates of the key point on the
* elliptic curve.
*/
static getCurvePoint({ keyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// If key is a private key, first compute the public key.
if (keyBytes.byteLength === 32) {
keyBytes = secp256k1.getPublicKey(keyBytes);
}
// Decode Weierstrass affine point from key bytes.
const point = secp256k1.ProjectivePoint.fromHex(keyBytes);
// Get x- and y-coordinate values and convert to Uint8Array.
const x = numberToBytesBE(point.x, 32);
const y = numberToBytesBE(point.y, 32);
return { x, y };
});
}
}
//# sourceMappingURL=secp256k1.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,806 @@
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 { sha256 } from '@noble/hashes/sha256';
import { secp256r1 } from '@noble/curves/p256';
import { numberToBytesBE } from '@noble/curves/abstract/utils';
import { computeJwkThumbprint, isEcPrivateJwk, isEcPublicJwk } from '../jose/jwk.js';
/**
* The `Secp256r1` class provides a comprehensive suite of utilities for working with
* the secp256r1 (aka P-256) elliptic curve, commonly used in blockchain and cryptographic
* applications. This class includes methods for key generation, conversion, signing, verification,
* and Elliptic Curve Diffie-Hellman (ECDH) key agreement.
*
* The class supports conversions between raw byte formats and JSON Web Key (JWK) formats. It
* adheres to RFC6979 for ECDSA signing and verification and RFC6090 for ECDH.
*
* Key Features:
* - Key Generation: Generate secp256r1 private keys in JWK format.
* - Key Conversion: Transform keys between raw byte arrays and JWK formats.
* - Public Key Derivation: Derive public keys from private keys.
* - ECDH Shared Secret Computation: Securely derive shared secrets using private and public keys.
* - ECDSA Signing and Verification: Sign data and verify signatures with secp256r1 keys.
* - Key Validation: Validate the mathematical correctness of secp256r1 keys.
*
* The methods in this class are asynchronous, returning Promises to accommodate various
* JavaScript environments, and use `Uint8Array` for binary data handling.
*
* @example
* ```ts
* // Key Generation
* const privateKey = await Secp256r1.generateKey();
*
* // Public Key Derivation
* const publicKey = await Secp256r1.computePublicKey({ key: privateKey });
* console.log(publicKey === await Secp256r1.getPublicKey({ key: privateKey })); // Output: true
*
* // ECDH Shared Secret Computation
* const sharedSecret = await Secp256r1.sharedSecret({
* privateKeyA: privateKey,
* publicKeyB: anotherPublicKey
* });
*
* // ECDSA Signing
* const signature = await Secp256r1.sign({
* key: privateKey,
* data: new TextEncoder().encode('Message')
* });
*
* // ECDSA Signature Verification
* const isValid = await Secp256r1.verify({
* key: publicKey,
* signature: signature,
* data: new TextEncoder().encode('Message')
* });
*
* // Key Conversion
* const publicKeyBytes = await Secp256r1.publicKeyToBytes({ publicKey });
* const privateKeyBytes = await Secp256r1.privateKeyToBytes({ privateKey });
* const compressedPublicKey = await Secp256r1.compressPublicKey({ publicKeyBytes });
* const uncompressedPublicKey = await Secp256r1.decompressPublicKey({ publicKeyBytes });
*
* // Key Validation
* const isPrivateKeyValid = await Secp256r1.validatePrivateKey({ privateKeyBytes });
* const isPublicKeyValid = await Secp256r1.validatePublicKey({ publicKeyBytes });
* ```
*/
export class Secp256r1 {
/**
* Adjusts an ECDSA signature to a normalized, low-S form.
*
* @remarks
* All ECDSA signatures, regardless of the curve, consist of two components, `r` and `s`, both of
* which are integers. The curve's order (the total number of points on the curve) is denoted by
* `n`. In a valid ECDSA signature, both `r` and `s` must be in the range [1, n-1]. However, due
* to the mathematical properties of ECDSA, if `(r, s)` is a valid signature, then `(r, n - s)` is
* also a valid signature for the same message and public key. In other words, for every
* signature, there's a "mirror" signature that's equally valid. For these elliptic curves:
*
* - Low S Signature: A signature where the `s` component is in the lower half of the range,
* specifically less than or equal to `n/2`.
*
* - High S Signature: This is where the `s` component is in the upper half of the range, greater
* than `n/2`.
*
* The practical implication is that a third-party can forge a second valid signature for the same
* message by negating the `s` component of the original signature, without any knowledge of the
* private key. This is known as a "signature malleability" attack.
*
* This type of forgery is not a problem in all systems, but it can be an issue in systems that
* rely on digital signature uniqueness to ensure transaction integrity. For example, in Bitcoin,
* transaction malleability is an issue because it allows for the modification of transaction
* identifiers (and potentially, transactions themselves) after they're signed but before they're
* confirmed in a block. By enforcing low `s` values, the Bitcoin network reduces the likelihood of
* this occurring, making the system more secure and predictable.
*
* For this reason, it's common practice to normalize ECDSA signatures to a low-S form. This
* form is considered standard and preferable in some systems and is known as the "normalized"
* form of the signature.
*
* This method takes a signature, and if it's high-S, returns the normalized low-S form. If the
* signature is already low-S, it's returned unmodified. It's important to note that this
* method does not change the validity of the signature but makes it compliant with systems that
* enforce low-S signatures.
*
* @example
* ```ts
* const signature = new Uint8Array([...]); // Your ECDSA signature
* const adjustedSignature = await Secp256r1.adjustSignatureToLowS({ signature });
* // Now 'adjustedSignature' is in the low-S form.
* ```
*
* @param params - The parameters for the signature adjustment.
* @param params.signature - The ECDSA signature as a `Uint8Array`.
*
* @returns A Promise that resolves to the adjusted signature in low-S form as a `Uint8Array`.
*/
static adjustSignatureToLowS({ signature }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the signature to a `Secp256r1.Signature` object.
const signatureObject = secp256r1.Signature.fromCompact(signature);
if (signatureObject.hasHighS()) {
// Adjust the signature to low-S format if it's high-S.
const adjustedSignatureObject = signatureObject.normalizeS();
// Convert the adjusted signature object back to a byte array.
const adjustedSignature = adjustedSignatureObject.toCompactRawBytes();
return adjustedSignature;
}
else {
// Return the unmodified signature if it is already in low-S format.
return signature;
}
});
}
/**
* Converts a raw private key in bytes to its corresponding JSON Web Key (JWK) format.
*
* @remarks
* This method takes a private key represented as a byte array (Uint8Array) and
* converts it into a JWK object. The conversion involves extracting the
* elliptic curve point (x and y coordinates) from the private key and encoding
* them into base64url format, alongside other JWK parameters.
*
* The resulting JWK object includes the following properties:
* - `kty`: Key Type, set to 'EC' for Elliptic Curve.
* - `crv`: Curve Name, set to 'P-256'.
* - `d`: The private key component, base64url-encoded.
* - `x`: The x-coordinate of the public key point, base64url-encoded.
* - `y`: The y-coordinate of the public key point, base64url-encoded.
*
* This method is useful for converting raw public keys into a standardized
* JSON format, facilitating their use in cryptographic operations and making
* them easy to share and store.
*
* @example
* ```ts
* const privateKeyBytes = new Uint8Array([...]); // Replace with actual private key bytes
* const privateKey = await Secp256r1.bytesToPrivateKey({ privateKeyBytes });
* ```
*
* @param params - The parameters for the private key conversion.
* @param params.privateKeyBytes - The raw private key as a Uint8Array.
*
* @returns A Promise that resolves to the private key in JWK format.
*/
static bytesToPrivateKey({ privateKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Get the elliptic curve points (x and y coordinates) for the provided private key.
const point = yield Secp256r1.getCurvePoint({ keyBytes: privateKeyBytes });
// Construct the private key in JWK format.
const privateKey = {
kty: 'EC',
crv: 'P-256',
d: Convert.uint8Array(privateKeyBytes).toBase64Url(),
x: Convert.uint8Array(point.x).toBase64Url(),
y: Convert.uint8Array(point.y).toBase64Url()
};
// Compute the JWK thumbprint and set as the key ID.
privateKey.kid = yield computeJwkThumbprint({ jwk: privateKey });
return privateKey;
});
}
/**
* Converts a raw public key in bytes to its corresponding JSON Web Key (JWK) format.
*
* @remarks
* This method accepts a public key in a byte array (Uint8Array) format and
* transforms it to a JWK object. It involves decoding the elliptic curve point
* (x and y coordinates) from the raw public key bytes and encoding them into
* base64url format, along with setting appropriate JWK parameters.
*
* The resulting JWK object includes the following properties:
* - `kty`: Key Type, set to 'EC' for Elliptic Curve.
* - `crv`: Curve Name, set to 'P-256'.
* - `x`: The x-coordinate of the public key point, base64url-encoded.
* - `y`: The y-coordinate of the public key point, base64url-encoded.
*
* This method is useful for converting raw public keys into a standardized
* JSON format, facilitating their use in cryptographic operations and making
* them easy to share and store.
*
* @example
* ```ts
* const publicKeyBytes = new Uint8Array([...]); // Replace with actual public key bytes
* const publicKey = await Secp256r1.bytesToPublicKey({ publicKeyBytes });
* ```
*
* @param params - The parameters for the public key conversion.
* @param params.publicKeyBytes - The raw public key as a Uint8Array.
*
* @returns A Promise that resolves to the public key in JWK format.
*/
static bytesToPublicKey({ publicKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Get the elliptic curve point (x and y coordinates) for the provided public key.
const point = yield Secp256r1.getCurvePoint({ keyBytes: publicKeyBytes });
// Construct the public key in JWK format.
const publicKey = {
kty: 'EC',
crv: 'P-256',
x: Convert.uint8Array(point.x).toBase64Url(),
y: Convert.uint8Array(point.y).toBase64Url()
};
// Compute the JWK thumbprint and set as the key ID.
publicKey.kid = yield computeJwkThumbprint({ jwk: publicKey });
return publicKey;
});
}
/**
* Converts a public key to its compressed form.
*
* @remarks
* This method takes a public key represented as a byte array and compresses it. Public key
* compression is a process that reduces the size of the public key by removing the y-coordinate,
* making it more efficient for storage and transmission. The compressed key retains the same
* level of security as the uncompressed key.
*
* @example
* ```ts
* const uncompressedPublicKeyBytes = new Uint8Array([...]); // Replace with actual uncompressed public key bytes
* const compressedPublicKey = await Secp256r1.compressPublicKey({
* publicKeyBytes: uncompressedPublicKeyBytes
* });
* ```
*
* @param params - The parameters for the public key compression.
* @param params.publicKeyBytes - The public key as a Uint8Array.
*
* @returns A Promise that resolves to the compressed public key as a Uint8Array.
*/
static compressPublicKey({ publicKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Decode Weierstrass points from the public key byte array.
const point = secp256r1.ProjectivePoint.fromHex(publicKeyBytes);
// Return the compressed form of the public key.
return point.toRawBytes(true);
});
}
/**
* 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 derivation process involves converting the private key to a raw
* byte array, then computing the elliptic curve point (x and y coordinates) from this private
* key. These coordinates are then encoded into base64url format to construct the public key in
* JWK format.
*
* The process ensures that the derived public key correctly corresponds to the given private key,
* adhering to the secp256r1 elliptic curve standards. This method is useful in cryptographic
* operations where a public key is needed for operations like signature verification, but only
* the private key is available.
*
* @example
* ```ts
* const privateKey = { ... }; // A Jwk object representing a secp256r1 private key
* const publicKey = await Secp256r1.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.
*/
static computePublicKey({ key }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the provided private key to a byte array.
const privateKeyBytes = yield Secp256r1.privateKeyToBytes({ privateKey: key });
// Get the elliptic curve point (x and y coordinates) for the provided private key.
const point = yield Secp256r1.getCurvePoint({ keyBytes: privateKeyBytes });
// Construct the public key in JWK format.
const publicKey = {
kty: 'EC',
crv: 'P-256',
x: Convert.uint8Array(point.x).toBase64Url(),
y: Convert.uint8Array(point.y).toBase64Url()
};
// Compute the JWK thumbprint and set as the key ID.
publicKey.kid = yield computeJwkThumbprint({ jwk: publicKey });
return publicKey;
});
}
/**
* Converts an ASN.1 DER encoded ECDSA signature to a compact R+S format.
*
* @remarks
* This method is used for converting an ECDSA signature from the ASN.1 DER encoding to the more
* compact R+S format. This conversion is often required when dealing with ECDSA signatures in
* certain cryptographic standards such as JWS (JSON Web Signature).
*
* The method decodes the DER-encoded signature, extracts the R and S values, and concatenates
* them into a single byte array. This process involves handling the ASN.1 structure to correctly
* parse the R and S values, considering padding and integer encoding specifics of DER.
*
* @example
* ```ts
* const derSignature = new Uint8Array([...]); // Replace with your DER-encoded signature
* const signature = await Secp256r1.convertDerToCompactSignature({ derSignature });
* ```
*
* @param params - The parameters for the signature conversion.
* @param params.derSignature - The signature in ASN.1 DER format as a `Uint8Array`.
*
* @returns A Promise that resolves to the signature in compact R+S format as a `Uint8Array`.
*/
static convertDerToCompactSignature({ derSignature }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the DER-encoded signature into a `Secp256r1.Signature` object.
// This involves parsing the ASN.1 DER structure to extract the R and S components.
const signatureObject = secp256r1.Signature.fromDER(derSignature);
// Convert the signature object into compact R+S format, which concatenates the R and S values
// into a single byte array.
const compactSignature = signatureObject.toCompactRawBytes();
return compactSignature;
});
}
/**
* Converts a public key to its uncompressed form.
*
* @remarks
* This method takes a compressed public key represented as a byte array and decompresses it.
* Public key decompression involves reconstructing the y-coordinate from the x-coordinate,
* resulting in the full public key. This method is used when the uncompressed key format is
* required for certain cryptographic operations or interoperability.
*
* @example
* ```ts
* const compressedPublicKeyBytes = new Uint8Array([...]); // Replace with actual compressed public key bytes
* const decompressedPublicKey = await Secp256r1.decompressPublicKey({
* publicKeyBytes: compressedPublicKeyBytes
* });
* ```
*
* @param params - The parameters for the public key decompression.
* @param params.publicKeyBytes - The public key as a Uint8Array.
*
* @returns A Promise that resolves to the uncompressed public key as a Uint8Array.
*/
static decompressPublicKey({ publicKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Decode Weierstrass points from the public key byte array.
const point = secp256r1.ProjectivePoint.fromHex(publicKeyBytes);
// Return the uncompressed form of the public key.
return point.toRawBytes(false);
});
}
/**
* Generates a secp256r1 private key in JSON Web Key (JWK) format.
*
* @remarks
* This method creates a new private key suitable for use with the secp256r1
* elliptic curve. The key is generated using cryptographically secure random
* number generation to ensure its uniqueness and security. The resulting
* private key adheres to the JWK format, specifically tailored for secp256r1,
* making it compatible with common cryptographic standards and easy to use in
* various cryptographic processes.
*
* The private key generated by this method includes the following components:
* - `kty`: Key Type, set to 'EC' for Elliptic Curve.
* - `crv`: Curve Name, set to 'P-256'.
* - `d`: The private key component, base64url-encoded.
* - `x`: The x-coordinate of the public key point, derived from the private key, base64url-encoded.
* - `y`: The y-coordinate of the public key point, derived from the private key, base64url-encoded.
*
* The key is returned in a format suitable for direct use in signin and key agreement operations.
*
* @example
* ```ts
* const privateKey = await Secp256r1.generateKey();
* ```
*
* @returns A Promise that resolves to the generated private key in JWK format.
*/
static generateKey() {
return __awaiter(this, void 0, void 0, function* () {
// Generate a random private key.
const privateKeyBytes = secp256r1.utils.randomPrivateKey();
// Convert private key from bytes to JWK format.
const privateKey = yield Secp256r1.bytesToPrivateKey({ privateKeyBytes });
// Compute the JWK thumbprint and set as the key ID.
privateKey.kid = yield computeJwkThumbprint({ jwk: privateKey });
return privateKey;
});
}
/**
* Retrieves the public key properties from a given private key in JWK format.
*
* @remarks
* This method extracts the public key portion from a secp256r1 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. The method sets the 'kid' (key ID) property using the JWK thumbprint if it is not
* already defined. This approach is used under the assumption that a private key in JWK format
* always contains the corresponding public key properties.
*
* 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 privateKey = { ... }; // A Jwk object representing a secp256r1 private key
* const publicKey = await Secp256r1.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.
*/
static getPublicKey({ key }) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
// Verify the provided JWK represents an elliptic curve (EC) secp256r1 private key.
if (!(isEcPrivateJwk(key) && key.crv === 'P-256')) {
throw new Error(`Secp256r1: The provided key is not a 'P-256' private JWK.`);
}
// Remove the private key property ('d') and make a shallow copy of the provided key.
let { d } = key, publicKey = __rest(key, ["d"]);
// If the key ID is undefined, set it to the JWK thumbprint.
(_a = publicKey.kid) !== null && _a !== void 0 ? _a : (publicKey.kid = yield computeJwkThumbprint({ jwk: publicKey }));
return publicKey;
});
}
/**
* Converts a private key from JSON Web Key (JWK) format to a raw byte array (Uint8Array).
*
* @remarks
* This method takes a private key in JWK format and extracts its raw byte representation.
* It specifically focuses on the 'd' parameter of the JWK, which represents the private
* key component in base64url encoding. The method decodes this value into a byte array.
*
* This conversion is essential for operations that require the private key in its raw
* binary form, such as certain low-level cryptographic operations or when interfacing
* with systems and libraries that expect keys in a byte array format.
*
* @example
* ```ts
* const privateKey = { ... }; // An X25519 private key in JWK format
* const privateKeyBytes = await Secp256r1.privateKeyToBytes({ privateKey });
* ```
*
* @param params - The parameters for the private key conversion.
* @param params.privateKey - The private key in JWK format.
*
* @returns A Promise that resolves to the private key as a Uint8Array.
*/
static privateKeyToBytes({ privateKey }) {
return __awaiter(this, void 0, void 0, function* () {
// Verify the provided JWK represents a valid EC P-256 private key.
if (!isEcPrivateJwk(privateKey)) {
throw new Error(`Secp256r1: The provided key is not a valid EC private key.`);
}
// Decode the provided private key to bytes.
const privateKeyBytes = Convert.base64Url(privateKey.d).toUint8Array();
return privateKeyBytes;
});
}
/**
* Converts a public key from JSON Web Key (JWK) format to a raw byte array (Uint8Array).
*
* @remarks
* This method accepts a public key in JWK format and converts it into its raw binary
* form. The conversion process involves decoding the 'x' and 'y' parameters of the JWK
* (which represent the x and y coordinates of the elliptic curve point, respectively)
* from base64url format into a byte array. The method then concatenates these values,
* along with a prefix indicating the key format, to form the full public key.
*
* This function is particularly useful for use cases where the public key is needed
* in its raw byte format, such as for certain cryptographic operations or when
* interfacing with systems that require raw key formats.
*
* @example
* ```ts
* const publicKey = { ... }; // A Jwk public key object
* const publicKeyBytes = await Secp256r1.publicKeyToBytes({ publicKey });
* ```
*
* @param params - The parameters for the public key conversion.
* @param params.publicKey - The public key in JWK format.
*
* @returns A Promise that resolves to the public key as a Uint8Array.
*/
static publicKeyToBytes({ publicKey }) {
return __awaiter(this, void 0, void 0, function* () {
// Verify the provided JWK represents a valid EC P-256 public key, which must have a 'y' value.
if (!(isEcPublicJwk(publicKey) && publicKey.y)) {
throw new Error(`Secp256r1: The provided key is not a valid EC public key.`);
}
// Decode the provided public key to bytes.
const prefix = new Uint8Array([0x04]); // Designates an uncompressed key.
const x = Convert.base64Url(publicKey.x).toUint8Array();
const y = Convert.base64Url(publicKey.y).toUint8Array();
// Concatenate the prefix, x-coordinate, and y-coordinate as a single byte array.
const publicKeyBytes = new Uint8Array([...prefix, ...x, ...y]);
return publicKeyBytes;
});
}
/**
* Computes an RFC6090-compliant Elliptic Curve Diffie-Hellman (ECDH) shared secret
* using secp256r1 private and public keys in JSON Web Key (JWK) format.
*
* @remarks
* This method facilitates the ECDH key agreement protocol, which is a method of securely
* deriving a shared secret between two parties based on their private and public keys.
* It takes the private key of one party (privateKeyA) and the public key of another
* party (publicKeyB) to compute a shared secret. The shared secret is derived from the
* x-coordinate of the elliptic curve point resulting from the multiplication of the
* public key with the private key.
*
* Note: When performing Elliptic Curve Diffie-Hellman (ECDH) key agreement,
* the resulting shared secret is a point on the elliptic curve, which
* consists of an x-coordinate and a y-coordinate. With a 256-bit curve like
* secp256r1, each of these coordinates is 32 bytes (256 bits) long. However,
* in the ECDH process, it's standard practice to use only the x-coordinate
* of the shared secret point as the resulting shared key. This is because
* the y-coordinate does not add to the entropy of the key, and both parties
* can independently compute the x-coordinate. Consquently, this implementation
* omits the y-coordinate for simplicity and standard compliance.
*
* @example
* ```ts
* const privateKeyA = { ... }; // A Jwk private key object for party A
* const publicKeyB = { ... }; // A Jwk public key object for party B
* const sharedSecret = await Secp256r1.sharedSecret({
* privateKeyA,
* publicKeyB
* });
* ```
*
* @param params - The parameters for the shared secret computation.
* @param params.privateKeyA - The private key in JWK format of one party.
* @param params.publicKeyB - The public key in JWK format of the other party.
*
* @returns A Promise that resolves to the computed shared secret as a Uint8Array.
*/
static sharedSecret({ privateKeyA, publicKeyB }) {
return __awaiter(this, void 0, void 0, function* () {
// Ensure that keys from the same key pair are not specified.
if ('x' in privateKeyA && 'x' in publicKeyB && privateKeyA.x === publicKeyB.x) {
throw new Error(`Secp256r1: ECDH shared secret cannot be computed from a single key pair's public and private keys.`);
}
// Convert the provided private and public keys to bytes.
const privateKeyABytes = yield Secp256r1.privateKeyToBytes({ privateKey: privateKeyA });
const publicKeyBBytes = yield Secp256r1.publicKeyToBytes({ publicKey: publicKeyB });
// Compute the compact representation shared secret between the public and private keys.
const sharedSecret = secp256r1.getSharedSecret(privateKeyABytes, publicKeyBBytes, true);
// Remove the leading byte that indicates the sign of the y-coordinate
// of the point on the elliptic curve. See note above.
return sharedSecret.slice(1);
});
}
/**
* Generates an RFC6979-compliant ECDSA signature of given data using a secp256r1 private key.
*
* @remarks
* This method signs the provided data with a specified private key using the ECDSA
* (Elliptic Curve Digital Signature Algorithm) signature algorithm, as defined in RFC6979.
* The data to be signed is first hashed using the SHA-256 algorithm, and this hash is then
* signed using the private key. The output is a digital signature in the form of a
* Uint8Array, which uniquely corresponds to both the data and the private key used for signing.
*
* This method is commonly used in cryptographic applications to ensure data integrity and
* authenticity. 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 data = new TextEncoder().encode('Messsage'); // Data to be signed
* const privateKey = { ... }; // A Jwk object representing a secp256r1 private key
* const signature = await Secp256r1.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, represented as a Uint8Array.
*
* @returns A Promise that resolves to the signature as a Uint8Array.
*/
static sign({ data, key }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the private key from JWK format to bytes.
const privateKeyBytes = yield Secp256r1.privateKeyToBytes({ privateKey: key });
// Generate a digest of the data using the SHA-256 hash function.
const digest = sha256(data);
// Sign the provided data using the ECDSA algorithm.
// The `Secp256r1.sign` operation returns a signature object with { r, s, recovery } properties.
const signatureObject = secp256r1.sign(digest, privateKeyBytes);
// Convert the signature object to Uint8Array.
const signature = signatureObject.toCompactRawBytes();
return signature;
});
}
/**
* Validates a given private key to ensure its compliance with the secp256r1 curve standards.
*
* @remarks
* This method checks whether a provided private key is a valid 32-byte number and falls within
* the range defined by the secp256r1 curve's order. It is essential for ensuring the private
* key's mathematical correctness in the context of secp256r1-based cryptographic operations.
*
* Note that this validation strictly pertains to the key's format and numerical validity; it does
* not assess whether the key corresponds to a known entity or its security status (e.g., whether
* it has been compromised).
*
* @example
* ```ts
* const privateKeyBytes = new Uint8Array([...]); // A 32-byte private key
* const isValid = await Secp256r1.validatePrivateKey({ privateKeyBytes });
* console.log(isValid); // true or false based on the key's validity
* ```
*
* @param params - The parameters for the key validation.
* @param params.privateKeyBytes - The private key to validate, represented as a Uint8Array.
*
* @returns A Promise that resolves to a boolean indicating whether the private key is valid.
*/
static validatePrivateKey({ privateKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
return secp256r1.utils.isValidPrivateKey(privateKeyBytes);
});
}
/**
* Validates a given public key to confirm its mathematical correctness on the secp256r1 curve.
*
* @remarks
* This method checks if the provided public key represents a valid point on the secp256r1 curve.
* It decodes the key's Weierstrass points (x and y coordinates) and verifies their validity
* against the curve's parameters. A valid point must lie on the curve and meet specific
* mathematical criteria defined by the curve's equation.
*
* It's important to note that this method does not verify the key's ownership or whether it has
* been compromised; it solely focuses on the key's adherence to the curve's mathematical
* principles.
*
* @example
* ```ts
* const publicKeyBytes = new Uint8Array([...]); // A public key in byte format
* const isValid = await Secp256r1.validatePublicKey({ publicKeyBytes });
* console.log(isValid); // true if the key is valid on the secp256r1 curve, false otherwise
* ```
*
* @param params - The parameters for the key validation.
* @param params.publicKeyBytes - The public key to validate, represented as a Uint8Array.
*
* @returns A Promise that resolves to a boolean indicating the public key's validity on
* the secp256r1 curve.
*/
static validatePublicKey({ publicKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
try {
// Decode Weierstrass points from key bytes.
const point = secp256r1.ProjectivePoint.fromHex(publicKeyBytes);
// Check if points are on the Short Weierstrass curve.
point.assertValidity();
}
catch (error) {
return false;
}
return true;
});
}
/**
* Verifies an RFC6979-compliant ECDSA signature against given data and a secp256r1 public key.
*
* @remarks
* This method validates a digital signature to ensure that it was generated by the holder of the
* corresponding private key and that the signed data has not been altered. The signature
* verification is performed using the ECDSA (Elliptic Curve Digital Signature Algorithm) as
* specified in RFC6979. The data to be verified is first hashed using the SHA-256 algorithm, and
* this hash is then used along with the public key to verify the signature.
*
* The method returns a boolean value indicating whether the signature is valid. A valid signature
* proves that the signed data was indeed signed by the owner of the private key corresponding to
* the provided public key and that the data has not been tampered with since it was signed.
*
* Note: The verification process does not consider the malleability of low-s signatures, which
* may be relevant in certain contexts, such as Bitcoin transactions.
*
* @example
* ```ts
* const data = new TextEncoder().encode('Messsage'); // Data that was signed
* const publicKey = { ... }; // Public key in JWK format corresponding to the private key that signed the data
* const signature = new Uint8Array([...]); // Signature to verify
* const isSignatureValid = await Secp256r1.verify({
* key: publicKey,
* signature,
* data
* });
* console.log(isSignatureValid); // true if the signature is valid, false otherwise
* ```
*
* @param params - The parameters for the signature verification.
* @param params.key - The public key used for verification, represented in JWK format.
* @param params.signature - The signature to verify, represented as a Uint8Array.
* @param params.data - The data that was signed, represented as a Uint8Array.
*
* @returns A Promise that resolves to a boolean indicating whether the signature is valid.
*/
static verify({ key, signature, data }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the public key from JWK format to bytes.
const publicKeyBytes = yield Secp256r1.publicKeyToBytes({ publicKey: key });
// Generate a digest of the data using the SHA-256 hash function.
const digest = sha256(data);
/** Perform the verification of the signature.
* This verify operation has the malleability check disabled. Guaranteed support
* for low-s signatures across languages is unlikely especially in the context
* of SSI. Notable Cloud KMS providers do not natively support it either. It is
* also worth noting that low-s signatures are a requirement for Bitcoin. */
const isValid = secp256r1.verify(signature, digest, publicKeyBytes, { lowS: false });
return isValid;
});
}
/**
* Returns the elliptic curve point (x and y coordinates) for a given secp256r1 key.
*
* @remarks
* This method extracts the elliptic curve point from a given secp256r1 key, whether
* it's a private or a public key. For a private key, the method first computes the
* corresponding public key and then extracts the x and y coordinates. For a public key,
* it directly returns these coordinates. The coordinates are represented as Uint8Array.
*
* The x and y coordinates represent the key's position on the elliptic curve and can be
* used in various cryptographic operations, such as digital signatures or key agreement
* protocols.
*
* @example
* ```ts
* // For a private key
* const privateKey = new Uint8Array([...]); // A 32-byte private key
* const { x: xFromPrivateKey, y: yFromPrivateKey } = await Secp256r1.getCurvePoint({ keyBytes: privateKey });
*
* // For a public key
* const publicKey = new Uint8Array([...]); // A 33-byte or 65-byte public key
* const { x: xFromPublicKey, y: yFromPublicKey } = await Secp256r1.getCurvePoint({ keyBytes: publicKey });
* ```
*
* @param params - The parameters for the curve point decoding operation.
* @param params.keyBytes - The key for which to get the elliptic curve point.
* Can be either a private key or a public key.
* The key should be passed as a `Uint8Array`.
*
* @returns A Promise that resolves to an object with properties 'x' and 'y',
* each being a Uint8Array representing the x and y coordinates of the key point on the
* elliptic curve.
*/
static getCurvePoint({ keyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// If key is a private key, first compute the public key.
if (keyBytes.byteLength === 32) {
keyBytes = secp256r1.getPublicKey(keyBytes);
}
// Decode Weierstrass affine point from key bytes.
const point = secp256r1.ProjectivePoint.fromHex(keyBytes);
// Get x- and y-coordinate values and convert to Uint8Array.
const x = numberToBytesBE(point.x, 32);
const y = numberToBytesBE(point.y, 32);
return { x, y };
});
}
}
export { Secp256r1 as P256 };
//# sourceMappingURL=secp256r1.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,55 @@
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 { sha256 } from '@noble/hashes/sha256';
/**
* The `Sha256` class provides an interface for generating SHA-256 hash digests.
*
* This class utilizes the '@noble/hashes/sha256' function to generate hash digests
* of the provided data. The SHA-256 algorithm is widely used in cryptographic
* applications to produce a fixed-size 256-bit (32-byte) hash.
*
* The methods of this class are asynchronous and return Promises. They use the Uint8Array
* type for input data and the resulting digest, ensuring a consistent interface
* for binary data processing.
*
* @example
* ```ts
* const data = new Uint8Array([...]);
* const hash = await Sha256.digest({ data });
* ```
*/
export class Sha256 {
/**
* Generates a SHA-256 hash digest for the given data.
*
* @remarks
* This method produces a hash digest using the SHA-256 algorithm. The resultant digest
* is deterministic, meaning the same data will always produce the same hash, but
* is computationally infeasible to regenerate the original data from the hash.
*
* @example
* ```ts
* const data = new Uint8Array([...]);
* const hash = await Sha256.digest({ data });
* ```
*
* @param params - The parameters for the hashing operation.
* @param params.data - The data to hash, represented as a Uint8Array.
*
* @returns A Promise that resolves to the SHA-256 hash digest of the provided data as a Uint8Array.
*/
static digest({ data }) {
return __awaiter(this, void 0, void 0, function* () {
const hash = sha256(data);
return hash;
});
}
}
//# sourceMappingURL=sha256.js.map
@@ -0,0 +1 @@
{"version":3,"file":"sha256.js","sourceRoot":"","sources":["../../../src/primitives/sha256.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,MAAM;IACjB;;;;;;;;;;;;;;;;;;OAkBG;IACI,MAAM,CAAO,MAAM,CAAC,EAAE,IAAI,EAEhC;;YACC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;YAE1B,OAAO,IAAI,CAAC;QACd,CAAC;KAAA;CACF"}
+392
View File
@@ -0,0 +1,392 @@
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 { x25519 } from '@noble/curves/ed25519';
import { computeJwkThumbprint, isOkpPrivateJwk, isOkpPublicJwk } from '../jose/jwk.js';
/**
* The `X25519` class provides a comprehensive suite of utilities for working with the X25519
* elliptic curve, widely used for key agreement protocols and cryptographic applications. It
* provides methods for key generation, conversion, and Elliptic Curve Diffie-Hellman (ECDH)
* key agreement, all aligned with standard cryptographic practices.
*
* The class supports conversions between raw byte formats and JSON Web Key (JWK) formats,
* making it versatile for various cryptographic tasks. It adheres to RFC6090 for ECDH, ensuring
* secure and effective handling of keys and cryptographic operations.
*
* Key Features:
* - Key Generation: Generate X25519 private keys in JWK format.
* - Key Conversion: Transform keys between raw byte arrays and JWK formats.
* - Public Key Derivation: Derive public keys from private keys.
* - ECDH Shared Secret Computation: Securely derive shared secrets using private and public keys.
*
* The methods in this class are asynchronous, returning Promises to accommodate various
* JavaScript environments.
*
* @example
* ```ts
* // Key Generation
* const privateKey = await X25519.generateKey();
*
* // Public Key Derivation
* const publicKey = await X25519.computePublicKey({ key: privateKey });
* console.log(publicKey === await X25519.getPublicKey({ key: privateKey })); // Output: true
*
* // ECDH Shared Secret Computation
* const sharedSecret = await X25519.sharedSecret({
* privateKeyA: privateKey,
* publicKeyB: anotherPublicKey
* });
*
* // Key Conversion
* const publicKeyBytes = await X25519.publicKeyToBytes({ publicKey });
* const privateKeyBytes = await X25519.privateKeyToBytes({ privateKey });
* ```
*/
export class X25519 {
/**
* Converts a raw private key in bytes to its corresponding JSON Web Key (JWK) format.
*
* @remarks
* This method accepts a private key as a byte array (Uint8Array) for the X25519 elliptic curve
* and transforms it into a JWK object. The process involves first deriving the public key from
* the private key, then encoding both the private and public keys into base64url format.
*
* The resulting JWK object includes the following properties:
* - `kty`: Key Type, set to 'OKP' for Octet Key Pair.
* - `crv`: Curve Name, set to 'X25519'.
* - `d`: The private key component, base64url-encoded.
* - `x`: The derived public key, base64url-encoded.
*
* This method is useful for converting raw public keys into a standardized
* JSON format, facilitating their use in cryptographic operations and making
* them easy to share and store.
*
* @example
* ```ts
* const privateKeyBytes = new Uint8Array([...]); // Replace with actual private key bytes
* const privateKey = await X25519.bytesToPrivateKey({ privateKeyBytes });
* ```
*
* @param params - The parameters for the private key conversion.
* @param params.privateKeyBytes - The raw private key as a Uint8Array.
*
* @returns A Promise that resolves to the private key in JWK format.
*/
static bytesToPrivateKey({ privateKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Derive the public key from the private key.
const publicKeyBytes = x25519.getPublicKey(privateKeyBytes);
// Construct the private key in JWK format.
const privateKey = {
kty: 'OKP',
crv: 'X25519',
d: Convert.uint8Array(privateKeyBytes).toBase64Url(),
x: Convert.uint8Array(publicKeyBytes).toBase64Url(),
};
// Compute the JWK thumbprint and set as the key ID.
privateKey.kid = yield computeJwkThumbprint({ jwk: privateKey });
return privateKey;
});
}
/**
* Converts a raw public key in bytes to its corresponding JSON Web Key (JWK) format.
*
* @remarks
* This method accepts a public key as a byte array (Uint8Array) for the X25519 elliptic curve
* and transforms it into a JWK object. The conversion process involves encoding the public
* key bytes into base64url format.
*
* The resulting JWK object includes the following properties:
* - `kty`: Key Type, set to 'OKP' for Octet Key Pair.
* - `crv`: Curve Name, set to 'X25519'.
* - `x`: The public key, base64url-encoded.
*
* This method is useful for converting raw public keys into a standardized
* JSON format, facilitating their use in cryptographic operations and making
* them easy to share and store.
*
* @example
* ```ts
* const publicKeyBytes = new Uint8Array([...]); // Replace with actual public key bytes
* const publicKey = await X25519.bytesToPublicKey({ publicKeyBytes });
* ```
*
* @param params - The parameters for the public key conversion.
* @param params.publicKeyBytes - The raw public key as a Uint8Array.
*
* @returns A Promise that resolves to the public key in JWK format.
*/
static bytesToPublicKey({ publicKeyBytes }) {
return __awaiter(this, void 0, void 0, function* () {
// Construct the public key in JWK format.
const publicKey = {
kty: 'OKP',
crv: 'X25519',
x: Convert.uint8Array(publicKeyBytes).toBase64Url(),
};
// Compute the JWK thumbprint and set as the key ID.
publicKey.kid = yield computeJwkThumbprint({ jwk: publicKey });
return publicKey;
});
}
/**
* Derives the public key in JWK format from a given X25519 private key.
*
* @remarks
* This method takes a private key in JWK format and derives its corresponding public key,
* also in JWK format. The derivation process involves converting the private key to a
* raw byte array and then computing the corresponding public key on the Curve25519 curve.
* The public key is then encoded into base64url format to construct a JWK representation.
*
* The process ensures that the derived public key correctly corresponds to the given private key,
* adhering to the Curve25519 elliptic curve in Twisted Edwards form standards. This method is
* useful in cryptographic operations where a public key is needed for operations like signature
* verification, but only the private key is available.
*
* @example
* ```ts
* const privateKey = { ... }; // A Jwk object representing an X25519 private key
* const publicKey = await X25519.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.
*/
static computePublicKey({ key }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the provided private key to a byte array.
const privateKeyBytes = yield X25519.privateKeyToBytes({ privateKey: key });
// Derive the public key from the private key.
const publicKeyBytes = x25519.getPublicKey(privateKeyBytes);
// Construct the public key in JWK format.
const publicKey = {
kty: 'OKP',
crv: 'X25519',
x: Convert.uint8Array(publicKeyBytes).toBase64Url()
};
// Compute the JWK thumbprint and set as the key ID.
publicKey.kid = yield computeJwkThumbprint({ jwk: publicKey });
return publicKey;
});
}
/**
* Generates an X25519 private key in JSON Web Key (JWK) format.
*
* @remarks
* This method creates a new private key suitable for use with the X25519 elliptic curve.
* The key generation process involves using cryptographically secure random number generation
* to ensure the uniqueness and security of the key. The resulting private key adheres to the
* JWK format making it compatible with common cryptographic standards and easy to use in various
* cryptographic processes.
*
* The generated private key in JWK format includes the following components:
* - `kty`: Key Type, set to 'OKP' for Octet Key Pair.
* - `crv`: Curve Name, set to 'X25519'.
* - `d`: The private key component, base64url-encoded.
* - `x`: The derived public key, base64url-encoded.
*
* The key is returned in a format suitable for direct use in key agreement operations.
*
* @example
* ```ts
* const privateKey = await X25519.generateKey();
* ```
*
* @returns A Promise that resolves to the generated private key in JWK format.
*/
static generateKey() {
return __awaiter(this, void 0, void 0, function* () {
// Generate a random private key.
const privateKeyBytes = x25519.utils.randomPrivateKey();
// Convert private key from bytes to JWK format.
const privateKey = yield X25519.bytesToPrivateKey({ privateKeyBytes });
// Compute the JWK thumbprint and set as the key ID.
privateKey.kid = yield computeJwkThumbprint({ jwk: privateKey });
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 X25519 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. The method sets the 'kid' (key ID) property using the JWK thumbprint if it is not
* already defined. This approach is used under the assumption that a private key in JWK format
* always contains the corresponding public key properties.
*
* Note: This method offers a significant performance advantage, being about 500 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 privateKey = { ... }; // A Jwk object representing an X25519 private key
* const publicKey = await X25519.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.
*/
static getPublicKey({ key }) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
// Verify the provided JWK represents an octet key pair (OKP) X25519 private key.
if (!(isOkpPrivateJwk(key) && key.crv === 'X25519')) {
throw new Error(`X25519: The provided key is not an X25519 private JWK.`);
}
// Remove the private key property ('d') and make a shallow copy of the provided key.
let { d } = key, publicKey = __rest(key, ["d"]);
// If the key ID is undefined, set it to the JWK thumbprint.
(_a = publicKey.kid) !== null && _a !== void 0 ? _a : (publicKey.kid = yield computeJwkThumbprint({ jwk: publicKey }));
return publicKey;
});
}
/**
* Converts a private key from JSON Web Key (JWK) format to a raw byte array (Uint8Array).
*
* @remarks
* This method accepts a private key in JWK format and extracts its raw byte representation.
*
* This method accepts a public key in JWK format and converts it into its raw binary
* form. The conversion process involves decoding the 'd' parameter of the JWK
* from base64url format into a byte array.
*
* This conversion is essential for operations that require the private key in its raw
* binary form, such as certain low-level cryptographic operations or when interfacing
* with systems and libraries that expect keys in a byte array format.
*
* @example
* ```ts
* const privateKey = { ... }; // An X25519 private key in JWK format
* const privateKeyBytes = await X25519.privateKeyToBytes({ privateKey });
* ```
*
* @param params - The parameters for the private key conversion.
* @param params.privateKey - The private key in JWK format.
*
* @returns A Promise that resolves to the private key as a Uint8Array.
*/
static privateKeyToBytes({ privateKey }) {
return __awaiter(this, void 0, void 0, function* () {
// Verify the provided JWK represents a valid OKP private key.
if (!isOkpPrivateJwk(privateKey)) {
throw new Error(`X25519: The provided key is not a valid OKP private key.`);
}
// Decode the provided private key to bytes.
const privateKeyBytes = Convert.base64Url(privateKey.d).toUint8Array();
return privateKeyBytes;
});
}
/**
* Converts a public key from JSON Web Key (JWK) format to a raw byte array (Uint8Array).
*
* @remarks
* This method accepts a public key in JWK format and converts it into its raw binary form.
* The conversion process involves decoding the 'x' parameter of the JWK (which represent the
* x coordinate of the elliptic curve point) from base64url format into a byte array.
*
* This conversion is essential for operations that require the public key in its raw
* binary form, such as certain low-level cryptographic operations or when interfacing
* with systems and libraries that expect keys in a byte array format.
*
* @example
* ```ts
* const publicKey = { ... }; // An X25519 public key in JWK format
* const publicKeyBytes = await X25519.publicKeyToBytes({ publicKey });
* ```
*
* @param params - The parameters for the public key conversion.
* @param params.publicKey - The public key in JWK format.
*
* @returns A Promise that resolves to the public key as a Uint8Array.
*/
static publicKeyToBytes({ publicKey }) {
return __awaiter(this, void 0, void 0, function* () {
// Verify the provided JWK represents a valid OKP public key.
if (!isOkpPublicJwk(publicKey)) {
throw new Error(`X25519: The provided key is not a valid OKP public key.`);
}
// Decode the provided public key to bytes.
const publicKeyBytes = Convert.base64Url(publicKey.x).toUint8Array();
return publicKeyBytes;
});
}
/**
* Computes an RFC6090-compliant Elliptic Curve Diffie-Hellman (ECDH) shared secret
* using secp256k1 private and public keys in JSON Web Key (JWK) format.
*
* @remarks
* This method facilitates the ECDH key agreement protocol, which is a method of securely
* deriving a shared secret between two parties based on their private and public keys.
* It takes the private key of one party (privateKeyA) and the public key of another
* party (publicKeyB) to compute a shared secret. The shared secret is derived from the
* x-coordinate of the elliptic curve point resulting from the multiplication of the
* public key with the private key.
*
* Note: When performing Elliptic Curve Diffie-Hellman (ECDH) key agreement,
* the resulting shared secret is a point on the elliptic curve, which
* consists of an x-coordinate and a y-coordinate. With a 256-bit curve like
* secp256k1, each of these coordinates is 32 bytes (256 bits) long. However,
* in the ECDH process, it's standard practice to use only the x-coordinate
* of the shared secret point as the resulting shared key. This is because
* the y-coordinate does not add to the entropy of the key, and both parties
* can independently compute the x-coordinate. Consquently, this implementation
* omits the y-coordinate for simplicity and standard compliance.
*
* @example
* ```ts
* const privateKeyA = { ... }; // A Jwk object for party A
* const publicKeyB = { ... }; // A PublicKeyJwk object for party B
* const sharedSecret = await Secp256k1.sharedSecret({
* privateKeyA,
* publicKeyB
* });
* ```
*
* @param params - The parameters for the shared secret computation.
* @param params.privateKeyA - The private key in JWK format of one party.
* @param params.publicKeyB - The public key in JWK format of the other party.
*
* @returns A Promise that resolves to the computed shared secret as a Uint8Array.
*/
static sharedSecret({ privateKeyA, publicKeyB }) {
return __awaiter(this, void 0, void 0, function* () {
// Ensure that keys from the same key pair are not specified.
if ('x' in privateKeyA && 'x' in publicKeyB && privateKeyA.x === publicKeyB.x) {
throw new Error(`X25519: ECDH shared secret cannot be computed from a single key pair's public and private keys.`);
}
// Convert the provided private and public keys to bytes.
const privateKeyABytes = yield X25519.privateKeyToBytes({ privateKey: privateKeyA });
const publicKeyBBytes = yield X25519.publicKeyToBytes({ publicKey: publicKeyB });
// Compute the shared secret between the public and private keys.
const sharedSecret = x25519.getSharedSecret(privateKeyABytes, publicKeyBBytes);
return sharedSecret;
});
}
}
//# sourceMappingURL=x25519.js.map
@@ -0,0 +1 @@
{"version":3,"file":"x25519.js","sourceRoot":"","sources":["../../../src/primitives/x25519.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,270 @@
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 { xchacha20poly1305 } from '@noble/ciphers/chacha';
import { getWebcryptoSubtle } from '@noble/ciphers/webcrypto/utils';
import { computeJwkThumbprint, isOctPrivateJwk } from '../jose/jwk.js';
/**
* Constant defining the length of the authentication tag in bytes for XChaCha20-Poly1305.
*
* @remarks
* The `POLY1305_TAG_LENGTH` is set to 16 bytes (128 bits), which is the standard size for the
* Poly1305 authentication tag in XChaCha20-Poly1305 encryption. This tag length ensures
* a strong level of security for message authentication, verifying the integrity and
* authenticity of the data during decryption.
*/
export const POLY1305_TAG_LENGTH = 16;
/**
* The `XChaCha20Poly1305` class provides a suite of utilities for cryptographic operations
* using the XChaCha20-Poly1305 algorithm, a combination of the XChaCha20 stream cipher and the
* Poly1305 message authentication code (MAC). This class encompasses methods for key generation,
* encryption, decryption, and conversions between raw byte arrays and JSON Web Key (JWK) formats.
*
* XChaCha20-Poly1305 is renowned for its high security and efficiency, especially in scenarios
* involving large data volumes or where data integrity and confidentiality are paramount. The
* extended nonce size of XChaCha20 reduces the risks of nonce reuse, while Poly1305 provides
* a strong MAC ensuring data integrity.
*
* Key Features:
* - Key Generation: Generate XChaCha20-Poly1305 symmetric keys in JWK format.
* - Key Conversion: Transform keys between raw byte arrays and JWK formats.
* - Encryption: Encrypt data using XChaCha20-Poly1305, returning both ciphertext and MAC tag.
* - Decryption: Decrypt data and verify integrity using the XChaCha20-Poly1305 algorithm.
*
* The methods in this class are asynchronous, returning Promises to accommodate various
* JavaScript environments.
*
* @example
* ```ts
* // Key Generation
* const privateKey = await XChaCha20Poly1305.generateKey();
*
* // Encryption
* const data = new TextEncoder().encode('Messsage');
* const nonce = utils.randomBytes(24); // 24-byte nonce
* const additionalData = new TextEncoder().encode('Associated data');
* const { ciphertext, tag } = await XChaCha20Poly1305.encrypt({
* data,
* nonce,
* additionalData,
* key: privateKey
* });
*
* // Decryption
* const decryptedData = await XChaCha20Poly1305.decrypt({
* data: ciphertext,
* nonce,
* tag,
* additionalData,
* key: privateKey
* });
*
* // Key Conversion
* const privateKeyBytes = await XChaCha20Poly1305.privateKeyToBytes({ privateKey });
* ```
*/
export class XChaCha20Poly1305 {
/**
* 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 the XChaCha20-Poly1305 algorithm. The 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 XChaCha20Poly1305.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 });
return privateKey;
});
}
/**
* Decrypts the provided data using XChaCha20-Poly1305.
*
* @remarks
* This method performs XChaCha20-Poly1305 decryption on the given encrypted data using the
* specified key, nonce, and authentication tag. It supports optional additional authenticated
* data (AAD) for enhanced security. The nonce must be 24 bytes long, consistent with XChaCha20's
* specifications.
*
* @example
* ```ts
* const encryptedData = new Uint8Array([...]); // Encrypted data
* const nonce = new Uint8Array(24); // 24-byte nonce
* const additionalData = new Uint8Array([...]); // Optional AAD
* const key = { ... }; // A Jwk object representing the XChaCha20-Poly1305 key
* const decryptedData = await XChaCha20Poly1305.decrypt({
* data: encryptedData,
* nonce,
* additionalData,
* key
* });
* ```
*
* @param params - The parameters for the decryption operation.
* @param params.data - The encrypted data to decrypt including the authentication tag,
* represented as a Uint8Array.
* @param params.key - The key to use for decryption, represented in JWK format.
* @param params.nonce - The nonce used during the encryption process.
* @param params.additionalData - Optional additional authenticated data.
*
* @returns A Promise that resolves to the decrypted data as a Uint8Array.
*/
static decrypt({ data, key, nonce, additionalData }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the private key from JWK format to bytes.
const privateKeyBytes = yield XChaCha20Poly1305.privateKeyToBytes({ privateKey: key });
const xc20p = xchacha20poly1305(privateKeyBytes, nonce, additionalData);
const plaintext = xc20p.decrypt(data);
return plaintext;
});
}
/**
* Encrypts the provided data using XChaCha20-Poly1305.
*
* @remarks
* This method performs XChaCha20-Poly1305 encryption on the given data using the specified key
* and nonce. It supports optional additional authenticated data (AAD) for enhanced security. The
* nonce must be 24 bytes long, as per XChaCha20's specifications. The method returns the
* encrypted data along with an authentication tag as a Uint8Array, ensuring both confidentiality
* and integrity of the data.
*
* @example
* ```ts
* const data = new TextEncoder().encode('Messsage');
* const nonce = utils.randomBytes(24); // 24-byte nonce
* const additionalData = new TextEncoder().encode('Associated data'); // Optional AAD
* const key = { ... }; // A Jwk object representing an XChaCha20-Poly1305 key
* const encryptedData = await XChaCha20Poly1305.encrypt({
* data,
* nonce,
* additionalData,
* key
* });
* ```
*
* @param params - The parameters for the encryption operation.
* @param params.data - The data to encrypt, represented as a Uint8Array.
* @param params.key - The key to use for encryption, represented in JWK format.
* @param params.nonce - A 24-byte nonce for the encryption process.
* @param params.additionalData - Optional additional authenticated data.
*
* @returns A Promise that resolves to a byte array containing the encrypted data and the
* authentication tag.
*/
static encrypt({ data, key, nonce, additionalData }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the private key from JWK format to bytes.
const privateKeyBytes = yield XChaCha20Poly1305.privateKeyToBytes({ privateKey: key });
const xc20p = xchacha20poly1305(privateKeyBytes, nonce, additionalData);
const ciphertext = xc20p.encrypt(data);
return ciphertext;
});
}
/**
* Generates a symmetric key for XChaCha20-Poly1305 in JSON Web Key (JWK) format.
*
* @remarks
* This method creates a new symmetric key suitable for use with the XChaCha20-Poly1305 algorithm.
* The key is generated using cryptographically secure random number generation to ensure its
* uniqueness and security. The XChaCha20-Poly1305 algorithm requires a 256-bit key (32 bytes),
* and this method adheres to that specification.
*
* Key components included in the JWK:
* - `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 privateKey = await XChaCha20Poly1305.generateKey();
* ```
*
* @returns A Promise that resolves to the generated symmetric key in JWK format.
*/
static generateKey() {
return __awaiter(this, void 0, void 0, function* () {
// 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-CTR', length: 256 }, true, ['encrypt']);
// Export the private key in JWK format.
const _a = yield webCrypto.exportKey('jwk', webCryptoKey), { alg, ext, key_ops } = _a, privateKey = __rest(_a, ["alg", "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).
*
* 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 XChaCha20Poly1305.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(`XChaCha20Poly1305: 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=xchacha20-poly1305.js.map
@@ -0,0 +1 @@
{"version":3,"file":"xchacha20-poly1305.js","sourceRoot":"","sources":["../../../src/primitives/xchacha20-poly1305.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,246 @@
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 { xchacha20 } from '@noble/ciphers/chacha';
import { getWebcryptoSubtle } from '@noble/ciphers/webcrypto/utils';
import { computeJwkThumbprint, isOctPrivateJwk } from '../jose/jwk.js';
/**
* The `XChaCha20` class provides a comprehensive suite of utilities for cryptographic operations
* using the XChaCha20 symmetric encryption algorithm. This class includes methods for key
* generation, encryption, decryption, and conversions between raw byte arrays and JSON Web Key
* (JWK) formats. XChaCha20 is an extended nonce variant of ChaCha20, a stream cipher designed for
* high-speed encryption with substantial security margins.
*
* The XChaCha20 algorithm is particularly well-suited for encrypting large volumes of data or
* data streams, especially where random access is required. The class adheres to standard
* cryptographic practices, ensuring robustness and security in its implementations.
*
* Key Features:
* - Key Generation: Generate XChaCha20 symmetric keys in JWK format.
* - Key Conversion: Transform keys between raw byte arrays and JWK formats.
* - Encryption: Encrypt data using XChaCha20 with the provided symmetric key.
* - Decryption: Decrypt data encrypted with XChaCha20 using the corresponding symmetric key.
*
* The methods in this class are asynchronous, returning Promises to accommodate various
* JavaScript environments.
*
* @example
* ```ts
* // Key Generation
* const privateKey = await XChaCha20.generateKey();
*
* // Encryption
* const data = new TextEncoder().encode('Messsage');
* const nonce = utils.randomBytes(24); // 24-byte nonce for XChaCha20
* const encryptedData = await XChaCha20.encrypt({
* data,
* nonce,
* key: privateKey
* });
*
* // Decryption
* const decryptedData = await XChaCha20.decrypt({
* data: encryptedData,
* nonce,
* key: privateKey
* });
*
* // Key Conversion
* const privateKeyBytes = await XChaCha20.privateKeyToBytes({ privateKey });
* ```
*/
export class XChaCha20 {
/**
* 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 the XChaCha20 symmetric encryption algorithm. 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 XChaCha20.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 });
return privateKey;
});
}
/**
* Decrypts the provided data using XChaCha20.
*
* @remarks
* This method performs XChaCha20 decryption on the given encrypted data using the specified key
* and nonce. The nonce should be the same as used in the encryption process and must be 24 bytes
* long. The method returns the decrypted data as a Uint8Array.
*
* @example
* ```ts
* const encryptedData = new Uint8Array([...]); // Encrypted data
* const nonce = new Uint8Array(24); // 24-byte nonce used during encryption
* const key = { ... }; // A Jwk object representing the XChaCha20 key
* const decryptedData = await XChaCha20.decrypt({
* data: encryptedData,
* nonce,
* key
* });
* ```
*
* @param params - The parameters for the decryption operation.
* @param params.data - The encrypted data to decrypt, represented as a Uint8Array.
* @param params.key - The key to use for decryption, represented in JWK format.
* @param params.nonce - The nonce used during the encryption process.
*
* @returns A Promise that resolves to the decrypted data as a Uint8Array.
*/
static decrypt({ data, key, nonce }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the private key from JWK format to bytes.
const privateKeyBytes = yield XChaCha20.privateKeyToBytes({ privateKey: key });
const ciphertext = xchacha20(privateKeyBytes, nonce, data);
return ciphertext;
});
}
/**
* Encrypts the provided data using XChaCha20.
*
* @remarks
* This method performs XChaCha20 encryption on the given data using the specified key and nonce.
* The nonce must be 24 bytes long, ensuring a high level of security through a vast nonce space,
* reducing the risks associated with nonce reuse. The method returns the encrypted data as a
* Uint8Array.
*
* @example
* ```ts
* const data = new TextEncoder().encode('Messsage');
* const nonce = utils.randomBytes(24); // 24-byte nonce for XChaCha20
* const key = { ... }; // A Jwk object representing an XChaCha20 key
* const encryptedData = await XChaCha20.encrypt({
* data,
* nonce,
* key
* });
* ```
*
* @param params - The parameters for the encryption operation.
* @param params.data - The data to encrypt, represented as a Uint8Array.
* @param params.key - The key to use for encryption, represented in JWK format.
* @param params.nonce - A 24-byte nonce for the encryption process.
*
* @returns A Promise that resolves to the encrypted data as a Uint8Array.
*/
static encrypt({ data, key, nonce }) {
return __awaiter(this, void 0, void 0, function* () {
// Convert the private key from JWK format to bytes.
const privateKeyBytes = yield XChaCha20.privateKeyToBytes({ privateKey: key });
const plaintext = xchacha20(privateKeyBytes, nonce, data);
return plaintext;
});
}
/**
* Generates a symmetric key for XChaCha20 in JSON Web Key (JWK) format.
*
* @remarks
* This method creates a new symmetric key suitable for use with the XChaCha20 encryption
* algorithm. The key is generated using cryptographically secure random number generation
* to ensure its uniqueness and security. The XChaCha20 algorithm requires a 256-bit key
* (32 bytes), and this method adheres to that specification.
*
* Key components included in the JWK:
* - `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 privateKey = await XChaCha20.generateKey();
* ```
*
* @returns A Promise that resolves to the generated symmetric key in JWK format.
*/
static generateKey() {
return __awaiter(this, void 0, void 0, function* () {
// 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-CTR', length: 256 }, true, ['encrypt']);
// Export the private key in JWK format.
const _a = yield webCrypto.exportKey('jwk', webCryptoKey), { alg, ext, key_ops } = _a, privateKey = __rest(_a, ["alg", "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 XChaCha20.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(`XChaCha20: 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=xchacha20.js.map
@@ -0,0 +1 @@
{"version":3,"file":"xchacha20.js","sourceRoot":"","sources":["../../../src/primitives/xchacha20.ts"],"names":[],"mappings":"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"}
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=cipher.js.map
@@ -0,0 +1 @@
{"version":3,"file":"cipher.js","sourceRoot":"","sources":["../../../src/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/types/crypto-api.ts"],"names":[],"mappings":""}
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=hasher.js.map
@@ -0,0 +1 @@
{"version":3,"file":"hasher.js","sourceRoot":"","sources":["../../../src/types/hasher.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=identifier.js.map
@@ -0,0 +1 @@
{"version":3,"file":"identifier.js","sourceRoot":"","sources":["../../../src/types/identifier.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=key-compressor.js.map
@@ -0,0 +1 @@
{"version":3,"file":"key-compressor.js","sourceRoot":"","sources":["../../../src/types/key-compressor.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/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/types/key-deriver.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=key-generator.js.map
@@ -0,0 +1 @@
{"version":3,"file":"key-generator.js","sourceRoot":"","sources":["../../../src/types/key-generator.ts"],"names":[],"mappings":""}
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=key-io.js.map
@@ -0,0 +1 @@
{"version":3,"file":"key-io.js","sourceRoot":"","sources":["../../../src/types/key-io.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/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/types/params-direct.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=params-enclosed.js.map
@@ -0,0 +1 @@
{"version":3,"file":"params-enclosed.js","sourceRoot":"","sources":["../../../src/types/params-enclosed.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/types/params-kms.ts"],"names":[],"mappings":""}
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=signer.js.map
@@ -0,0 +1 @@
{"version":3,"file":"signer.js","sourceRoot":"","sources":["../../../src/types/signer.ts"],"names":[],"mappings":""}
+189
View File
@@ -0,0 +1,189 @@
import { crypto } from '@noble/hashes/crypto';
import { randomBytes as nobleRandomBytes } from '@noble/hashes/utils';
/**
* Checks whether the properties object provided contains the specified property.
*
* @example
* ```ts
* const obj = { a: 'Bob', t: 30 };
* checkRequiredProperty({ property: 'a', inObject: obj }); // No error
* checkRequiredProperty({ property: 'z', inObject: obj }); // Throws TypeError
* ```
*
* @param params - The parameters for the check.
* @param params.property - Property key to check for.
* @param params.properties - Properties object to check within.
* @returns void
* @throws {TypeError} If the property is not a key in the properties object.
*/
export function checkRequiredProperty(params) {
if (!params || params.property === undefined || params.inObject === undefined) {
throw new TypeError(`One or more required parameters missing: 'property, properties'`);
}
const { property, inObject } = params;
if (!(property in inObject)) {
throw new TypeError(`Required parameter missing: '${property}'`);
}
}
/**
* Checks whether the property specified is a member of the list of valid properties.
*
* @example
* ```ts
* const property = 'color';
* const allowedProperties = ['size', 'shape', 'color'];
* checkValidProperty({ property, allowedProperties }); // No error
* checkValidProperty({ property: 'weight', allowedProperties }); // Throws TypeError
* ```
*
* @param property Property key to check for.
* @param allowedProperties Properties Array, Map, or Set to check within.
* @returns void
* @throws {TypeError} If the property is not a member of the allowedProperties Array, Map, or Set.
*/
export function checkValidProperty(params) {
if (!params || params.property === undefined || params.allowedProperties === undefined) {
throw new TypeError(`One or more required parameters missing: 'property, allowedProperties'`);
}
const { property, allowedProperties } = params;
if ((Array.isArray(allowedProperties) && !allowedProperties.includes(property)) ||
(allowedProperties instanceof Set && !allowedProperties.has(property)) ||
(allowedProperties instanceof Map && !allowedProperties.has(property))) {
const validProperties = Array.from((allowedProperties instanceof Map) ? allowedProperties.keys() : allowedProperties).join(', ');
throw new TypeError(`Out of range: '${property}'. Must be one of '${validProperties}'`);
}
}
/**
* Determines the JOSE algorithm identifier of the digital signature algorithm based on the `alg` or
* `crv` property of a {@link Jwk | JWK}.
*
* If the `alg` property is present, its value takes precedence and is returned. Otherwise, the
* `crv` property is used to determine the algorithm.
*
* @see {@link https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms | JOSE Algorithms}
* @see {@link https://datatracker.ietf.org/doc/draft-ietf-jose-fully-specified-algorithms/ | Fully-Specified Algorithms for JOSE and COSE}
*
* @example
* ```ts
* const publicKey: Jwk = {
* "kty": "OKP",
* "crv": "Ed25519",
* "x": "FEJG7OakZi500EydXxuE8uMc8uaAzEJkmQeG8khXANw"
* }
* const algorithm = getJoseSignatureAlgorithmFromPublicKey(publicKey);
* console.log(algorithm); // Output: "EdDSA"
* ```
*
* @param publicKey - A JWK containing the `alg` and/or `crv` properties.
* @returns The name of the algorithm associated with the key.
* @throws Error if the algorithm cannot be determined from the provided input.
*/
export function getJoseSignatureAlgorithmFromPublicKey(publicKey) {
const curveToJoseAlgorithm = {
'Ed25519': 'EdDSA',
'P-256': 'ES256',
'P-384': 'ES384',
'P-521': 'ES512',
'secp256k1': 'ES256K',
};
// If the key contains an `alg` property that matches a JOSE registered algorithm identifier,
// return its value.
if (publicKey.alg && Object.values(curveToJoseAlgorithm).includes(publicKey.alg)) {
return publicKey.alg;
}
// If the key contains a `crv` property, return the corresponding algorithm.
if (publicKey.crv && Object.keys(curveToJoseAlgorithm).includes(publicKey.crv)) {
return curveToJoseAlgorithm[publicKey.crv];
}
throw new Error(`Unable to determine algorithm based on provided input: alg=${publicKey.alg}, crv=${publicKey.crv}. ` +
`Supported 'alg' values: ${Object.values(curveToJoseAlgorithm).join(', ')}. ` +
`Supported 'crv' values: ${Object.keys(curveToJoseAlgorithm).join(', ')}.`);
}
/**
* Checks if the Web Crypto API is supported in the current runtime environment.
*
* @remarks
* The function uses `globalThis` to provide a universal reference to the global
* scope, regardless of the environment. `globalThis` is a standard feature introduced
* in ECMAScript 2020 that is agnostic to the underlying JavaScript environment, making
* the code portable across browser, Node.js, and Web Workers environments.
*
* In a web browser, `globalThis` is equivalent to the `window` object. In Node.js, it
* is equivalent to the `global` object, and in Web Workers, it corresponds to `self`.
*
* This method checks for the `crypto` object and its `subtle` property on the global scope
* to determine the availability of the Web Crypto API. If both are present, the API is
* supported; otherwise, it is not.
*
* @example
* ```ts
* if (isWebCryptoSupported()) {
* console.log('Crypto operations can be performed');
* } else {
* console.log('Crypto operations are not supported in this environment');
* }
* ```
*
* @returns A boolean indicating whether the Web Crypto API is supported in the current environment.
*/
export function isWebCryptoSupported() {
if (globalThis.crypto && globalThis.crypto.subtle) {
return true;
}
else {
return false;
}
}
/**
* Generates secure pseudorandom values of the specified length using
* `crypto.getRandomValues`, which defers to the operating system.
*
* @remarks
* This function is a wrapper around `randomBytes` from the '@noble/hashes'
* package. It's designed to be cryptographically strong, suitable for
* generating initialization vectors, nonces, and other random values.
*
* @see {@link https://www.npmjs.com/package/@noble/hashes | @noble/hashes on NPM} for more
* information about the underlying implementation.
*
* @example
* ```ts
* const bytes = randomBytes(32); // Generates 32 random bytes
* ```
*
* @param bytesLength - The number of bytes to generate.
* @returns A Uint8Array containing the generated random bytes.
*/
export function randomBytes(bytesLength) {
return nobleRandomBytes(bytesLength);
}
/**
* Generates a UUID (Universally Unique Identifier) using a
* cryptographically strong random number generator following
* the version 4 format, as specified in RFC 4122.
*
* A version 4 UUID is a randomly generated UUID. The 13th character
* is set to '4' to denote version 4, and the 17th character is one
* of '8', '9', 'A', or 'B' to comply with the variant 1 format of
* UUIDs (the high bits are set to '10').
*
* The UUID is a 36 character string, including hyphens, and looks like this:
* xxxxxxxx-xxxx-4xxx-axxx-xxxxxxxxxxxx
*
* Note that while UUIDs are not guaranteed to be unique, they are
* practically unique" given the large number of possible UUIDs and
* the randomness of generation.
*
* @example
* ```ts
* const uuid = randomUuid();
* console.log(uuid); // Outputs a version 4 UUID, e.g., '123e4567-e89b-12d3-a456-426655440000'
* ```
*
* @returns A string containing a randomly generated, 36 character long v4 UUID.
*/
export function randomUuid() {
const uuid = crypto.randomUUID();
return uuid;
}
//# sourceMappingURL=utils.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"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"}