hd-wallet-wasm 0.2.0 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -1
- package/dist/hd-wallet.js +0 -0
- package/dist/hd-wallet.wasm +0 -0
- package/dist/index.d.ts +14 -13
- package/package.json +2 -3
- package/src/index.d.ts +14 -13
- package/src/index.mjs +148 -27
- package/src/webcrypto.mjs +0 -216
package/README.md
CHANGED
|
@@ -7,9 +7,10 @@ A comprehensive HD (Hierarchical Deterministic) wallet implementation compiled t
|
|
|
7
7
|
- **BIP-32/39/44/49/84** - Complete HD wallet derivation standards
|
|
8
8
|
- **Multi-curve support** - secp256k1, Ed25519, P-256, P-384, X25519
|
|
9
9
|
- **Multi-chain** - Bitcoin, Ethereum, Solana, Cosmos, Polkadot
|
|
10
|
+
- **AES-256-GCM** - Authenticated encryption via WASM (Crypto++/OpenSSL)
|
|
10
11
|
- **Hardware wallet ready** - Trezor, Ledger, KeepKey abstraction layer
|
|
11
12
|
- **Secure** - Crypto++ backend, secure memory handling
|
|
12
|
-
- **Fast** - WebAssembly performance
|
|
13
|
+
- **Fast** - WebAssembly performance, synchronous cryptographic operations
|
|
13
14
|
- **TypeScript** - Full type definitions included
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
@@ -224,6 +225,40 @@ const pbkdf2 = wallet.utils.pbkdf2(password, salt, 100000, 32);
|
|
|
224
225
|
wallet.utils.secureWipe(sensitiveData);
|
|
225
226
|
```
|
|
226
227
|
|
|
228
|
+
### AES-GCM Encryption
|
|
229
|
+
|
|
230
|
+
```javascript
|
|
231
|
+
// Generate key and IV
|
|
232
|
+
const key = wallet.utils.generateAesKey(256); // 32 bytes for AES-256
|
|
233
|
+
const iv = wallet.utils.generateIv(); // 12 bytes
|
|
234
|
+
|
|
235
|
+
// Encrypt
|
|
236
|
+
const plaintext = new TextEncoder().encode('Secret data');
|
|
237
|
+
const { ciphertext, tag } = wallet.utils.aesGcm.encrypt(key, plaintext, iv);
|
|
238
|
+
|
|
239
|
+
// Decrypt
|
|
240
|
+
const decrypted = wallet.utils.aesGcm.decrypt(key, ciphertext, tag, iv);
|
|
241
|
+
|
|
242
|
+
// With additional authenticated data (AAD)
|
|
243
|
+
const aad = new TextEncoder().encode('context');
|
|
244
|
+
const enc = wallet.utils.aesGcm.encrypt(key, plaintext, iv, aad);
|
|
245
|
+
const dec = wallet.utils.aesGcm.decrypt(key, enc.ciphertext, enc.tag, iv, aad);
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Random Number Generation
|
|
249
|
+
|
|
250
|
+
```javascript
|
|
251
|
+
// Generate cryptographically secure random bytes
|
|
252
|
+
const randomBytes = wallet.utils.getRandomBytes(32);
|
|
253
|
+
|
|
254
|
+
// Generate random IV for AES-GCM (12 bytes)
|
|
255
|
+
const iv = wallet.utils.generateIv();
|
|
256
|
+
|
|
257
|
+
// Generate random AES key (128, 192, or 256 bits)
|
|
258
|
+
const aes128Key = wallet.utils.generateAesKey(128);
|
|
259
|
+
const aes256Key = wallet.utils.generateAesKey(256);
|
|
260
|
+
```
|
|
261
|
+
|
|
227
262
|
## Coin Types (SLIP-44)
|
|
228
263
|
|
|
229
264
|
```javascript
|
package/dist/hd-wallet.js
CHANGED
|
Binary file
|
package/dist/hd-wallet.wasm
CHANGED
|
Binary file
|
package/dist/index.d.ts
CHANGED
|
@@ -24,6 +24,14 @@ export interface HDWalletModule {
|
|
|
24
24
|
getSupportedCoins(): string[];
|
|
25
25
|
getSupportedCurves(): string[];
|
|
26
26
|
|
|
27
|
+
// OpenSSL FIPS support (when built with HD_WALLET_USE_OPENSSL)
|
|
28
|
+
/** Initialize OpenSSL in FIPS mode. Returns true if FIPS mode activated, false if using fallback. */
|
|
29
|
+
initFips(): boolean;
|
|
30
|
+
/** Check if OpenSSL backend is being used for FIPS algorithms */
|
|
31
|
+
isOpenSSL(): boolean;
|
|
32
|
+
/** Check if running in FIPS mode (OpenSSL FIPS provider active) */
|
|
33
|
+
isOpenSSLFips(): boolean;
|
|
34
|
+
|
|
27
35
|
// WASI bridge
|
|
28
36
|
wasiHasFeature(feature: WasiFeature): boolean;
|
|
29
37
|
wasiGetWarning(feature: WasiFeature): WasiWarning;
|
|
@@ -579,28 +587,21 @@ export interface UtilsAPI {
|
|
|
579
587
|
blake2b(data: Uint8Array, outputLength?: number): Uint8Array;
|
|
580
588
|
blake2s(data: Uint8Array, outputLength?: number): Uint8Array;
|
|
581
589
|
|
|
582
|
-
// Key derivation
|
|
590
|
+
// Key derivation
|
|
583
591
|
hkdf(ikm: Uint8Array, salt: Uint8Array, info: Uint8Array, length: number): Uint8Array;
|
|
584
592
|
pbkdf2(password: Uint8Array, salt: Uint8Array, iterations: number, length: number): Uint8Array;
|
|
585
593
|
scrypt(password: Uint8Array, salt: Uint8Array, n: number, r: number, p: number, length: number): Uint8Array;
|
|
586
594
|
|
|
587
|
-
//
|
|
588
|
-
hkdfSha256Async(ikm: Uint8Array, salt: Uint8Array, info: Uint8Array, length: number): Promise<Uint8Array>;
|
|
589
|
-
hkdfSha384Async(ikm: Uint8Array, salt: Uint8Array, info: Uint8Array, length: number): Promise<Uint8Array>;
|
|
590
|
-
|
|
591
|
-
// AES-GCM encryption/decryption (async - WebCrypto, hardware accelerated)
|
|
595
|
+
// AES-GCM encryption/decryption (WASM/OpenSSL)
|
|
592
596
|
aesGcm: {
|
|
593
|
-
encrypt(key: Uint8Array, plaintext: Uint8Array, iv: Uint8Array, aad?: Uint8Array):
|
|
594
|
-
decrypt(key: Uint8Array, ciphertext: Uint8Array, tag: Uint8Array, iv: Uint8Array, aad?: Uint8Array):
|
|
595
|
-
generateIv(): Uint8Array;
|
|
596
|
-
generateKey(bits?: number): Uint8Array;
|
|
597
|
+
encrypt(key: Uint8Array, plaintext: Uint8Array, iv: Uint8Array, aad?: Uint8Array): { ciphertext: Uint8Array; tag: Uint8Array };
|
|
598
|
+
decrypt(key: Uint8Array, ciphertext: Uint8Array, tag: Uint8Array, iv: Uint8Array, aad?: Uint8Array): Uint8Array;
|
|
597
599
|
};
|
|
598
600
|
|
|
599
|
-
// WebCrypto availability
|
|
600
|
-
isWebCryptoAvailable(): boolean;
|
|
601
|
-
|
|
602
601
|
// Random number generation
|
|
603
602
|
getRandomBytes(length: number): Uint8Array;
|
|
603
|
+
generateIv(): Uint8Array;
|
|
604
|
+
generateAesKey(bits?: number): Uint8Array;
|
|
604
605
|
|
|
605
606
|
// Encoding
|
|
606
607
|
encodeBase58(data: Uint8Array): string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hd-wallet-wasm",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "Comprehensive HD Wallet implementation in WebAssembly - BIP-32/39/44, multi-curve, multi-chain support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.mjs",
|
|
@@ -25,7 +25,6 @@
|
|
|
25
25
|
"files": [
|
|
26
26
|
"src/index.mjs",
|
|
27
27
|
"src/index.d.ts",
|
|
28
|
-
"src/webcrypto.mjs",
|
|
29
28
|
"src/aligned.mjs",
|
|
30
29
|
"src/aligned.d.ts",
|
|
31
30
|
"src/generated/",
|
|
@@ -43,7 +42,7 @@
|
|
|
43
42
|
"test:bip32": "node test/test_bip32.mjs",
|
|
44
43
|
"test:vectors": "node test/test_vectors.mjs",
|
|
45
44
|
"test:aligned": "node test/test_aligned.mjs",
|
|
46
|
-
"prepublishOnly": "
|
|
45
|
+
"prepublishOnly": "echo 'Build handled by CI'"
|
|
47
46
|
},
|
|
48
47
|
"repository": {
|
|
49
48
|
"type": "git",
|
package/src/index.d.ts
CHANGED
|
@@ -24,6 +24,14 @@ export interface HDWalletModule {
|
|
|
24
24
|
getSupportedCoins(): string[];
|
|
25
25
|
getSupportedCurves(): string[];
|
|
26
26
|
|
|
27
|
+
// OpenSSL FIPS support (when built with HD_WALLET_USE_OPENSSL)
|
|
28
|
+
/** Initialize OpenSSL in FIPS mode. Returns true if FIPS mode activated, false if using fallback. */
|
|
29
|
+
initFips(): boolean;
|
|
30
|
+
/** Check if OpenSSL backend is being used for FIPS algorithms */
|
|
31
|
+
isOpenSSL(): boolean;
|
|
32
|
+
/** Check if running in FIPS mode (OpenSSL FIPS provider active) */
|
|
33
|
+
isOpenSSLFips(): boolean;
|
|
34
|
+
|
|
27
35
|
// WASI bridge
|
|
28
36
|
wasiHasFeature(feature: WasiFeature): boolean;
|
|
29
37
|
wasiGetWarning(feature: WasiFeature): WasiWarning;
|
|
@@ -579,28 +587,21 @@ export interface UtilsAPI {
|
|
|
579
587
|
blake2b(data: Uint8Array, outputLength?: number): Uint8Array;
|
|
580
588
|
blake2s(data: Uint8Array, outputLength?: number): Uint8Array;
|
|
581
589
|
|
|
582
|
-
// Key derivation
|
|
590
|
+
// Key derivation
|
|
583
591
|
hkdf(ikm: Uint8Array, salt: Uint8Array, info: Uint8Array, length: number): Uint8Array;
|
|
584
592
|
pbkdf2(password: Uint8Array, salt: Uint8Array, iterations: number, length: number): Uint8Array;
|
|
585
593
|
scrypt(password: Uint8Array, salt: Uint8Array, n: number, r: number, p: number, length: number): Uint8Array;
|
|
586
594
|
|
|
587
|
-
//
|
|
588
|
-
hkdfSha256Async(ikm: Uint8Array, salt: Uint8Array, info: Uint8Array, length: number): Promise<Uint8Array>;
|
|
589
|
-
hkdfSha384Async(ikm: Uint8Array, salt: Uint8Array, info: Uint8Array, length: number): Promise<Uint8Array>;
|
|
590
|
-
|
|
591
|
-
// AES-GCM encryption/decryption (async - WebCrypto, hardware accelerated)
|
|
595
|
+
// AES-GCM encryption/decryption (WASM/OpenSSL)
|
|
592
596
|
aesGcm: {
|
|
593
|
-
encrypt(key: Uint8Array, plaintext: Uint8Array, iv: Uint8Array, aad?: Uint8Array):
|
|
594
|
-
decrypt(key: Uint8Array, ciphertext: Uint8Array, tag: Uint8Array, iv: Uint8Array, aad?: Uint8Array):
|
|
595
|
-
generateIv(): Uint8Array;
|
|
596
|
-
generateKey(bits?: number): Uint8Array;
|
|
597
|
+
encrypt(key: Uint8Array, plaintext: Uint8Array, iv: Uint8Array, aad?: Uint8Array): { ciphertext: Uint8Array; tag: Uint8Array };
|
|
598
|
+
decrypt(key: Uint8Array, ciphertext: Uint8Array, tag: Uint8Array, iv: Uint8Array, aad?: Uint8Array): Uint8Array;
|
|
597
599
|
};
|
|
598
600
|
|
|
599
|
-
// WebCrypto availability
|
|
600
|
-
isWebCryptoAvailable(): boolean;
|
|
601
|
-
|
|
602
601
|
// Random number generation
|
|
603
602
|
getRandomBytes(length: number): Uint8Array;
|
|
603
|
+
generateIv(): Uint8Array;
|
|
604
|
+
generateAesKey(bits?: number): Uint8Array;
|
|
604
605
|
|
|
605
606
|
// Encoding
|
|
606
607
|
encodeBase58(data: Uint8Array): string;
|
package/src/index.mjs
CHANGED
|
@@ -9,15 +9,12 @@
|
|
|
9
9
|
* - Transaction building and signing
|
|
10
10
|
*
|
|
11
11
|
* @module hd-wallet-wasm
|
|
12
|
-
* @version 0.
|
|
12
|
+
* @version 0.2.0
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
// Import aligned API for batch operations
|
|
16
16
|
import { AlignedAPI } from './aligned.mjs';
|
|
17
17
|
|
|
18
|
-
// Import WebCrypto bridge for hardware-accelerated async operations
|
|
19
|
-
import * as WebCrypto from './webcrypto.mjs';
|
|
20
|
-
|
|
21
18
|
// =============================================================================
|
|
22
19
|
// Enums (matching TypeScript definitions)
|
|
23
20
|
// =============================================================================
|
|
@@ -2241,45 +2238,125 @@ function createModule(wasm) {
|
|
|
2241
2238
|
}
|
|
2242
2239
|
},
|
|
2243
2240
|
|
|
2244
|
-
//
|
|
2245
|
-
hkdfSha256Async: WebCrypto.hkdfSha256,
|
|
2246
|
-
hkdfSha384Async: WebCrypto.hkdfSha384,
|
|
2247
|
-
|
|
2248
|
-
// AES-GCM encryption/decryption (async - uses WebCrypto)
|
|
2241
|
+
// AES-GCM encryption/decryption (uses WASM/Crypto++ or OpenSSL)
|
|
2249
2242
|
aesGcm: {
|
|
2250
2243
|
/**
|
|
2251
|
-
* Encrypt data with AES-GCM (
|
|
2252
|
-
* @param {Uint8Array} key - AES key (
|
|
2244
|
+
* Encrypt data with AES-GCM (synchronous WASM)
|
|
2245
|
+
* @param {Uint8Array} key - AES-256 key (32 bytes)
|
|
2253
2246
|
* @param {Uint8Array} plaintext - Data to encrypt
|
|
2254
|
-
* @param {Uint8Array} iv - Initialization vector (12 bytes
|
|
2247
|
+
* @param {Uint8Array} iv - Initialization vector (12 bytes)
|
|
2255
2248
|
* @param {Uint8Array} [aad] - Additional authenticated data
|
|
2256
|
-
* @returns {
|
|
2249
|
+
* @returns {{ciphertext: Uint8Array, tag: Uint8Array}}
|
|
2257
2250
|
*/
|
|
2258
|
-
encrypt
|
|
2251
|
+
encrypt(key, plaintext, iv, aad = new Uint8Array(0)) {
|
|
2252
|
+
if (key.length !== 32) throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'Key must be 32 bytes');
|
|
2253
|
+
if (iv.length !== 12) throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'IV must be 12 bytes');
|
|
2254
|
+
|
|
2255
|
+
const keyPtr = allocAndCopy(wasm, key);
|
|
2256
|
+
const ptPtr = allocAndCopy(wasm, plaintext);
|
|
2257
|
+
const ivPtr = allocAndCopy(wasm, iv);
|
|
2258
|
+
const aadPtr = aad.length > 0 ? allocAndCopy(wasm, aad) : 0;
|
|
2259
|
+
const ctPtr = wasm._hd_alloc(plaintext.length);
|
|
2260
|
+
const tagPtr = wasm._hd_alloc(16);
|
|
2261
|
+
|
|
2262
|
+
try {
|
|
2263
|
+
const result = wasm._hd_aes_gcm_encrypt(
|
|
2264
|
+
keyPtr, key.length,
|
|
2265
|
+
ptPtr, plaintext.length,
|
|
2266
|
+
ivPtr, iv.length,
|
|
2267
|
+
aadPtr, aad.length,
|
|
2268
|
+
ctPtr, tagPtr
|
|
2269
|
+
);
|
|
2270
|
+
if (result < 0) throw new HDWalletError(-result);
|
|
2271
|
+
|
|
2272
|
+
return {
|
|
2273
|
+
ciphertext: readBytes(wasm, ctPtr, plaintext.length),
|
|
2274
|
+
tag: readBytes(wasm, tagPtr, 16)
|
|
2275
|
+
};
|
|
2276
|
+
} finally {
|
|
2277
|
+
wasm._hd_secure_wipe(keyPtr, key.length);
|
|
2278
|
+
wasm._hd_dealloc(keyPtr);
|
|
2279
|
+
wasm._hd_dealloc(ptPtr);
|
|
2280
|
+
wasm._hd_dealloc(ivPtr);
|
|
2281
|
+
if (aadPtr) wasm._hd_dealloc(aadPtr);
|
|
2282
|
+
wasm._hd_dealloc(ctPtr);
|
|
2283
|
+
wasm._hd_dealloc(tagPtr);
|
|
2284
|
+
}
|
|
2285
|
+
},
|
|
2259
2286
|
|
|
2260
2287
|
/**
|
|
2261
|
-
* Decrypt data with AES-GCM (
|
|
2262
|
-
* @param {Uint8Array} key - AES key
|
|
2288
|
+
* Decrypt data with AES-GCM (synchronous WASM)
|
|
2289
|
+
* @param {Uint8Array} key - AES-256 key (32 bytes)
|
|
2263
2290
|
* @param {Uint8Array} ciphertext - Encrypted data
|
|
2264
2291
|
* @param {Uint8Array} tag - Authentication tag (16 bytes)
|
|
2265
|
-
* @param {Uint8Array} iv - Initialization vector
|
|
2292
|
+
* @param {Uint8Array} iv - Initialization vector (12 bytes)
|
|
2266
2293
|
* @param {Uint8Array} [aad] - Additional authenticated data
|
|
2267
|
-
* @returns {
|
|
2294
|
+
* @returns {Uint8Array} Decrypted plaintext
|
|
2295
|
+
* @throws {HDWalletError} If authentication fails
|
|
2268
2296
|
*/
|
|
2269
|
-
decrypt
|
|
2297
|
+
decrypt(key, ciphertext, tag, iv, aad = new Uint8Array(0)) {
|
|
2298
|
+
if (key.length !== 32) throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'Key must be 32 bytes');
|
|
2299
|
+
if (iv.length !== 12) throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'IV must be 12 bytes');
|
|
2300
|
+
if (tag.length !== 16) throw new HDWalletError(ErrorCode.INVALID_ARGUMENT, 'Tag must be 16 bytes');
|
|
2301
|
+
|
|
2302
|
+
const keyPtr = allocAndCopy(wasm, key);
|
|
2303
|
+
const ctPtr = allocAndCopy(wasm, ciphertext);
|
|
2304
|
+
const ivPtr = allocAndCopy(wasm, iv);
|
|
2305
|
+
const tagPtr = allocAndCopy(wasm, tag);
|
|
2306
|
+
const aadPtr = aad.length > 0 ? allocAndCopy(wasm, aad) : 0;
|
|
2307
|
+
const ptPtr = wasm._hd_alloc(ciphertext.length);
|
|
2270
2308
|
|
|
2271
|
-
|
|
2272
|
-
|
|
2309
|
+
try {
|
|
2310
|
+
const result = wasm._hd_aes_gcm_decrypt(
|
|
2311
|
+
keyPtr, key.length,
|
|
2312
|
+
ctPtr, ciphertext.length,
|
|
2313
|
+
ivPtr, iv.length,
|
|
2314
|
+
aadPtr, aad.length,
|
|
2315
|
+
tagPtr, ptPtr
|
|
2316
|
+
);
|
|
2317
|
+
if (result < 0) {
|
|
2318
|
+
throw new HDWalletError(
|
|
2319
|
+
result === -2 ? ErrorCode.VERIFICATION_FAILED : -result,
|
|
2320
|
+
result === -2 ? 'AES-GCM authentication failed' : undefined
|
|
2321
|
+
);
|
|
2322
|
+
}
|
|
2273
2323
|
|
|
2274
|
-
|
|
2275
|
-
|
|
2324
|
+
return readBytes(wasm, ptPtr, ciphertext.length);
|
|
2325
|
+
} finally {
|
|
2326
|
+
wasm._hd_secure_wipe(keyPtr, key.length);
|
|
2327
|
+
wasm._hd_dealloc(keyPtr);
|
|
2328
|
+
wasm._hd_dealloc(ctPtr);
|
|
2329
|
+
wasm._hd_dealloc(ivPtr);
|
|
2330
|
+
wasm._hd_dealloc(tagPtr);
|
|
2331
|
+
if (aadPtr) wasm._hd_dealloc(aadPtr);
|
|
2332
|
+
wasm._hd_dealloc(ptPtr);
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2276
2335
|
},
|
|
2277
2336
|
|
|
2278
|
-
/** Check if WebCrypto is available */
|
|
2279
|
-
isWebCryptoAvailable: WebCrypto.isWebCryptoAvailable,
|
|
2280
|
-
|
|
2281
2337
|
/** Generate cryptographically secure random bytes */
|
|
2282
|
-
getRandomBytes
|
|
2338
|
+
getRandomBytes(length) {
|
|
2339
|
+
const bytes = new Uint8Array(length);
|
|
2340
|
+
if (typeof globalThis.crypto !== 'undefined' && globalThis.crypto.getRandomValues) {
|
|
2341
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
2342
|
+
} else {
|
|
2343
|
+
throw new Error('No cryptographic random source available');
|
|
2344
|
+
}
|
|
2345
|
+
return bytes;
|
|
2346
|
+
},
|
|
2347
|
+
|
|
2348
|
+
/** Generate a random IV for AES-GCM (12 bytes) */
|
|
2349
|
+
generateIv() {
|
|
2350
|
+
return this.getRandomBytes(12);
|
|
2351
|
+
},
|
|
2352
|
+
|
|
2353
|
+
/** Generate a random AES key (default 256 bits) */
|
|
2354
|
+
generateAesKey(bits = 256) {
|
|
2355
|
+
if (![128, 192, 256].includes(bits)) {
|
|
2356
|
+
throw new Error('AES key size must be 128, 192, or 256 bits');
|
|
2357
|
+
}
|
|
2358
|
+
return this.getRandomBytes(bits / 8);
|
|
2359
|
+
},
|
|
2283
2360
|
|
|
2284
2361
|
pbkdf2(password, salt, iterations, length) {
|
|
2285
2362
|
const pwdPtr = allocAndCopy(wasm, password);
|
|
@@ -2525,6 +2602,50 @@ function createModule(wasm) {
|
|
|
2525
2602
|
return wasm._hd_get_entropy_status();
|
|
2526
2603
|
},
|
|
2527
2604
|
|
|
2605
|
+
// OpenSSL FIPS support
|
|
2606
|
+
/**
|
|
2607
|
+
* Initialize OpenSSL in FIPS mode
|
|
2608
|
+
* @returns {boolean} True if FIPS mode activated, false if using default provider
|
|
2609
|
+
*/
|
|
2610
|
+
initFips() {
|
|
2611
|
+
// Check if the function exists (only when built with HD_WALLET_USE_OPENSSL)
|
|
2612
|
+
if (typeof wasm._hd_openssl_init_fips !== 'function') {
|
|
2613
|
+
console.warn('[HD Wallet] OpenSSL not compiled in, skipping FIPS init');
|
|
2614
|
+
return false;
|
|
2615
|
+
}
|
|
2616
|
+
const result = wasm._hd_openssl_init_fips();
|
|
2617
|
+
if (result === 1) {
|
|
2618
|
+
console.log('[HD Wallet] OpenSSL FIPS mode activated');
|
|
2619
|
+
return true;
|
|
2620
|
+
} else if (result === -1) {
|
|
2621
|
+
console.warn('[HD Wallet] FIPS provider not available, using default OpenSSL');
|
|
2622
|
+
wasm._hd_openssl_init_default();
|
|
2623
|
+
return false;
|
|
2624
|
+
} else {
|
|
2625
|
+
console.error('[HD Wallet] Failed to initialize OpenSSL');
|
|
2626
|
+
return false;
|
|
2627
|
+
}
|
|
2628
|
+
},
|
|
2629
|
+
|
|
2630
|
+
/**
|
|
2631
|
+
* Check if OpenSSL backend is being used
|
|
2632
|
+
* @returns {boolean}
|
|
2633
|
+
*/
|
|
2634
|
+
isOpenSSL() {
|
|
2635
|
+
return typeof wasm._hd_openssl_init_fips === 'function';
|
|
2636
|
+
},
|
|
2637
|
+
|
|
2638
|
+
/**
|
|
2639
|
+
* Check if running in FIPS mode (OpenSSL FIPS provider active)
|
|
2640
|
+
* @returns {boolean}
|
|
2641
|
+
*/
|
|
2642
|
+
isOpenSSLFips() {
|
|
2643
|
+
if (typeof wasm._hd_openssl_is_fips !== 'function') {
|
|
2644
|
+
return false;
|
|
2645
|
+
}
|
|
2646
|
+
return wasm._hd_openssl_is_fips() !== 0;
|
|
2647
|
+
},
|
|
2648
|
+
|
|
2528
2649
|
// APIs
|
|
2529
2650
|
mnemonic,
|
|
2530
2651
|
hdkey,
|
package/src/webcrypto.mjs
DELETED
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WebCrypto Bridge - Hardware-accelerated cryptographic operations
|
|
3
|
-
*
|
|
4
|
-
* These functions use the browser's native WebCrypto API for
|
|
5
|
-
* hardware-accelerated, constant-time cryptographic operations.
|
|
6
|
-
*
|
|
7
|
-
* All functions are async because WebCrypto is Promise-based.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Check if WebCrypto is available
|
|
12
|
-
* @returns {boolean}
|
|
13
|
-
*/
|
|
14
|
-
export function isWebCryptoAvailable() {
|
|
15
|
-
return typeof globalThis.crypto !== 'undefined' &&
|
|
16
|
-
typeof globalThis.crypto.subtle !== 'undefined';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* AES-GCM Encryption using WebCrypto
|
|
21
|
-
* @param {Uint8Array} key - AES key (128, 192, or 256 bits = 16, 24, or 32 bytes)
|
|
22
|
-
* @param {Uint8Array} plaintext - Data to encrypt
|
|
23
|
-
* @param {Uint8Array} iv - Initialization vector (12 bytes recommended for GCM)
|
|
24
|
-
* @param {Uint8Array} [aad] - Additional authenticated data (optional)
|
|
25
|
-
* @returns {Promise<{ciphertext: Uint8Array, tag: Uint8Array}>}
|
|
26
|
-
*/
|
|
27
|
-
export async function aesGcmEncrypt(key, plaintext, iv, aad = null) {
|
|
28
|
-
if (!isWebCryptoAvailable()) {
|
|
29
|
-
throw new Error('WebCrypto not available');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const cryptoKey = await crypto.subtle.importKey(
|
|
33
|
-
'raw',
|
|
34
|
-
key,
|
|
35
|
-
{ name: 'AES-GCM' },
|
|
36
|
-
false,
|
|
37
|
-
['encrypt']
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
const algorithmParams = {
|
|
41
|
-
name: 'AES-GCM',
|
|
42
|
-
iv: iv,
|
|
43
|
-
tagLength: 128 // 16 bytes
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
if (aad && aad.length > 0) {
|
|
47
|
-
algorithmParams.additionalData = aad;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const encrypted = await crypto.subtle.encrypt(
|
|
51
|
-
algorithmParams,
|
|
52
|
-
cryptoKey,
|
|
53
|
-
plaintext
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
// WebCrypto returns ciphertext + tag combined
|
|
57
|
-
const fullResult = new Uint8Array(encrypted);
|
|
58
|
-
const ciphertext = fullResult.slice(0, fullResult.length - 16);
|
|
59
|
-
const tag = fullResult.slice(fullResult.length - 16);
|
|
60
|
-
|
|
61
|
-
return { ciphertext, tag };
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* AES-GCM Decryption using WebCrypto
|
|
66
|
-
* @param {Uint8Array} key - AES key (128, 192, or 256 bits)
|
|
67
|
-
* @param {Uint8Array} ciphertext - Encrypted data
|
|
68
|
-
* @param {Uint8Array} tag - Authentication tag (16 bytes)
|
|
69
|
-
* @param {Uint8Array} iv - Initialization vector used during encryption
|
|
70
|
-
* @param {Uint8Array} [aad] - Additional authenticated data (must match encryption)
|
|
71
|
-
* @returns {Promise<Uint8Array>} Decrypted plaintext
|
|
72
|
-
* @throws {Error} If decryption fails (invalid tag, wrong key, etc.)
|
|
73
|
-
*/
|
|
74
|
-
export async function aesGcmDecrypt(key, ciphertext, tag, iv, aad = null) {
|
|
75
|
-
if (!isWebCryptoAvailable()) {
|
|
76
|
-
throw new Error('WebCrypto not available');
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const cryptoKey = await crypto.subtle.importKey(
|
|
80
|
-
'raw',
|
|
81
|
-
key,
|
|
82
|
-
{ name: 'AES-GCM' },
|
|
83
|
-
false,
|
|
84
|
-
['decrypt']
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
// Reconstruct the full ciphertext + tag format WebCrypto expects
|
|
88
|
-
const encrypted = new Uint8Array(ciphertext.length + tag.length);
|
|
89
|
-
encrypted.set(ciphertext, 0);
|
|
90
|
-
encrypted.set(tag, ciphertext.length);
|
|
91
|
-
|
|
92
|
-
const algorithmParams = {
|
|
93
|
-
name: 'AES-GCM',
|
|
94
|
-
iv: iv,
|
|
95
|
-
tagLength: 128
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
if (aad && aad.length > 0) {
|
|
99
|
-
algorithmParams.additionalData = aad;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
try {
|
|
103
|
-
const decrypted = await crypto.subtle.decrypt(
|
|
104
|
-
algorithmParams,
|
|
105
|
-
cryptoKey,
|
|
106
|
-
encrypted
|
|
107
|
-
);
|
|
108
|
-
return new Uint8Array(decrypted);
|
|
109
|
-
} catch (err) {
|
|
110
|
-
throw new Error('AES-GCM decryption failed: authentication tag mismatch or invalid data');
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* HKDF using WebCrypto (SHA-256)
|
|
116
|
-
* @param {Uint8Array} ikm - Input keying material
|
|
117
|
-
* @param {Uint8Array} salt - Salt value (can be empty Uint8Array)
|
|
118
|
-
* @param {Uint8Array} info - Context/application-specific info (can be empty)
|
|
119
|
-
* @param {number} length - Desired output length in bytes
|
|
120
|
-
* @returns {Promise<Uint8Array>} Derived key material
|
|
121
|
-
*/
|
|
122
|
-
export async function hkdfSha256(ikm, salt, info, length) {
|
|
123
|
-
if (!isWebCryptoAvailable()) {
|
|
124
|
-
throw new Error('WebCrypto not available');
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const keyMaterial = await crypto.subtle.importKey(
|
|
128
|
-
'raw',
|
|
129
|
-
ikm,
|
|
130
|
-
{ name: 'HKDF' },
|
|
131
|
-
false,
|
|
132
|
-
['deriveBits']
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
const derivedBits = await crypto.subtle.deriveBits(
|
|
136
|
-
{
|
|
137
|
-
name: 'HKDF',
|
|
138
|
-
hash: 'SHA-256',
|
|
139
|
-
salt: salt.length > 0 ? salt : new Uint8Array(32), // WebCrypto requires non-empty salt
|
|
140
|
-
info: info
|
|
141
|
-
},
|
|
142
|
-
keyMaterial,
|
|
143
|
-
length * 8
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
return new Uint8Array(derivedBits);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* HKDF using WebCrypto (SHA-384)
|
|
151
|
-
* @param {Uint8Array} ikm - Input keying material
|
|
152
|
-
* @param {Uint8Array} salt - Salt value
|
|
153
|
-
* @param {Uint8Array} info - Context/application-specific info
|
|
154
|
-
* @param {number} length - Desired output length in bytes
|
|
155
|
-
* @returns {Promise<Uint8Array>} Derived key material
|
|
156
|
-
*/
|
|
157
|
-
export async function hkdfSha384(ikm, salt, info, length) {
|
|
158
|
-
if (!isWebCryptoAvailable()) {
|
|
159
|
-
throw new Error('WebCrypto not available');
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const keyMaterial = await crypto.subtle.importKey(
|
|
163
|
-
'raw',
|
|
164
|
-
ikm,
|
|
165
|
-
{ name: 'HKDF' },
|
|
166
|
-
false,
|
|
167
|
-
['deriveBits']
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
const derivedBits = await crypto.subtle.deriveBits(
|
|
171
|
-
{
|
|
172
|
-
name: 'HKDF',
|
|
173
|
-
hash: 'SHA-384',
|
|
174
|
-
salt: salt.length > 0 ? salt : new Uint8Array(48),
|
|
175
|
-
info: info
|
|
176
|
-
},
|
|
177
|
-
keyMaterial,
|
|
178
|
-
length * 8
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
return new Uint8Array(derivedBits);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Generate cryptographically secure random bytes
|
|
186
|
-
* @param {number} length - Number of random bytes to generate
|
|
187
|
-
* @returns {Uint8Array} Random bytes
|
|
188
|
-
*/
|
|
189
|
-
export function getRandomBytes(length) {
|
|
190
|
-
if (!isWebCryptoAvailable()) {
|
|
191
|
-
throw new Error('WebCrypto not available');
|
|
192
|
-
}
|
|
193
|
-
const bytes = new Uint8Array(length);
|
|
194
|
-
crypto.getRandomValues(bytes);
|
|
195
|
-
return bytes;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Generate a random AES-GCM IV (12 bytes recommended)
|
|
200
|
-
* @returns {Uint8Array} 12-byte IV
|
|
201
|
-
*/
|
|
202
|
-
export function generateIv() {
|
|
203
|
-
return getRandomBytes(12);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Generate a random AES key
|
|
208
|
-
* @param {number} [bits=256] - Key size in bits (128, 192, or 256)
|
|
209
|
-
* @returns {Uint8Array} Random key
|
|
210
|
-
*/
|
|
211
|
-
export function generateAesKey(bits = 256) {
|
|
212
|
-
if (![128, 192, 256].includes(bits)) {
|
|
213
|
-
throw new Error('AES key size must be 128, 192, or 256 bits');
|
|
214
|
-
}
|
|
215
|
-
return getRandomBytes(bits / 8);
|
|
216
|
-
}
|