hd-wallet-wasm 0.1.9 → 0.2.0
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/dist/index.d.ts +20 -2
- package/package.json +2 -1
- package/src/index.d.ts +20 -2
- package/src/index.mjs +44 -1
- package/src/webcrypto.mjs +216 -0
package/dist/index.d.ts
CHANGED
|
@@ -570,7 +570,7 @@ export interface Keyring {
|
|
|
570
570
|
// =============================================================================
|
|
571
571
|
|
|
572
572
|
export interface UtilsAPI {
|
|
573
|
-
// Hashing
|
|
573
|
+
// Hashing (sync - Crypto++)
|
|
574
574
|
sha256(data: Uint8Array): Uint8Array;
|
|
575
575
|
sha512(data: Uint8Array): Uint8Array;
|
|
576
576
|
keccak256(data: Uint8Array): Uint8Array;
|
|
@@ -579,11 +579,29 @@ export interface UtilsAPI {
|
|
|
579
579
|
blake2b(data: Uint8Array, outputLength?: number): Uint8Array;
|
|
580
580
|
blake2s(data: Uint8Array, outputLength?: number): Uint8Array;
|
|
581
581
|
|
|
582
|
-
// Key derivation
|
|
582
|
+
// Key derivation (sync - Crypto++)
|
|
583
583
|
hkdf(ikm: Uint8Array, salt: Uint8Array, info: Uint8Array, length: number): Uint8Array;
|
|
584
584
|
pbkdf2(password: Uint8Array, salt: Uint8Array, iterations: number, length: number): Uint8Array;
|
|
585
585
|
scrypt(password: Uint8Array, salt: Uint8Array, n: number, r: number, p: number, length: number): Uint8Array;
|
|
586
586
|
|
|
587
|
+
// Key derivation (async - WebCrypto, hardware accelerated)
|
|
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)
|
|
592
|
+
aesGcm: {
|
|
593
|
+
encrypt(key: Uint8Array, plaintext: Uint8Array, iv: Uint8Array, aad?: Uint8Array): Promise<{ ciphertext: Uint8Array; tag: Uint8Array }>;
|
|
594
|
+
decrypt(key: Uint8Array, ciphertext: Uint8Array, tag: Uint8Array, iv: Uint8Array, aad?: Uint8Array): Promise<Uint8Array>;
|
|
595
|
+
generateIv(): Uint8Array;
|
|
596
|
+
generateKey(bits?: number): Uint8Array;
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
// WebCrypto availability
|
|
600
|
+
isWebCryptoAvailable(): boolean;
|
|
601
|
+
|
|
602
|
+
// Random number generation
|
|
603
|
+
getRandomBytes(length: number): Uint8Array;
|
|
604
|
+
|
|
587
605
|
// Encoding
|
|
588
606
|
encodeBase58(data: Uint8Array): string;
|
|
589
607
|
decodeBase58(str: string): Uint8Array;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hd-wallet-wasm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
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,6 +25,7 @@
|
|
|
25
25
|
"files": [
|
|
26
26
|
"src/index.mjs",
|
|
27
27
|
"src/index.d.ts",
|
|
28
|
+
"src/webcrypto.mjs",
|
|
28
29
|
"src/aligned.mjs",
|
|
29
30
|
"src/aligned.d.ts",
|
|
30
31
|
"src/generated/",
|
package/src/index.d.ts
CHANGED
|
@@ -570,7 +570,7 @@ export interface Keyring {
|
|
|
570
570
|
// =============================================================================
|
|
571
571
|
|
|
572
572
|
export interface UtilsAPI {
|
|
573
|
-
// Hashing
|
|
573
|
+
// Hashing (sync - Crypto++)
|
|
574
574
|
sha256(data: Uint8Array): Uint8Array;
|
|
575
575
|
sha512(data: Uint8Array): Uint8Array;
|
|
576
576
|
keccak256(data: Uint8Array): Uint8Array;
|
|
@@ -579,11 +579,29 @@ export interface UtilsAPI {
|
|
|
579
579
|
blake2b(data: Uint8Array, outputLength?: number): Uint8Array;
|
|
580
580
|
blake2s(data: Uint8Array, outputLength?: number): Uint8Array;
|
|
581
581
|
|
|
582
|
-
// Key derivation
|
|
582
|
+
// Key derivation (sync - Crypto++)
|
|
583
583
|
hkdf(ikm: Uint8Array, salt: Uint8Array, info: Uint8Array, length: number): Uint8Array;
|
|
584
584
|
pbkdf2(password: Uint8Array, salt: Uint8Array, iterations: number, length: number): Uint8Array;
|
|
585
585
|
scrypt(password: Uint8Array, salt: Uint8Array, n: number, r: number, p: number, length: number): Uint8Array;
|
|
586
586
|
|
|
587
|
+
// Key derivation (async - WebCrypto, hardware accelerated)
|
|
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)
|
|
592
|
+
aesGcm: {
|
|
593
|
+
encrypt(key: Uint8Array, plaintext: Uint8Array, iv: Uint8Array, aad?: Uint8Array): Promise<{ ciphertext: Uint8Array; tag: Uint8Array }>;
|
|
594
|
+
decrypt(key: Uint8Array, ciphertext: Uint8Array, tag: Uint8Array, iv: Uint8Array, aad?: Uint8Array): Promise<Uint8Array>;
|
|
595
|
+
generateIv(): Uint8Array;
|
|
596
|
+
generateKey(bits?: number): Uint8Array;
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
// WebCrypto availability
|
|
600
|
+
isWebCryptoAvailable(): boolean;
|
|
601
|
+
|
|
602
|
+
// Random number generation
|
|
603
|
+
getRandomBytes(length: number): Uint8Array;
|
|
604
|
+
|
|
587
605
|
// Encoding
|
|
588
606
|
encodeBase58(data: Uint8Array): string;
|
|
589
607
|
decodeBase58(str: string): Uint8Array;
|
package/src/index.mjs
CHANGED
|
@@ -15,6 +15,9 @@
|
|
|
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
|
+
|
|
18
21
|
// =============================================================================
|
|
19
22
|
// Enums (matching TypeScript definitions)
|
|
20
23
|
// =============================================================================
|
|
@@ -2219,7 +2222,7 @@ function createModule(wasm) {
|
|
|
2219
2222
|
}
|
|
2220
2223
|
},
|
|
2221
2224
|
|
|
2222
|
-
// Key derivation
|
|
2225
|
+
// Key derivation (sync - uses Crypto++ in WASM)
|
|
2223
2226
|
hkdf(ikm, salt, info, length) {
|
|
2224
2227
|
const ikmPtr = allocAndCopy(wasm, ikm);
|
|
2225
2228
|
const saltPtr = allocAndCopy(wasm, salt);
|
|
@@ -2238,6 +2241,46 @@ function createModule(wasm) {
|
|
|
2238
2241
|
}
|
|
2239
2242
|
},
|
|
2240
2243
|
|
|
2244
|
+
// Key derivation (async - uses WebCrypto for hardware acceleration)
|
|
2245
|
+
hkdfSha256Async: WebCrypto.hkdfSha256,
|
|
2246
|
+
hkdfSha384Async: WebCrypto.hkdfSha384,
|
|
2247
|
+
|
|
2248
|
+
// AES-GCM encryption/decryption (async - uses WebCrypto)
|
|
2249
|
+
aesGcm: {
|
|
2250
|
+
/**
|
|
2251
|
+
* Encrypt data with AES-GCM (WebCrypto)
|
|
2252
|
+
* @param {Uint8Array} key - AES key (16, 24, or 32 bytes)
|
|
2253
|
+
* @param {Uint8Array} plaintext - Data to encrypt
|
|
2254
|
+
* @param {Uint8Array} iv - Initialization vector (12 bytes recommended)
|
|
2255
|
+
* @param {Uint8Array} [aad] - Additional authenticated data
|
|
2256
|
+
* @returns {Promise<{ciphertext: Uint8Array, tag: Uint8Array}>}
|
|
2257
|
+
*/
|
|
2258
|
+
encrypt: WebCrypto.aesGcmEncrypt,
|
|
2259
|
+
|
|
2260
|
+
/**
|
|
2261
|
+
* Decrypt data with AES-GCM (WebCrypto)
|
|
2262
|
+
* @param {Uint8Array} key - AES key
|
|
2263
|
+
* @param {Uint8Array} ciphertext - Encrypted data
|
|
2264
|
+
* @param {Uint8Array} tag - Authentication tag (16 bytes)
|
|
2265
|
+
* @param {Uint8Array} iv - Initialization vector
|
|
2266
|
+
* @param {Uint8Array} [aad] - Additional authenticated data
|
|
2267
|
+
* @returns {Promise<Uint8Array>} Decrypted plaintext
|
|
2268
|
+
*/
|
|
2269
|
+
decrypt: WebCrypto.aesGcmDecrypt,
|
|
2270
|
+
|
|
2271
|
+
/** Generate a random IV (12 bytes) */
|
|
2272
|
+
generateIv: WebCrypto.generateIv,
|
|
2273
|
+
|
|
2274
|
+
/** Generate a random AES key (default 256 bits) */
|
|
2275
|
+
generateKey: WebCrypto.generateAesKey
|
|
2276
|
+
},
|
|
2277
|
+
|
|
2278
|
+
/** Check if WebCrypto is available */
|
|
2279
|
+
isWebCryptoAvailable: WebCrypto.isWebCryptoAvailable,
|
|
2280
|
+
|
|
2281
|
+
/** Generate cryptographically secure random bytes */
|
|
2282
|
+
getRandomBytes: WebCrypto.getRandomBytes,
|
|
2283
|
+
|
|
2241
2284
|
pbkdf2(password, salt, iterations, length) {
|
|
2242
2285
|
const pwdPtr = allocAndCopy(wasm, password);
|
|
2243
2286
|
const saltPtr = allocAndCopy(wasm, salt);
|
|
@@ -0,0 +1,216 @@
|
|
|
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
|
+
}
|