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,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":";;;;;;;;;;;;;;;;;;;;AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAIpE,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEvE;;;;;;;;;;;;;GAaG;AACH,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B;;;;;;;;;;;;GAYG;AACH,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAU,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,kBAAkB,GAAG,cAAc,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,MAAM,OAAO,MAAM;IACjB;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACI,MAAM,CAAO,iBAAiB,CAAC,EAAE,eAAe,EAEtD;;YACC,2CAA2C;YAC3C,MAAM,UAAU,GAAQ;gBACtB,CAAC,EAAK,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,WAAW,EAAE;gBACvD,GAAG,EAAG,KAAK;aACZ,CAAC;YAEF,oDAAoD;YACpD,UAAU,CAAC,GAAG,GAAG,MAAM,oBAAoB,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC;YAEjE,OAAO,UAAU,CAAC;QACpB,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACI,MAAM,CAAO,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAKvD;;YACC,wEAAwE;YACxE,IAAI,OAAO,CAAC,UAAU,KAAK,cAAc,GAAG,CAAC,EAAE;gBAC7C,MAAM,IAAI,SAAS,CAAC,uBAAuB,cAAc,iBAAiB,CAAC,CAAC;aAC7E;YAED,sCAAsC;YACtC,IAAI,MAAM,KAAK,CAAC,IAAI,MAAM,GAAG,kBAAkB,EAAE;gBAC/C,MAAM,IAAI,SAAS,CAAC,mDAAmD,kBAAkB,EAAE,CAAC,CAAC;aAC9F;YAED,oCAAoC;YACpC,MAAM,SAAS,GAAG,kBAAkB,EAAE,CAAC;YAEvC,2EAA2E;YAC3E,MAAM,YAAY,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YAEnG,oBAAoB;YACpB,MAAM,eAAe,GAAG,MAAM,SAAS,CAAC,OAAO,CAC7C,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,EACpC,YAAY,EACZ,IAAI,CACL,CAAC;YAEF,0CAA0C;YAC1C,MAAM,SAAS,GAAG,IAAI,UAAU,CAAC,eAAe,CAAC,CAAC;YAElD,OAAO,SAAS,CAAC;QACnB,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACI,MAAM,CAAO,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAKvD;;YACC,mDAAmD;YACnD,IAAI,OAAO,CAAC,UAAU,KAAK,cAAc,GAAG,CAAC,EAAE;gBAC7C,MAAM,IAAI,SAAS,CAAC,uBAAuB,cAAc,iBAAiB,CAAC,CAAC;aAC7E;YAED,sCAAsC;YACtC,IAAI,MAAM,KAAK,CAAC,IAAI,MAAM,GAAG,kBAAkB,EAAE;gBAC/C,MAAM,IAAI,SAAS,CAAC,mDAAmD,kBAAkB,EAAE,CAAC,CAAC;aAC9F;YAED,oCAAoC;YACpC,MAAM,SAAS,GAAG,kBAAkB,EAAE,CAAC;YAEvC,2EAA2E;YAC3E,MAAM,YAAY,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;YAE9G,oBAAoB;YACpB,MAAM,gBAAgB,GAAG,MAAM,SAAS,CAAC,OAAO,CAC9C,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,EACpC,YAAY,EACZ,IAAI,CACL,CAAC;YAEF,0CAA0C;YAC1C,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC,gBAAgB,CAAC,CAAC;YAEpD,OAAO,UAAU,CAAC;QACpB,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACI,MAAM,CAAO,WAAW,CAAC,EAAE,MAAM,EAEvC;;YACC,2BAA2B;YAC3B,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,MAAa,CAAC,EAAE;gBAC5C,MAAM,IAAI,UAAU,CAAC,sCAAsC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;aAC/F;YAED,oCAAoC;YACpC,MAAM,SAAS,GAAG,kBAAkB,EAAE,CAAC;YAEvC,iCAAiC;YACjC,8FAA8F;YAC9F,wFAAwF;YACxF,MAAM,YAAY,GAAG,MAAM,SAAS,CAAC,WAAW,CAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YAElG,wCAAwC;YACxC,MAAM,KAAkC,MAAM,SAAS,CAAC,SAAS,CAAC,KAAK,EAAE,YAAY,CAAC,EAAhF,EAAE,GAAG,EAAE,OAAO,OAAkE,EAA7D,UAAU,cAA7B,kBAA+B,CAAiD,CAAC;YAEvF,oDAAoD;YACpD,UAAU,CAAC,GAAG,GAAG,MAAM,oBAAoB,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC;YAEjE,OAAO,UAAU,CAAC;QACpB,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACI,MAAM,CAAO,iBAAiB,CAAC,EAAE,UAAU,EAEjD;;YACC,8DAA8D;YAC9D,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,EAAE;gBAChC,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;aAC7E;YAED,4CAA4C;YAC5C,MAAM,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC;YAEvE,OAAO,eAAe,CAAC;QACzB,CAAC;KAAA;CACF"}
@@ -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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"}