@bcts/crypto 1.0.0-alpha.9 → 1.0.0-beta.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/LICENSE +3 -2
- package/dist/chunk-DQk6qfdC.mjs +18 -0
- package/dist/index.cjs +263 -149
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +134 -73
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +134 -73
- package/dist/index.d.mts.map +1 -1
- package/dist/index.iife.js +255 -141
- package/dist/index.iife.js.map +1 -1
- package/dist/index.mjs +223 -124
- package/dist/index.mjs.map +1 -1
- package/package.json +18 -19
- package/src/argon.ts +14 -9
- package/src/ecdsa-keys.ts +11 -5
- package/src/ecdsa-signing.ts +11 -6
- package/src/ed25519-signing.ts +7 -1
- package/src/error.ts +15 -0
- package/src/hash.ts +10 -4
- package/src/index.ts +24 -18
- package/src/memzero.ts +38 -12
- package/src/public-key-encryption.ts +31 -7
- package/src/schnorr-signing.ts +7 -1
- package/src/scrypt.ts +12 -3
- package/src/symmetric-encryption.ts +7 -1
package/dist/index.mjs
CHANGED
|
@@ -1,86 +1,38 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
import { t as __exportAll } from "./chunk-DQk6qfdC.mjs";
|
|
2
|
+
import { sha256 as sha256$1, sha512 as sha512$1 } from "@noble/hashes/sha2.js";
|
|
3
|
+
import { hmac } from "@noble/hashes/hmac.js";
|
|
4
|
+
import { pbkdf2 } from "@noble/hashes/pbkdf2.js";
|
|
5
|
+
import { hkdf } from "@noble/hashes/hkdf.js";
|
|
6
|
+
import { chacha20poly1305 } from "@noble/ciphers/chacha.js";
|
|
7
|
+
import { ed25519, x25519 } from "@noble/curves/ed25519.js";
|
|
8
|
+
import { schnorr, secp256k1 } from "@noble/curves/secp256k1.js";
|
|
8
9
|
import { SecureRandomNumberGenerator } from "@bcts/rand";
|
|
9
|
-
import { scrypt as scrypt$1 } from "@noble/hashes/scrypt";
|
|
10
|
-
import { argon2id } from "@noble/hashes/argon2";
|
|
10
|
+
import { scrypt as scrypt$1 } from "@noble/hashes/scrypt.js";
|
|
11
|
+
import { argon2id as argon2id$1 } from "@noble/hashes/argon2.js";
|
|
11
12
|
|
|
12
|
-
//#region src/
|
|
13
|
-
/**
|
|
14
|
-
* AEAD-specific error for authentication failures
|
|
15
|
-
*/
|
|
16
|
-
var AeadError = class extends Error {
|
|
17
|
-
constructor(message = "AEAD authentication failed") {
|
|
18
|
-
super(message);
|
|
19
|
-
this.name = "AeadError";
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
/**
|
|
23
|
-
* Generic crypto error type
|
|
24
|
-
*/
|
|
25
|
-
var CryptoError = class CryptoError extends Error {
|
|
26
|
-
cause;
|
|
27
|
-
constructor(message, cause) {
|
|
28
|
-
super(message);
|
|
29
|
-
this.name = "CryptoError";
|
|
30
|
-
this.cause = cause;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Create a CryptoError for AEAD authentication failures.
|
|
34
|
-
*
|
|
35
|
-
* @param error - Optional underlying AeadError
|
|
36
|
-
* @returns A CryptoError wrapping the AEAD error
|
|
37
|
-
*/
|
|
38
|
-
static aead(error) {
|
|
39
|
-
return new CryptoError("AEAD error", error ?? new AeadError());
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Create a CryptoError for invalid parameter values.
|
|
43
|
-
*
|
|
44
|
-
* @param message - Description of the invalid parameter
|
|
45
|
-
* @returns A CryptoError describing the invalid parameter
|
|
46
|
-
*/
|
|
47
|
-
static invalidParameter(message) {
|
|
48
|
-
return new CryptoError(`Invalid parameter: ${message}`);
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
//#endregion
|
|
53
|
-
//#region src/memzero.ts
|
|
13
|
+
//#region src/hash.ts
|
|
54
14
|
/**
|
|
55
|
-
*
|
|
15
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
16
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
56
17
|
*
|
|
57
|
-
* **IMPORTANT: This is a best-effort implementation.** Unlike the Rust reference
|
|
58
|
-
* implementation which uses `std::ptr::write_volatile()` for guaranteed volatile
|
|
59
|
-
* writes, JavaScript engines and JIT compilers can still potentially optimize
|
|
60
|
-
* away these zeroing operations. The check at the end helps prevent optimization,
|
|
61
|
-
* but it is not foolproof.
|
|
62
|
-
*
|
|
63
|
-
* For truly sensitive cryptographic operations, consider using the Web Crypto API's
|
|
64
|
-
* `crypto.subtle` with non-extractable keys when possible, as it provides stronger
|
|
65
|
-
* guarantees than what can be achieved with pure JavaScript.
|
|
66
|
-
*
|
|
67
|
-
* This function attempts to prevent the compiler from optimizing away
|
|
68
|
-
* the zeroing operation by using a verification check after the zeroing loop.
|
|
69
18
|
*/
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
19
|
+
var hash_exports = /* @__PURE__ */ __exportAll({
|
|
20
|
+
CRC32_SIZE: () => 4,
|
|
21
|
+
SHA256_SIZE: () => 32,
|
|
22
|
+
SHA512_SIZE: () => 64,
|
|
23
|
+
crc32: () => crc32,
|
|
24
|
+
crc32Data: () => crc32Data,
|
|
25
|
+
crc32DataOpt: () => crc32DataOpt,
|
|
26
|
+
doubleSha256: () => doubleSha256,
|
|
27
|
+
hkdfHmacSha256: () => hkdfHmacSha256,
|
|
28
|
+
hkdfHmacSha512: () => hkdfHmacSha512,
|
|
29
|
+
hmacSha256: () => hmacSha256,
|
|
30
|
+
hmacSha512: () => hmacSha512,
|
|
31
|
+
pbkdf2HmacSha256: () => pbkdf2HmacSha256,
|
|
32
|
+
pbkdf2HmacSha512: () => pbkdf2HmacSha512,
|
|
33
|
+
sha256: () => sha256,
|
|
34
|
+
sha512: () => sha512
|
|
35
|
+
});
|
|
84
36
|
const CRC32_SIZE = 4;
|
|
85
37
|
const SHA256_SIZE = 32;
|
|
86
38
|
const SHA512_SIZE = 64;
|
|
@@ -177,8 +129,103 @@ function hkdfHmacSha512(keyMaterial, salt, keyLen) {
|
|
|
177
129
|
return hkdf(sha512$1, keyMaterial, salt, void 0, keyLen);
|
|
178
130
|
}
|
|
179
131
|
|
|
132
|
+
//#endregion
|
|
133
|
+
//#region src/error.ts
|
|
134
|
+
/**
|
|
135
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
136
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
137
|
+
*
|
|
138
|
+
*/
|
|
139
|
+
/**
|
|
140
|
+
* AEAD-specific error for authentication failures
|
|
141
|
+
*/
|
|
142
|
+
var AeadError = class extends Error {
|
|
143
|
+
constructor(message = "AEAD authentication failed") {
|
|
144
|
+
super(message);
|
|
145
|
+
this.name = "AeadError";
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* Generic crypto error type
|
|
150
|
+
*/
|
|
151
|
+
var CryptoError = class CryptoError extends Error {
|
|
152
|
+
cause;
|
|
153
|
+
constructor(message, cause) {
|
|
154
|
+
super(message);
|
|
155
|
+
this.name = "CryptoError";
|
|
156
|
+
this.cause = cause;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Create a CryptoError for AEAD authentication failures.
|
|
160
|
+
*
|
|
161
|
+
* @param error - Optional underlying AeadError
|
|
162
|
+
* @returns A CryptoError wrapping the AEAD error
|
|
163
|
+
*/
|
|
164
|
+
static aead(error) {
|
|
165
|
+
return new CryptoError("AEAD error", error ?? new AeadError());
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Create a CryptoError for invalid parameter values.
|
|
169
|
+
*
|
|
170
|
+
* **TS-specific.** Rust's `bc_crypto::Error` enum has no
|
|
171
|
+
* `InvalidParameter` variant; size validation in Rust is enforced at
|
|
172
|
+
* compile time via fixed-size array references (e.g. `&[u8; 32]`) or
|
|
173
|
+
* via `panic!`/`expect(...)` for runtime checks. The TS port has no
|
|
174
|
+
* fixed-size array types, so it surfaces those same conditions through
|
|
175
|
+
* a thrown `CryptoError.invalidParameter(...)`. Catching this is
|
|
176
|
+
* equivalent to defensive guards around an `expect`-style panic on the
|
|
177
|
+
* Rust side.
|
|
178
|
+
*
|
|
179
|
+
* @param message - Description of the invalid parameter
|
|
180
|
+
* @returns A CryptoError describing the invalid parameter
|
|
181
|
+
*/
|
|
182
|
+
static invalidParameter(message) {
|
|
183
|
+
return new CryptoError(`Invalid parameter: ${message}`);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
//#endregion
|
|
188
|
+
//#region src/memzero.ts
|
|
189
|
+
/**
|
|
190
|
+
* Securely zero out a typed array.
|
|
191
|
+
*
|
|
192
|
+
* Mirrors Rust `bc_crypto::memzero<T>(s: &mut [T])`. The Rust impl uses
|
|
193
|
+
* `std::ptr::write_volatile()` to guarantee the writes survive optimization;
|
|
194
|
+
* JavaScript has no equivalent primitive, so this is **best-effort** — JIT
|
|
195
|
+
* compilers may still elide the loop, though the post-hoc verification
|
|
196
|
+
* check forces the engine to keep the writes observable.
|
|
197
|
+
*
|
|
198
|
+
* For truly sensitive cryptographic operations, consider using the Web
|
|
199
|
+
* Crypto API's `crypto.subtle` with non-extractable keys when possible, as
|
|
200
|
+
* it provides stronger guarantees than what can be achieved with pure
|
|
201
|
+
* JavaScript.
|
|
202
|
+
*
|
|
203
|
+
* Accepts any of the standard numeric typed arrays — `Uint8Array`,
|
|
204
|
+
* `Uint8ClampedArray`, `Uint16Array`, `Uint32Array`, `Int8Array`,
|
|
205
|
+
* `Int16Array`, `Int32Array`, `Float32Array`, `Float64Array` — matching
|
|
206
|
+
* Rust's generic `&mut [T]`. (`BigInt64Array` / `BigUint64Array` are
|
|
207
|
+
* excluded because their elements are `bigint`, not `number`; if that
|
|
208
|
+
* support is needed, add a dedicated overload.)
|
|
209
|
+
*/
|
|
210
|
+
function memzero(data) {
|
|
211
|
+
const len = data.length;
|
|
212
|
+
for (let i = 0; i < len; i++) data[i] = 0;
|
|
213
|
+
if (data.length > 0 && data[0] !== 0) throw new Error("memzero failed");
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Securely zero out an array of Uint8Arrays.
|
|
217
|
+
*/
|
|
218
|
+
function memzeroVecVecU8(arrays) {
|
|
219
|
+
for (const arr of arrays) memzero(arr);
|
|
220
|
+
}
|
|
221
|
+
|
|
180
222
|
//#endregion
|
|
181
223
|
//#region src/symmetric-encryption.ts
|
|
224
|
+
/**
|
|
225
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
226
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
227
|
+
*
|
|
228
|
+
*/
|
|
182
229
|
const SYMMETRIC_KEY_SIZE = 32;
|
|
183
230
|
const SYMMETRIC_NONCE_SIZE = 12;
|
|
184
231
|
const SYMMETRIC_AUTH_SIZE = 16;
|
|
@@ -213,10 +260,10 @@ function aeadChaCha20Poly1305Encrypt(plaintext, key, nonce) {
|
|
|
213
260
|
* @throws {CryptoError} If key is not 32 bytes or nonce is not 12 bytes
|
|
214
261
|
*/
|
|
215
262
|
function aeadChaCha20Poly1305EncryptWithAad(plaintext, key, nonce, aad) {
|
|
216
|
-
if (key.length !==
|
|
217
|
-
if (nonce.length !==
|
|
263
|
+
if (key.length !== 32) throw CryptoError.invalidParameter(`Key must be ${32} bytes`);
|
|
264
|
+
if (nonce.length !== 12) throw CryptoError.invalidParameter(`Nonce must be ${12} bytes`);
|
|
218
265
|
const sealed = chacha20poly1305(key, nonce, aad).encrypt(plaintext);
|
|
219
|
-
return [sealed.slice(0, sealed.length -
|
|
266
|
+
return [sealed.slice(0, sealed.length - 16), sealed.slice(sealed.length - 16)];
|
|
220
267
|
}
|
|
221
268
|
/**
|
|
222
269
|
* Decrypt data using ChaCha20-Poly1305 AEAD cipher.
|
|
@@ -245,9 +292,9 @@ function aeadChaCha20Poly1305Decrypt(ciphertext, key, nonce, authTag) {
|
|
|
245
292
|
* @throws {CryptoError} If authentication fails (tampered data, wrong key/nonce, or AAD mismatch)
|
|
246
293
|
*/
|
|
247
294
|
function aeadChaCha20Poly1305DecryptWithAad(ciphertext, key, nonce, aad, authTag) {
|
|
248
|
-
if (key.length !==
|
|
249
|
-
if (nonce.length !==
|
|
250
|
-
if (authTag.length !==
|
|
295
|
+
if (key.length !== 32) throw CryptoError.invalidParameter(`Key must be ${32} bytes`);
|
|
296
|
+
if (nonce.length !== 12) throw CryptoError.invalidParameter(`Nonce must be ${12} bytes`);
|
|
297
|
+
if (authTag.length !== 16) throw CryptoError.invalidParameter(`Auth tag must be ${16} bytes`);
|
|
251
298
|
const sealed = new Uint8Array(ciphertext.length + authTag.length);
|
|
252
299
|
sealed.set(ciphertext);
|
|
253
300
|
sealed.set(authTag, ciphertext.length);
|
|
@@ -261,8 +308,11 @@ function aeadChaCha20Poly1305DecryptWithAad(ciphertext, key, nonce, aad, authTag
|
|
|
261
308
|
|
|
262
309
|
//#endregion
|
|
263
310
|
//#region src/public-key-encryption.ts
|
|
264
|
-
|
|
265
|
-
|
|
311
|
+
/**
|
|
312
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
313
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
314
|
+
*
|
|
315
|
+
*/
|
|
266
316
|
const X25519_PRIVATE_KEY_SIZE = 32;
|
|
267
317
|
const X25519_PUBLIC_KEY_SIZE = 32;
|
|
268
318
|
/**
|
|
@@ -270,7 +320,7 @@ const X25519_PUBLIC_KEY_SIZE = 32;
|
|
|
270
320
|
* Uses HKDF with "agreement" as domain separation salt.
|
|
271
321
|
*/
|
|
272
322
|
function deriveAgreementPrivateKey(keyMaterial) {
|
|
273
|
-
return hkdfHmacSha256(keyMaterial, new TextEncoder().encode("agreement"),
|
|
323
|
+
return hkdfHmacSha256(keyMaterial, new TextEncoder().encode("agreement"), 32);
|
|
274
324
|
}
|
|
275
325
|
/**
|
|
276
326
|
* Derive a signing private key from key material.
|
|
@@ -283,35 +333,52 @@ function deriveSigningPrivateKey(keyMaterial) {
|
|
|
283
333
|
* Generate a new random X25519 private key.
|
|
284
334
|
*/
|
|
285
335
|
function x25519NewPrivateKeyUsing(rng) {
|
|
286
|
-
return rng.randomData(
|
|
336
|
+
return rng.randomData(32);
|
|
287
337
|
}
|
|
288
338
|
/**
|
|
289
339
|
* Derive an X25519 public key from a private key.
|
|
290
340
|
*/
|
|
291
341
|
function x25519PublicKeyFromPrivateKey(privateKey) {
|
|
292
|
-
if (privateKey.length !==
|
|
342
|
+
if (privateKey.length !== 32) throw new Error(`Private key must be ${32} bytes`);
|
|
293
343
|
return x25519.getPublicKey(privateKey);
|
|
294
344
|
}
|
|
345
|
+
const SYMMETRIC_KEY_SIZE$1 = 32;
|
|
295
346
|
/**
|
|
296
|
-
* Compute a shared
|
|
347
|
+
* Compute a shared symmetric key using X25519 key agreement (ECDH).
|
|
348
|
+
*
|
|
349
|
+
* This function performs X25519 Diffie-Hellman key agreement and then
|
|
350
|
+
* derives a symmetric key using HKDF-SHA256 with "agreement" as the salt.
|
|
351
|
+
* This matches the Rust bc-crypto implementation for cross-platform compatibility.
|
|
297
352
|
*
|
|
298
|
-
* **
|
|
299
|
-
*
|
|
300
|
-
*
|
|
353
|
+
* **Low-order public key handling.** The underlying `@noble/curves` X25519
|
|
354
|
+
* implementation rejects low-order public keys (where the u-coordinate is
|
|
355
|
+
* `0`) by throwing `'invalid private or public key received'`. Rust's
|
|
356
|
+
* `x25519-dalek` (v2.0-rc.2) instead silently produces the all-zero shared
|
|
357
|
+
* secret. This means an adversarial low-order public key fed in via TS
|
|
358
|
+
* surfaces as an exception, while in Rust it would yield an HKDF-derived
|
|
359
|
+
* key from a zero shared secret. For honest inputs both implementations
|
|
360
|
+
* produce byte-identical results; the TS port's stricter behaviour is a
|
|
361
|
+
* security improvement, not a parity bug.
|
|
301
362
|
*
|
|
302
363
|
* @param x25519Private - 32-byte X25519 private key
|
|
303
364
|
* @param x25519Public - 32-byte X25519 public key from the other party
|
|
304
|
-
* @returns 32-byte
|
|
365
|
+
* @returns 32-byte derived symmetric key
|
|
305
366
|
* @throws {Error} If private key is not 32 bytes or public key is not 32 bytes
|
|
367
|
+
* @throws {Error} If the public key is low-order (`@noble/curves`-specific guard)
|
|
306
368
|
*/
|
|
307
369
|
function x25519SharedKey(x25519Private, x25519Public) {
|
|
308
|
-
if (x25519Private.length !==
|
|
309
|
-
if (x25519Public.length !==
|
|
310
|
-
return x25519.getSharedSecret(x25519Private, x25519Public);
|
|
370
|
+
if (x25519Private.length !== 32) throw new Error(`Private key must be ${32} bytes`);
|
|
371
|
+
if (x25519Public.length !== 32) throw new Error(`Public key must be ${32} bytes`);
|
|
372
|
+
return hkdfHmacSha256(x25519.getSharedSecret(x25519Private, x25519Public), new TextEncoder().encode("agreement"), SYMMETRIC_KEY_SIZE$1);
|
|
311
373
|
}
|
|
312
374
|
|
|
313
375
|
//#endregion
|
|
314
376
|
//#region src/ecdsa-keys.ts
|
|
377
|
+
/**
|
|
378
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
379
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
380
|
+
*
|
|
381
|
+
*/
|
|
315
382
|
const ECDSA_PRIVATE_KEY_SIZE = 32;
|
|
316
383
|
const ECDSA_PUBLIC_KEY_SIZE = 33;
|
|
317
384
|
const ECDSA_UNCOMPRESSED_PUBLIC_KEY_SIZE = 65;
|
|
@@ -326,28 +393,28 @@ const SCHNORR_PUBLIC_KEY_SIZE = 32;
|
|
|
326
393
|
* the key is used.
|
|
327
394
|
*/
|
|
328
395
|
function ecdsaNewPrivateKeyUsing(rng) {
|
|
329
|
-
return rng.randomData(
|
|
396
|
+
return rng.randomData(32);
|
|
330
397
|
}
|
|
331
398
|
/**
|
|
332
399
|
* Derive a compressed ECDSA public key from a private key.
|
|
333
400
|
*/
|
|
334
401
|
function ecdsaPublicKeyFromPrivateKey(privateKey) {
|
|
335
|
-
if (privateKey.length !==
|
|
402
|
+
if (privateKey.length !== 32) throw new Error(`Private key must be ${32} bytes`);
|
|
336
403
|
return secp256k1.getPublicKey(privateKey, true);
|
|
337
404
|
}
|
|
338
405
|
/**
|
|
339
406
|
* Decompress a compressed public key to uncompressed format.
|
|
340
407
|
*/
|
|
341
408
|
function ecdsaDecompressPublicKey(compressed) {
|
|
342
|
-
if (compressed.length !==
|
|
343
|
-
return secp256k1.
|
|
409
|
+
if (compressed.length !== 33) throw new Error(`Compressed public key must be ${33} bytes`);
|
|
410
|
+
return secp256k1.Point.fromBytes(compressed).toBytes(false);
|
|
344
411
|
}
|
|
345
412
|
/**
|
|
346
413
|
* Compress an uncompressed public key.
|
|
347
414
|
*/
|
|
348
415
|
function ecdsaCompressPublicKey(uncompressed) {
|
|
349
|
-
if (uncompressed.length !==
|
|
350
|
-
return secp256k1.
|
|
416
|
+
if (uncompressed.length !== 65) throw new Error(`Uncompressed public key must be ${65} bytes`);
|
|
417
|
+
return secp256k1.Point.fromBytes(uncompressed).toBytes(true);
|
|
351
418
|
}
|
|
352
419
|
/**
|
|
353
420
|
* Derive an ECDSA private key from key material using HKDF.
|
|
@@ -356,20 +423,25 @@ function ecdsaCompressPublicKey(uncompressed) {
|
|
|
356
423
|
* matching the Rust reference implementation behavior.
|
|
357
424
|
*/
|
|
358
425
|
function ecdsaDerivePrivateKey(keyMaterial) {
|
|
359
|
-
return hkdfHmacSha256(keyMaterial, new TextEncoder().encode("signing"),
|
|
426
|
+
return hkdfHmacSha256(keyMaterial, new TextEncoder().encode("signing"), 32);
|
|
360
427
|
}
|
|
361
428
|
/**
|
|
362
429
|
* Extract the x-only (Schnorr) public key from a private key.
|
|
363
430
|
* This is used for BIP-340 Schnorr signatures.
|
|
364
431
|
*/
|
|
365
432
|
function schnorrPublicKeyFromPrivateKey(privateKey) {
|
|
366
|
-
if (privateKey.length !==
|
|
433
|
+
if (privateKey.length !== 32) throw new Error(`Private key must be ${32} bytes`);
|
|
367
434
|
return secp256k1.getPublicKey(privateKey, false).slice(1, 33);
|
|
368
435
|
}
|
|
369
436
|
|
|
370
437
|
//#endregion
|
|
371
438
|
//#region src/ecdsa-signing.ts
|
|
372
439
|
/**
|
|
440
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
441
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
442
|
+
*
|
|
443
|
+
*/
|
|
444
|
+
/**
|
|
373
445
|
* Sign a message using ECDSA with secp256k1.
|
|
374
446
|
*
|
|
375
447
|
* The message is hashed with double SHA-256 before signing (Bitcoin standard).
|
|
@@ -384,9 +456,9 @@ function schnorrPublicKeyFromPrivateKey(privateKey) {
|
|
|
384
456
|
* @throws {Error} If private key is not 32 bytes
|
|
385
457
|
*/
|
|
386
458
|
function ecdsaSign(privateKey, message) {
|
|
387
|
-
if (privateKey.length !==
|
|
459
|
+
if (privateKey.length !== 32) throw new Error(`Private key must be ${32} bytes`);
|
|
388
460
|
const messageHash = doubleSha256(message);
|
|
389
|
-
return secp256k1.sign(messageHash, privateKey)
|
|
461
|
+
return secp256k1.sign(messageHash, privateKey, { prehash: false });
|
|
390
462
|
}
|
|
391
463
|
/**
|
|
392
464
|
* Verify an ECDSA signature with secp256k1.
|
|
@@ -400,11 +472,11 @@ function ecdsaSign(privateKey, message) {
|
|
|
400
472
|
* @throws {Error} If public key is not 33 bytes or signature is not 64 bytes
|
|
401
473
|
*/
|
|
402
474
|
function ecdsaVerify(publicKey, signature, message) {
|
|
403
|
-
if (publicKey.length !==
|
|
404
|
-
if (signature.length !==
|
|
475
|
+
if (publicKey.length !== 33) throw new Error(`Public key must be ${33} bytes`);
|
|
476
|
+
if (signature.length !== 64) throw new Error(`Signature must be ${64} bytes`);
|
|
405
477
|
try {
|
|
406
478
|
const messageHash = doubleSha256(message);
|
|
407
|
-
return secp256k1.verify(signature, messageHash, publicKey);
|
|
479
|
+
return secp256k1.verify(signature, messageHash, publicKey, { prehash: false });
|
|
408
480
|
} catch {
|
|
409
481
|
return false;
|
|
410
482
|
}
|
|
@@ -412,6 +484,11 @@ function ecdsaVerify(publicKey, signature, message) {
|
|
|
412
484
|
|
|
413
485
|
//#endregion
|
|
414
486
|
//#region src/schnorr-signing.ts
|
|
487
|
+
/**
|
|
488
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
489
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
490
|
+
*
|
|
491
|
+
*/
|
|
415
492
|
const SCHNORR_SIGNATURE_SIZE = 64;
|
|
416
493
|
/**
|
|
417
494
|
* Sign a message using Schnorr signature (BIP-340).
|
|
@@ -445,7 +522,7 @@ function schnorrSignUsing(ecdsaPrivateKey, message, rng) {
|
|
|
445
522
|
* @returns 64-byte Schnorr signature
|
|
446
523
|
*/
|
|
447
524
|
function schnorrSignWithAuxRand(ecdsaPrivateKey, message, auxRand) {
|
|
448
|
-
if (ecdsaPrivateKey.length !==
|
|
525
|
+
if (ecdsaPrivateKey.length !== 32) throw new Error(`Private key must be ${32} bytes`);
|
|
449
526
|
if (auxRand.length !== 32) throw new Error("Auxiliary randomness must be 32 bytes");
|
|
450
527
|
return schnorr.sign(message, ecdsaPrivateKey, auxRand);
|
|
451
528
|
}
|
|
@@ -458,8 +535,8 @@ function schnorrSignWithAuxRand(ecdsaPrivateKey, message, auxRand) {
|
|
|
458
535
|
* @returns true if signature is valid
|
|
459
536
|
*/
|
|
460
537
|
function schnorrVerify(schnorrPublicKey, signature, message) {
|
|
461
|
-
if (schnorrPublicKey.length !==
|
|
462
|
-
if (signature.length !==
|
|
538
|
+
if (schnorrPublicKey.length !== 32) throw new Error(`Public key must be ${32} bytes`);
|
|
539
|
+
if (signature.length !== 64) throw new Error(`Signature must be ${64} bytes`);
|
|
463
540
|
try {
|
|
464
541
|
return schnorr.verify(signature, message, schnorrPublicKey);
|
|
465
542
|
} catch {
|
|
@@ -469,6 +546,11 @@ function schnorrVerify(schnorrPublicKey, signature, message) {
|
|
|
469
546
|
|
|
470
547
|
//#endregion
|
|
471
548
|
//#region src/ed25519-signing.ts
|
|
549
|
+
/**
|
|
550
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
551
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
552
|
+
*
|
|
553
|
+
*/
|
|
472
554
|
const ED25519_PUBLIC_KEY_SIZE = 32;
|
|
473
555
|
const ED25519_PRIVATE_KEY_SIZE = 32;
|
|
474
556
|
const ED25519_SIGNATURE_SIZE = 64;
|
|
@@ -476,13 +558,13 @@ const ED25519_SIGNATURE_SIZE = 64;
|
|
|
476
558
|
* Generate a new random Ed25519 private key.
|
|
477
559
|
*/
|
|
478
560
|
function ed25519NewPrivateKeyUsing(rng) {
|
|
479
|
-
return rng.randomData(
|
|
561
|
+
return rng.randomData(32);
|
|
480
562
|
}
|
|
481
563
|
/**
|
|
482
564
|
* Derive an Ed25519 public key from a private key.
|
|
483
565
|
*/
|
|
484
566
|
function ed25519PublicKeyFromPrivateKey(privateKey) {
|
|
485
|
-
if (privateKey.length !==
|
|
567
|
+
if (privateKey.length !== 32) throw new Error(`Private key must be ${32} bytes`);
|
|
486
568
|
return ed25519.getPublicKey(privateKey);
|
|
487
569
|
}
|
|
488
570
|
/**
|
|
@@ -497,7 +579,7 @@ function ed25519PublicKeyFromPrivateKey(privateKey) {
|
|
|
497
579
|
* @throws {Error} If private key is not 32 bytes
|
|
498
580
|
*/
|
|
499
581
|
function ed25519Sign(privateKey, message) {
|
|
500
|
-
if (privateKey.length !==
|
|
582
|
+
if (privateKey.length !== 32) throw new Error(`Private key must be ${32} bytes`);
|
|
501
583
|
return ed25519.sign(message, privateKey);
|
|
502
584
|
}
|
|
503
585
|
/**
|
|
@@ -510,8 +592,8 @@ function ed25519Sign(privateKey, message) {
|
|
|
510
592
|
* @throws {Error} If public key is not 32 bytes or signature is not 64 bytes
|
|
511
593
|
*/
|
|
512
594
|
function ed25519Verify(publicKey, message, signature) {
|
|
513
|
-
if (publicKey.length !==
|
|
514
|
-
if (signature.length !==
|
|
595
|
+
if (publicKey.length !== 32) throw new Error(`Public key must be ${32} bytes`);
|
|
596
|
+
if (signature.length !== 64) throw new Error(`Signature must be ${64} bytes`);
|
|
515
597
|
try {
|
|
516
598
|
return ed25519.verify(signature, message, publicKey);
|
|
517
599
|
} catch {
|
|
@@ -522,8 +604,16 @@ function ed25519Verify(publicKey, message, signature) {
|
|
|
522
604
|
//#endregion
|
|
523
605
|
//#region src/scrypt.ts
|
|
524
606
|
/**
|
|
607
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
608
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
609
|
+
*
|
|
610
|
+
*/
|
|
611
|
+
/**
|
|
525
612
|
* Derive a key using Scrypt with recommended parameters.
|
|
526
|
-
*
|
|
613
|
+
*
|
|
614
|
+
* Mirrors Rust `bc_crypto::scrypt` which calls `scrypt::Params::recommended()`.
|
|
615
|
+
* The recommended parameters per the upstream `scrypt` crate are
|
|
616
|
+
* `log_n = 17` (N = 2^17 = 131072), `r = 8`, `p = 1`.
|
|
527
617
|
*
|
|
528
618
|
* @param password - Password or passphrase
|
|
529
619
|
* @param salt - Salt value
|
|
@@ -531,7 +621,7 @@ function ed25519Verify(publicKey, message, signature) {
|
|
|
531
621
|
* @returns Derived key
|
|
532
622
|
*/
|
|
533
623
|
function scrypt(password, salt, outputLen) {
|
|
534
|
-
return scryptOpt(password, salt, outputLen,
|
|
624
|
+
return scryptOpt(password, salt, outputLen, 17, 8, 1);
|
|
535
625
|
}
|
|
536
626
|
/**
|
|
537
627
|
* Derive a key using Scrypt with custom parameters.
|
|
@@ -559,15 +649,24 @@ function scryptOpt(password, salt, outputLen, logN, r, p) {
|
|
|
559
649
|
//#endregion
|
|
560
650
|
//#region src/argon.ts
|
|
561
651
|
/**
|
|
652
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
653
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
654
|
+
*
|
|
655
|
+
*/
|
|
656
|
+
/**
|
|
562
657
|
* Derive a key using Argon2id with default parameters.
|
|
563
658
|
*
|
|
659
|
+
* Mirrors Rust `bc_crypto::argon2id` which calls `Argon2::default()`. The
|
|
660
|
+
* upstream `argon2` crate's defaults are `t = 2` iterations, `m = 19 * 1024
|
|
661
|
+
* = 19456` KiB of memory, `p = 1` lane (per `argon2-0.5.x/src/params.rs`).
|
|
662
|
+
*
|
|
564
663
|
* @param password - Password or passphrase
|
|
565
664
|
* @param salt - Salt value (must be at least 8 bytes)
|
|
566
665
|
* @param outputLen - Desired output length
|
|
567
666
|
* @returns Derived key
|
|
568
667
|
*/
|
|
569
|
-
function
|
|
570
|
-
return argon2idHashOpt(password, salt, outputLen,
|
|
668
|
+
function argon2id(password, salt, outputLen) {
|
|
669
|
+
return argon2idHashOpt(password, salt, outputLen, 2, 19456, 1);
|
|
571
670
|
}
|
|
572
671
|
/**
|
|
573
672
|
* Derive a key using Argon2id with custom parameters.
|
|
@@ -581,7 +680,7 @@ function argon2idHash(password, salt, outputLen) {
|
|
|
581
680
|
* @returns Derived key
|
|
582
681
|
*/
|
|
583
682
|
function argon2idHashOpt(password, salt, outputLen, iterations, memory, parallelism) {
|
|
584
|
-
return argon2id(password, salt, {
|
|
683
|
+
return argon2id$1(password, salt, {
|
|
585
684
|
t: iterations,
|
|
586
685
|
m: memory,
|
|
587
686
|
p: parallelism,
|
|
@@ -590,5 +689,5 @@ function argon2idHashOpt(password, salt, outputLen, iterations, memory, parallel
|
|
|
590
689
|
}
|
|
591
690
|
|
|
592
691
|
//#endregion
|
|
593
|
-
export { AeadError, CRC32_SIZE, CryptoError, ECDSA_MESSAGE_HASH_SIZE, ECDSA_PRIVATE_KEY_SIZE, ECDSA_PUBLIC_KEY_SIZE, ECDSA_SIGNATURE_SIZE, ECDSA_UNCOMPRESSED_PUBLIC_KEY_SIZE, ED25519_PRIVATE_KEY_SIZE, ED25519_PUBLIC_KEY_SIZE, ED25519_SIGNATURE_SIZE,
|
|
692
|
+
export { AeadError, CRC32_SIZE, CryptoError, ECDSA_MESSAGE_HASH_SIZE, ECDSA_PRIVATE_KEY_SIZE, ECDSA_PUBLIC_KEY_SIZE, ECDSA_SIGNATURE_SIZE, ECDSA_UNCOMPRESSED_PUBLIC_KEY_SIZE, ED25519_PRIVATE_KEY_SIZE, ED25519_PUBLIC_KEY_SIZE, ED25519_SIGNATURE_SIZE, SCHNORR_PUBLIC_KEY_SIZE, SCHNORR_SIGNATURE_SIZE, SHA256_SIZE, SHA512_SIZE, SYMMETRIC_AUTH_SIZE, SYMMETRIC_KEY_SIZE, SYMMETRIC_NONCE_SIZE, X25519_PRIVATE_KEY_SIZE, X25519_PUBLIC_KEY_SIZE, aeadChaCha20Poly1305Decrypt, aeadChaCha20Poly1305DecryptWithAad, aeadChaCha20Poly1305Encrypt, aeadChaCha20Poly1305EncryptWithAad, argon2id, deriveAgreementPrivateKey, deriveSigningPrivateKey, doubleSha256, ecdsaCompressPublicKey, ecdsaDecompressPublicKey, ecdsaDerivePrivateKey, ecdsaNewPrivateKeyUsing, ecdsaPublicKeyFromPrivateKey, ecdsaSign, ecdsaVerify, ed25519NewPrivateKeyUsing, ed25519PublicKeyFromPrivateKey, ed25519Sign, ed25519Verify, hash_exports as hash, hkdfHmacSha256, hmacSha256, hmacSha512, memzero, memzeroVecVecU8, pbkdf2HmacSha256, schnorrPublicKeyFromPrivateKey, schnorrSign, schnorrSignUsing, schnorrSignWithAuxRand, schnorrVerify, scrypt, scryptOpt, sha256, sha512, x25519NewPrivateKeyUsing, x25519PublicKeyFromPrivateKey, x25519SharedKey };
|
|
594
693
|
//# sourceMappingURL=index.mjs.map
|