@talismn/crypto 0.0.0-pr2277-20251211071316 → 0.0.0-pr2295-20260110031023

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.
Files changed (43) hide show
  1. package/dist/index.d.mts +124 -0
  2. package/dist/index.d.ts +124 -0
  3. package/dist/index.js +808 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/index.mjs +726 -0
  6. package/dist/index.mjs.map +1 -0
  7. package/package.json +20 -6
  8. package/dist/declarations/src/address/addressFromPublicKey.d.ts +0 -5
  9. package/dist/declarations/src/address/encodeAnyAddress.d.ts +0 -5
  10. package/dist/declarations/src/address/encoding/addressEncodingFromCurve.d.ts +0 -3
  11. package/dist/declarations/src/address/encoding/bitcoin.d.ts +0 -37
  12. package/dist/declarations/src/address/encoding/detectAddressEncoding.d.ts +0 -2
  13. package/dist/declarations/src/address/encoding/ethereum.d.ts +0 -6
  14. package/dist/declarations/src/address/encoding/index.d.ts +0 -6
  15. package/dist/declarations/src/address/encoding/solana.d.ts +0 -2
  16. package/dist/declarations/src/address/encoding/ss58.d.ts +0 -3
  17. package/dist/declarations/src/address/index.d.ts +0 -6
  18. package/dist/declarations/src/address/isAddressEqual.d.ts +0 -1
  19. package/dist/declarations/src/address/isAddressValid.d.ts +0 -1
  20. package/dist/declarations/src/address/normalizeAddress.d.ts +0 -1
  21. package/dist/declarations/src/derivation/common.d.ts +0 -5
  22. package/dist/declarations/src/derivation/deriveEcdsa.d.ts +0 -3
  23. package/dist/declarations/src/derivation/deriveEd25519.d.ts +0 -3
  24. package/dist/declarations/src/derivation/deriveEthereum.d.ts +0 -3
  25. package/dist/declarations/src/derivation/deriveSolana.d.ts +0 -3
  26. package/dist/declarations/src/derivation/deriveSr25519.d.ts +0 -4
  27. package/dist/declarations/src/derivation/index.d.ts +0 -1
  28. package/dist/declarations/src/derivation/utils.d.ts +0 -7
  29. package/dist/declarations/src/encryption/encryptKemAead.d.ts +0 -1
  30. package/dist/declarations/src/encryption/index.d.ts +0 -1
  31. package/dist/declarations/src/hashing/index.d.ts +0 -9
  32. package/dist/declarations/src/index.d.ts +0 -8
  33. package/dist/declarations/src/mnemonic/index.d.ts +0 -9
  34. package/dist/declarations/src/platform/index.d.ts +0 -4
  35. package/dist/declarations/src/types/index.d.ts +0 -9
  36. package/dist/declarations/src/utils/exports.d.ts +0 -2
  37. package/dist/declarations/src/utils/index.d.ts +0 -2
  38. package/dist/declarations/src/utils/pbkdf2.d.ts +0 -1
  39. package/dist/talismn-crypto.cjs.d.ts +0 -1
  40. package/dist/talismn-crypto.cjs.dev.js +0 -784
  41. package/dist/talismn-crypto.cjs.js +0 -7
  42. package/dist/talismn-crypto.cjs.prod.js +0 -784
  43. package/dist/talismn-crypto.esm.js +0 -716
@@ -1,784 +0,0 @@
1
- 'use strict';
2
-
3
- var bip39 = require('@scure/bip39');
4
- var english = require('@scure/bip39/wordlists/english');
5
- var base = require('@scure/base');
6
- var ed25519 = require('@noble/curves/ed25519');
7
- var secp256k1 = require('@noble/curves/secp256k1');
8
- var bech32 = require('bech32');
9
- var bs58check = require('bs58check');
10
- var sha3 = require('@noble/hashes/sha3');
11
- var utils = require('@noble/hashes/utils');
12
- var blake2b = require('@noble/hashes/blake2b');
13
- var blake3$1 = require('@noble/hashes/blake3');
14
- var scaleTs = require('scale-ts');
15
- var bip32 = require('@scure/bip32');
16
- var hmac = require('@noble/hashes/hmac');
17
- var sha512 = require('@noble/hashes/sha512');
18
- var microSr25519 = require('micro-sr25519');
19
- var chacha_js = require('@noble/ciphers/chacha.js');
20
- var utils_js = require('@noble/ciphers/utils.js');
21
- var mlkem = require('mlkem');
22
-
23
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
24
-
25
- var bs58check__default = /*#__PURE__*/_interopDefault(bs58check);
26
-
27
- const pbkdf2 = async (hash, entropy, salt, iterations, outputLenBytes) => {
28
- // NOTE: react-native-quick-crypto (our `global.crypto` polyfill on Talisman Mobile) doesn't support `crypto.subtle.deriveKey`.
29
- // But, we can work around this by using `crypto.subtle.deriveBits` and `crypto.subtle.importKey`, which when used together
30
- // can provide the same functionality as `crypto.subtle.deriveKey`.
31
- const keyMaterial = await crypto.subtle.importKey("raw", entropy, "PBKDF2", false, ["deriveBits"]);
32
- const derivedBits = await crypto.subtle.deriveBits({
33
- name: "PBKDF2",
34
- salt,
35
- iterations,
36
- hash
37
- }, keyMaterial, outputLenBytes * 8);
38
- return new Uint8Array(derivedBits);
39
- };
40
-
41
- const mnemonicToEntropy = mnemonic => {
42
- return bip39.mnemonicToEntropy(mnemonic, english.wordlist);
43
- };
44
- const entropyToMnemonic = entropy => {
45
- return bip39.entropyToMnemonic(entropy, english.wordlist);
46
- };
47
- const entropyToSeedSubstrate = async (entropy, password) => await pbkdf2("SHA-512", entropy, mnemonicPasswordToSalt(password ?? ""), 2048,
48
- // 2048 iterations
49
- 32 // 32 bytes (32 * 8 == 256 bits)
50
- );
51
- const entropyToSeedClassic = async (entropy, password) => await pbkdf2("SHA-512", encodeNormalized(entropyToMnemonic(entropy)), mnemonicPasswordToSalt(password ?? ""), 2048,
52
- // 2048 iterations
53
- 64 // 64 bytes (64 * 8 == 512 bits)
54
- );
55
- const mnemonicPasswordToSalt = password => encodeNormalized(`mnemonic${password}`);
56
-
57
- /** Normalizes a UTF-8 string using `NFKD` form, then encodes it into bytes */
58
- const encodeNormalized = utf8 => new TextEncoder().encode(utf8.normalize("NFKD"));
59
- const getSeedDerivationType = curve => {
60
- switch (curve) {
61
- case "sr25519":
62
- case "ed25519":
63
- case "ecdsa":
64
- return "substrate";
65
- case "ethereum":
66
- case "solana":
67
- return "classic";
68
- case "bitcoin-ecdsa":
69
- case "bitcoin-ed25519":
70
- throw new Error("seed derivation is not implemented for Bitcoin");
71
- }
72
- };
73
-
74
- // when deriving keys from a mnemonic, we usually dont want a password here.
75
- // a password provided here would be used as a 25th mnemonic word.
76
- const entropyToSeed = async (entropy, curve, password) => {
77
- const type = getSeedDerivationType(curve);
78
- switch (type) {
79
- case "classic":
80
- return await entropyToSeedClassic(entropy, password);
81
- case "substrate":
82
- return await entropyToSeedSubstrate(entropy, password);
83
- }
84
- };
85
- const isValidMnemonic = mnemonic => {
86
- return bip39.validateMnemonic(mnemonic, english.wordlist);
87
- };
88
- const generateMnemonic = words => {
89
- switch (words) {
90
- case 12:
91
- return bip39.generateMnemonic(english.wordlist, 128);
92
- case 24:
93
- return bip39.generateMnemonic(english.wordlist, 256);
94
- }
95
- };
96
-
97
- // well-known mnemonic used by polkadot.js, can be checked on polkadot wiki
98
- const DEV_MNEMONIC_POLKADOT = "bottom drive obey lake curtain smoke basket hold race lonely fit walk";
99
-
100
- // well-known phrase used by hardhat and anvil
101
- const DEV_MNEMONIC_ETHEREUM = "test test test test test test test test test test test junk";
102
-
103
- // keep dev seeds in cache as we will reuse them to validate multiple derivation paths
104
- const DEV_SEED_CACHE = new Map();
105
- const getDevSeed = async curve => {
106
- const type = getSeedDerivationType(curve);
107
- if (!DEV_SEED_CACHE.has(type)) {
108
- switch (type) {
109
- case "classic":
110
- {
111
- const entropy = mnemonicToEntropy(DEV_MNEMONIC_ETHEREUM);
112
- const seed = await entropyToSeedClassic(entropy); // 80ms
113
- DEV_SEED_CACHE.set(type, seed);
114
- break;
115
- }
116
- case "substrate":
117
- {
118
- const entropy = mnemonicToEntropy(DEV_MNEMONIC_POLKADOT);
119
- const seed = await entropyToSeedSubstrate(entropy); // 80ms
120
- DEV_SEED_CACHE.set(type, seed);
121
- break;
122
- }
123
- default:
124
- throw new Error("Unsupported derivation type");
125
- }
126
- }
127
- return DEV_SEED_CACHE.get(type);
128
- };
129
-
130
- /** NOTE: Try not to use this too much, it will need to change */
131
- const addressEncodingFromCurve = curve => {
132
- switch (curve) {
133
- case "sr25519":
134
- case "ed25519":
135
- case "ecdsa":
136
- return "ss58";
137
- case "bitcoin-ecdsa":
138
- case "bitcoin-ed25519":
139
- // NOTE: Bitcoin has multiple address formats, so this isn't necessarily correct
140
- // The format MAY be bech32m, but it might also be bech32 or base58check.
141
- // bech32m is the most recent format.
142
- return "bech32m";
143
- case "ethereum":
144
- return "ethereum";
145
- case "solana":
146
- return "base58solana";
147
- }
148
- };
149
-
150
- const encodeAddressSolana = publicKey => {
151
- if (publicKey.length !== 32) throw new Error("Public key must be 32 bytes long for Solana base58 encoding");
152
- return base.base58.encode(publicKey);
153
- };
154
- function isSolanaAddress(address) {
155
- try {
156
- const bytes = base.base58.decode(address);
157
- return bytes.length === 32;
158
- } catch (error) {
159
- return false;
160
- }
161
- }
162
-
163
- const isBitcoinAddress = address => isBech32mAddress(address) || isBech32Address(address) || isBase58CheckAddress(address);
164
- function isBech32mAddress(address) {
165
- try {
166
- fromBech32m(address);
167
- } catch {
168
- return false;
169
- }
170
- return true;
171
- }
172
- function isBech32Address(address) {
173
- try {
174
- fromBech32(address);
175
- } catch {
176
- return false;
177
- }
178
- return true;
179
- }
180
- function isBase58CheckAddress(address) {
181
- try {
182
- fromBase58Check(address);
183
- } catch {
184
- return false;
185
- }
186
- return true;
187
- }
188
-
189
- /**
190
- * Converts a Bech32m encoded address to its corresponding data representation.
191
- * @param address - The Bech32m encoded address.
192
- * @returns An object containing the version, prefix, and data of the address.
193
- * @throws {TypeError} If the address uses the wrong encoding.
194
- */
195
- function fromBech32m(address) {
196
- const result = bech32.bech32m.decode(address);
197
- const version = result.words[0];
198
- if (version === 0) throw new TypeError(address + " uses wrong encoding");
199
- const data = bech32.bech32m.fromWords(result.words.slice(1));
200
- return {
201
- version,
202
- prefix: result.prefix,
203
- data: Uint8Array.from(data)
204
- };
205
- }
206
-
207
- /**
208
- * Converts a Bech32 encoded address to its corresponding data representation.
209
- * @param address - The Bech32 encoded address.
210
- * @returns An object containing the version, prefix, and data of the address.
211
- * @throws {TypeError} If the address uses the wrong encoding.
212
- */
213
- function fromBech32(address) {
214
- const result = bech32.bech32.decode(address);
215
- const version = result.words[0];
216
- if (version !== 0) throw new TypeError(address + " uses wrong encoding");
217
- const data = bech32.bech32.fromWords(result.words.slice(1));
218
- return {
219
- version,
220
- prefix: result.prefix,
221
- data: Uint8Array.from(data)
222
- };
223
- }
224
-
225
- /**
226
- * Decodes a base58check encoded Bitcoin address and returns the version and hash.
227
- *
228
- * @param address - The base58check encoded Bitcoin address to decode.
229
- * @returns An object containing the version and hash of the decoded address.
230
- * @throws {TypeError} If the address is too short or too long.
231
- */
232
- function fromBase58Check(address) {
233
- const payload = bs58check__default.default.decode(address);
234
- if (payload.length < 21) throw new TypeError(address + " is too short");
235
- if (payload.length > 21) throw new TypeError(address + " is too long");
236
- function readUInt8(buffer, offset) {
237
- if (offset + 1 > buffer.length) {
238
- throw new Error("Offset is outside the bounds of Uint8Array");
239
- }
240
- const buf = Buffer.from(buffer);
241
- return buf.readUInt8(offset);
242
- }
243
- const version = readUInt8(payload, 0);
244
- const hash = payload.slice(1);
245
- return {
246
- version,
247
- hash
248
- };
249
- }
250
-
251
- /**
252
- * Encodes a public key using H160 encoding with Ethereum checksum.
253
- */
254
- const encodeAddressEthereum = publicKey => {
255
- // Ensure the public key is in uncompressed format (starts with 0x04)
256
- if (publicKey[0] !== 0x04) throw new Error("Invalid public key format");
257
-
258
- // Remove the prefix (0x04)
259
- const publicKeyWithoutPrefix = publicKey.slice(1);
260
-
261
- // Apply Keccak-256 hashing to the public key
262
- const hash = sha3.keccak_256(publicKeyWithoutPrefix);
263
-
264
- // Take the last 20 bytes of the hash to get the Ethereum address
265
- const address = hash.slice(-20);
266
-
267
- // Apply checksum
268
- return checksumEthereumAddress(`0x${utils.bytesToHex(address)}`);
269
- };
270
- const checksumEthereumAddress = address => {
271
- const addr = address.toLowerCase().replace(/^0x/, "");
272
- const hash = sha3.keccak_256(new TextEncoder().encode(addr));
273
- const hashHex = utils.bytesToHex(hash);
274
- const checksum = addr.split("").map((char, i) => parseInt(hashHex[i], 16) >= 8 ? char.toUpperCase() : char).join("");
275
- return `0x${checksum}`;
276
- };
277
- function isEthereumAddress(address) {
278
- return /^0x[a-fA-F0-9]{40}$/.test(address);
279
- }
280
-
281
- // Note: public key cannot be retrieved from an address, because address is hashed from only the last 20 bytes of the pk
282
-
283
- const blake3 = blake3$1.blake3;
284
- const blake2b256 = msg => blake2b.blake2b(msg, {
285
- dkLen: 32
286
- });
287
- const blake2b512 = msg => blake2b.blake2b(msg, {
288
- dkLen: 64
289
- });
290
- const getSafeHash = bytes => {
291
- // cryptographically secure one way hash
292
- // outputs 44 characters without special characters
293
- return base.base58.encode(blake3$1.blake3(bytes));
294
- };
295
-
296
- // Inspired from MIT licensed @polkadot-labs/hdkd-helpers
297
- // https://github.com/polkadot-labs/hdkd/blob/3ef6e02827212d934b59a4e566d8aa61d3ba7b27/packages/hdkd-helpers/src/accountId.ts#L3
298
-
299
- const VALID_PUBLICKEY_LENGTHS = [32, 33];
300
- const accountId = publicKey => {
301
- if (!VALID_PUBLICKEY_LENGTHS.includes(publicKey.length)) throw new Error("Invalid publicKey");
302
- return publicKey.length === 33 ? blake2b256(publicKey) : publicKey;
303
- };
304
- const SS58PRE = /* @__PURE__ */new TextEncoder().encode("SS58PRE");
305
- const CHECKSUM_LENGTH = 2;
306
- const VALID_PAYLOAD_LENGTHS = [32, 33];
307
- const ss58Encode = (payload, prefix = 42) => {
308
- if (!VALID_PAYLOAD_LENGTHS.includes(payload.length)) throw new Error("Invalid payload");
309
- const prefixBytes = prefix < 64 ? Uint8Array.of(prefix) : Uint8Array.of((prefix & 0b0000_0000_1111_1100) >> 2 | 0b0100_0000, prefix >> 8 | (prefix & 0b0000_0000_0000_0011) << 6);
310
- const checksum = blake2b512(Uint8Array.of(...SS58PRE, ...prefixBytes, ...payload)).subarray(0, CHECKSUM_LENGTH);
311
- return base.base58.encode(Uint8Array.of(...prefixBytes, ...payload, ...checksum));
312
- };
313
- const VALID_ADDRESS_LENGTHS = [35, 36, 37];
314
- const decodeSs58Address = addressStr => {
315
- const address = base.base58.decode(addressStr);
316
- if (!VALID_ADDRESS_LENGTHS.includes(address.length)) throw new Error("Invalid address length");
317
- const addressChecksum = address.subarray(address.length - CHECKSUM_LENGTH);
318
- const checksum = blake2b512(Uint8Array.of(...SS58PRE, ...address.subarray(0, address.length - CHECKSUM_LENGTH))).subarray(0, CHECKSUM_LENGTH);
319
- if (addressChecksum[0] !== checksum[0] || addressChecksum[1] !== checksum[1]) throw new Error("Invalid checksum");
320
- const prefixLength = address[0] & 0b0100_0000 ? 2 : 1;
321
- const prefix = prefixLength === 1 ? address[0] : (address[0] & 0b0011_1111) << 2 | address[1] >> 6 | (address[1] & 0b0011_1111) << 8;
322
- const publicKey = address.slice(prefixLength, address.length - CHECKSUM_LENGTH);
323
- return [publicKey, prefix];
324
- };
325
- const encodeAddressSs58 = (publicKey, prefix = 42) => {
326
- if (typeof publicKey === "string") [publicKey] = decodeSs58Address(publicKey);
327
- return ss58Encode(accountId(publicKey), prefix);
328
- };
329
- function isSs58Address(address) {
330
- try {
331
- decodeSs58Address(address);
332
- return true;
333
- } catch (error) {
334
- return false;
335
- }
336
- }
337
-
338
- const CACHE$1 = new Map();
339
- const detectAddressEncodingInner = address => {
340
- if (isEthereumAddress(address)) return "ethereum";
341
- if (isSs58Address(address)) return "ss58";
342
- if (isSolanaAddress(address)) return "base58solana";
343
- if (isBech32mAddress(address)) return "bech32m";
344
- if (isBech32Address(address)) return "bech32";
345
- if (isBase58CheckAddress(address)) return "base58check";
346
- throw new Error(`Unknown address encoding`);
347
- };
348
- const detectAddressEncoding = address => {
349
- if (!CACHE$1.has(address)) CACHE$1.set(address, detectAddressEncodingInner(address));
350
- return CACHE$1.get(address);
351
- };
352
-
353
- const addressFromPublicKey = (publicKey, encoding, options) => {
354
- switch (encoding) {
355
- case "ss58":
356
- return encodeAddressSs58(publicKey, options?.ss58Prefix);
357
- case "ethereum":
358
- return encodeAddressEthereum(publicKey);
359
- case "base58solana":
360
- return encodeAddressSolana(publicKey);
361
- case "bech32m":
362
- case "bech32":
363
- case "base58check":
364
- throw new Error("addressFromPublicKey is not implemented for Bitcoin");
365
- }
366
- };
367
-
368
- const CACHE = new Map();
369
-
370
- // Normalize an address in a way that it can be compared to other addresses that have also been normalized
371
- const normalizeAddress = address => {
372
- try {
373
- if (!CACHE.has(address)) CACHE.set(address, normalizeAnyAddress(address));
374
- return CACHE.get(address);
375
- } catch (cause) {
376
- throw new Error(`Unable to normalize address: ${address}`, {
377
- cause
378
- });
379
- }
380
- };
381
- const normalizeAnyAddress = address => {
382
- switch (detectAddressEncoding(address)) {
383
- case "ethereum":
384
- return checksumEthereumAddress(address);
385
- case "bech32m":
386
- case "bech32":
387
- case "base58check":
388
- case "base58solana":
389
- return address;
390
- case "ss58":
391
- {
392
- const [pk] = decodeSs58Address(address);
393
- return encodeAddressSs58(pk, 42);
394
- }
395
- }
396
- };
397
-
398
- const isAddressEqual = (address1, address2) => {
399
- try {
400
- return normalizeAddress(address1) === normalizeAddress(address2);
401
- } catch (err) {
402
- // if normalization fails, assume the addresses are not equal
403
- return false;
404
- }
405
- };
406
-
407
- const encodeAnyAddress = (address, options) => {
408
- // this leverages cache
409
- const encoding = detectAddressEncoding(address);
410
-
411
- // this does NOT leverage cache
412
- if (encoding === "ss58" && options?.ss58Format !== undefined) {
413
- const [publicKey] = decodeSs58Address(address);
414
- return encodeAddressSs58(publicKey, options?.ss58Format ?? 42);
415
- }
416
-
417
- // this leverages cache
418
- return normalizeAddress(address);
419
- };
420
-
421
- const isAddressValid = address => {
422
- try {
423
- detectAddressEncoding(address);
424
- return true;
425
- } catch {
426
- return false;
427
- }
428
- };
429
-
430
- // Inspired from MIT licensed @polkadot-labs/hdkd helpers
431
- // https://github.com/polkadot-labs/hdkd/blob/3ef6e02827212d934b59a4e566d8aa61d3ba7b27/packages/hdkd-helpers/src/parseDerivations.ts#L1
432
-
433
- const DERIVATION_RE = /(\/{1,2})(\w+)/g;
434
- const parseSubstrateDerivations = derivationsStr => {
435
- const derivations = [];
436
- if (derivations) for (const [_, type, code] of derivationsStr.matchAll(DERIVATION_RE)) {
437
- derivations.push([type === "//" ? "hard" : "soft", code]);
438
- }
439
- return derivations;
440
- };
441
- const createChainCode = code => {
442
- const chainCode = new Uint8Array(32);
443
- chainCode.set(Number.isNaN(+code) ? scaleTs.str.enc(code) : scaleTs.u32.enc(+code));
444
- return chainCode;
445
- };
446
- const derivationCodec = /* @__PURE__ */scaleTs.Tuple(scaleTs.str, scaleTs.Bytes(32), scaleTs.Bytes(32));
447
- const createSubstrateDeriveFn = prefix => (seed, chainCode) => blake2b256(derivationCodec.enc([prefix, seed, chainCode]));
448
- const deriveSubstrateSecretKey = (seed, derivationPath, prefix) => {
449
- const derivations = parseSubstrateDerivations(derivationPath);
450
- const derive = createSubstrateDeriveFn(prefix);
451
- return derivations.reduce((seed, [type, chainCode]) => {
452
- const code = createChainCode(chainCode);
453
- if (type === "soft") throw new Error("Soft derivations are not supported");
454
- return derive(seed, code);
455
- }, seed);
456
- };
457
-
458
- const deriveEcdsa = (seed, derivationPath) => {
459
- const secretKey = deriveSubstrateSecretKey(seed, derivationPath, "Secp256k1HDKD");
460
- const publicKey = getPublicKeyEcdsa(secretKey);
461
- return {
462
- type: "ecdsa",
463
- secretKey,
464
- publicKey,
465
- address: addressFromPublicKey(publicKey, "ss58")
466
- };
467
- };
468
- const getPublicKeyEcdsa = secretKey => {
469
- return secp256k1.secp256k1.getPublicKey(secretKey);
470
- };
471
-
472
- const deriveEd25519 = (seed, derivationPath) => {
473
- const secretKey = deriveSubstrateSecretKey(seed, derivationPath, "Ed25519HDKD");
474
- const publicKey = getPublicKeyEd25519(secretKey);
475
- return {
476
- type: "ed25519",
477
- secretKey,
478
- publicKey,
479
- address: addressFromPublicKey(publicKey, "ss58")
480
- };
481
- };
482
- const getPublicKeyEd25519 = secretKey => {
483
- // When importing ed25519 polkadot-js accounts via json, which we do inside of `packages/extension-core/src/domains/keyring/getSecretKeyFromPjsJson.ts`,
484
- // the secretKey we produce is 64 bytes in length.
485
- //
486
- // When using the ed25519 curve to derive a publicKey for this 64 bytes privateKey, we should only take the first 32 bytes:
487
- // - https://github.com/paulmillr/noble-curves/issues/53#issuecomment-1577362759
488
- // - https://github.com/paulmillr/noble-curves/discussions/33#discussioncomment-5685971
489
- // - https://github.com/paulmillr/noble-curves/pull/54
490
- // - https://github.com/paulmillr/noble-curves/issues/88
491
- //
492
- // When you compare the ed25519 publicKey of a given account produced by this function to the publicKey produced by
493
- // polkadot-js, you will find that they are the same as eachother.
494
- if (secretKey.length === 64) {
495
- const [privateComponent, publicComponent] = [secretKey.slice(0, 32), secretKey.slice(32)];
496
- const publicKey = ed25519.ed25519.getPublicKey(privateComponent);
497
-
498
- // NOTE: We only accept a 64 byte secretKey when the first 32 bytes successfully produce a public key which equals the second 32 bytes of the secretKey.
499
- //
500
- // In this scenario, we assume the creator of the secretKey has given us an array of bytes which equals `[...privateKey, ...publicKey]`.
501
- //
502
- // However, if the second 32 bytes **don't** match the publicKey produced by the first 32 bytes, we no longer know what's going on.
503
- //
504
- // In that case we pass the 64 bytes directly through to `@noble/curves/ed25519`, which we expect will throw the error: `private key of length 32 expected, got 64`.
505
- // But if there's some 64 byte key format for ed25519 we don't know about, or one is added in the future, `@noble/curves/ed25519` can handle that for us instead of throwing.
506
- if (!isUint8ArrayEq(publicComponent, publicKey)) return ed25519.ed25519.getPublicKey(secretKey);
507
- return publicKey;
508
- }
509
- return ed25519.ed25519.getPublicKey(secretKey);
510
- };
511
-
512
- /** If a is identical to b, this function returns true, otherwise it returns false */
513
- const isUint8ArrayEq = (a, b) => a.length !== b.length || a.some((v, i) => v !== b[i]) ? false : true;
514
-
515
- const deriveEthereum = (seed, derivationPath) => {
516
- const hdkey = bip32.HDKey.fromMasterSeed(seed);
517
- const childKey = hdkey.derive(derivationPath);
518
- if (!childKey.privateKey) throw new Error("Invalid derivation path");
519
- const secretKey = new Uint8Array(childKey.privateKey);
520
- const publicKey = getPublicKeyEthereum(secretKey);
521
- return {
522
- type: "ethereum",
523
- secretKey,
524
- publicKey,
525
- address: addressFromPublicKey(publicKey, "ethereum")
526
- };
527
- };
528
- const getPublicKeyEthereum = secretKey => {
529
- const scalar = secp256k1.secp256k1.utils.normPrivateKeyToScalar(secretKey);
530
- return secp256k1.secp256k1.getPublicKey(scalar, false);
531
- };
532
-
533
- // Convert a path like "m/44'/501'/0'/0'" to an array of indexes
534
- const parseDerivationPath = path => {
535
- if (!path.startsWith("m/")) throw new Error("Path must start with 'm/'");
536
- return path.split("/").slice(1) // Remove 'm'
537
- .map(p => {
538
- if (!p.endsWith("'")) throw new Error("Only hardened derivation is supported");
539
- return parseInt(p.slice(0, -1), 10) + 0x80000000; // Convert to hardened index
540
- });
541
- };
542
- const deriveSolana = (seed, derivationPath) => {
543
- // Generate master private key using SLIP-0010 (HMAC-SHA512 with "ed25519 seed")
544
- let I = hmac.hmac(sha512.sha512, new TextEncoder().encode("ed25519 seed"), seed);
545
- let secretKey = I.slice(0, 32);
546
- let chainCode = I.slice(32);
547
-
548
- // Iterate over the derivation path and apply hardened key derivation
549
- for (const index of parseDerivationPath(derivationPath)) {
550
- // HMAC-SHA512(Key = chainCode, Data = 0x00 || privateKey || index)
551
- const data = new Uint8Array(1 + secretKey.length + 4);
552
- data.set([0x00], 0);
553
- data.set(secretKey, 1);
554
- data.set(new Uint8Array([index >> 24 & 0xff, index >> 16 & 0xff, index >> 8 & 0xff, index & 0xff]), 1 + secretKey.length);
555
- I = hmac.hmac(sha512.sha512, chainCode, data);
556
- secretKey = I.slice(0, 32);
557
- chainCode = I.slice(32);
558
- }
559
- const publicKey = getPublicKeySolana(secretKey);
560
- return {
561
- type: "solana",
562
- secretKey,
563
- publicKey,
564
- address: addressFromPublicKey(publicKey, "base58solana")
565
- };
566
- };
567
- const getPublicKeySolana = secretKey => {
568
- return ed25519.ed25519.getPublicKey(secretKey);
569
- };
570
-
571
- const deriveSr25519 = (seed, derivationPath) => {
572
- const derivations = parseSubstrateDerivations(derivationPath);
573
- const secretKey = derivations.reduce((secretKey, [type, chainCode]) => {
574
- const deriveFn = type === "hard" ? microSr25519.HDKD.secretHard : microSr25519.HDKD.secretSoft;
575
- const code = createChainCode(chainCode);
576
- return deriveFn(secretKey, code, utils.randomBytes);
577
- }, microSr25519.secretFromSeed(seed));
578
- const publicKey = getPublicKeySr25519(secretKey);
579
- return {
580
- type: "sr25519",
581
- secretKey,
582
- publicKey,
583
- address: addressFromPublicKey(publicKey, "ss58")
584
- };
585
- };
586
- const getPublicKeySr25519 = microSr25519.getPublicKey;
587
-
588
- const deriveKeypair = (seed, derivationPath, curve) => {
589
- switch (curve) {
590
- case "sr25519":
591
- return deriveSr25519(seed, derivationPath);
592
- case "ed25519":
593
- return deriveEd25519(seed, derivationPath);
594
- case "ecdsa":
595
- return deriveEcdsa(seed, derivationPath);
596
- case "bitcoin-ecdsa":
597
- case "bitcoin-ed25519":
598
- throw new Error("deriveKeypair is not implemented for Bitcoin");
599
- case "ethereum":
600
- return deriveEthereum(seed, derivationPath);
601
- case "solana":
602
- return deriveSolana(seed, derivationPath);
603
- }
604
- };
605
- const getPublicKeyFromSecret = (secretKey, curve) => {
606
- switch (curve) {
607
- case "ecdsa":
608
- return getPublicKeyEcdsa(secretKey);
609
- case "ethereum":
610
- return getPublicKeyEthereum(secretKey);
611
- case "sr25519":
612
- return getPublicKeySr25519(secretKey);
613
- case "ed25519":
614
- return getPublicKeyEd25519(secretKey);
615
- case "bitcoin-ecdsa":
616
- case "bitcoin-ed25519":
617
- throw new Error("getPublicKeyFromSecret is not implemented for Bitcoin");
618
- case "solana":
619
- return getPublicKeySolana(secretKey);
620
- }
621
- };
622
- const addressFromMnemonic = async (mnemonic, derivationPath, curve) => {
623
- const entropy = mnemonicToEntropy(mnemonic);
624
- const seed = await entropyToSeed(entropy, curve);
625
- const {
626
- address
627
- } = deriveKeypair(seed, derivationPath, curve);
628
- return address;
629
- };
630
- const removeHexPrefix = secretKey => {
631
- if (secretKey.startsWith("0x")) return secretKey.slice(2);
632
- return secretKey;
633
- };
634
- const parseSecretKey = (secretKey, platform) => {
635
- switch (platform) {
636
- case "ethereum":
637
- {
638
- const privateKey = removeHexPrefix(secretKey);
639
- return base.hex.decode(privateKey);
640
- }
641
- case "solana":
642
- {
643
- const bytes = secretKey.startsWith("[") ?
644
- // JSON bytes array (ex: solflare)
645
- Uint8Array.from(JSON.parse(secretKey)) :
646
- // base58 encoded string (ex: phantom)
647
- base.base58.decode(secretKey);
648
- if (bytes.length === 64) {
649
- const privateKey = bytes.slice(0, 32);
650
- const publicKey = bytes.slice(32, 64);
651
- const computedPublicKey = getPublicKeySolana(privateKey);
652
- if (!publicKey.every((b, i) => b === computedPublicKey[i])) throw new Error("Invalid Solana secret key: public key does not match");
653
- return privateKey;
654
- } else if (bytes.length === 32) return bytes;
655
- throw new Error("Invalid Solana secret key length");
656
- }
657
- default:
658
- throw new Error("Not implemented");
659
- }
660
- };
661
-
662
- // @dev: didn't find a reliable source of information on which characters are valid => assume it s valid if a keypair can be generated from it
663
- const isValidDerivationPath = async (derivationPath, curve) => {
664
- try {
665
- deriveKeypair(await getDevSeed(curve), derivationPath, curve);
666
- return true;
667
- } catch (err) {
668
- return false;
669
- }
670
- };
671
-
672
- const getAccountPlatformFromCurve = curve => {
673
- switch (curve) {
674
- case "sr25519":
675
- case "ed25519":
676
- case "ecdsa":
677
- return "polkadot";
678
- case "ethereum":
679
- return "ethereum";
680
- case "bitcoin-ecdsa":
681
- case "bitcoin-ed25519":
682
- return "bitcoin";
683
- case "solana":
684
- return "solana";
685
- }
686
- };
687
- const getAccountPlatformFromEncoding = encoding => {
688
- switch (encoding) {
689
- case "ss58":
690
- return "polkadot";
691
- case "ethereum":
692
- return "ethereum";
693
- case "bech32m":
694
- case "bech32":
695
- case "base58check":
696
- return "bitcoin";
697
- case "base58solana":
698
- return "solana";
699
- }
700
- };
701
- const getAccountPlatformFromAddress = address => {
702
- const encoding = detectAddressEncoding(address);
703
- return getAccountPlatformFromEncoding(encoding);
704
- };
705
-
706
- const encryptKemAead = async (publicKey, plaintext) => {
707
- const kem = new mlkem.MlKem768();
708
- const [kemCt, sharedSecret] = await kem.encap(publicKey);
709
- if (sharedSecret.length !== 32) {
710
- throw new Error(`Expected 32-byte shared secret, got ${sharedSecret.length}`);
711
- }
712
- const nonce = crypto.getRandomValues(new Uint8Array(24));
713
- const aead = chacha_js.xchacha20poly1305(sharedSecret, nonce);
714
- const aeadCt = aead.encrypt(plaintext);
715
- const kemLen = new Uint8Array(2);
716
- new DataView(kemLen.buffer).setUint16(0, kemCt.length, true);
717
- return utils_js.concatBytes(kemLen, kemCt, nonce, aeadCt);
718
- };
719
-
720
- Object.defineProperty(exports, "base58", {
721
- enumerable: true,
722
- get: function () { return base.base58; }
723
- });
724
- Object.defineProperty(exports, "base64", {
725
- enumerable: true,
726
- get: function () { return base.base64; }
727
- });
728
- Object.defineProperty(exports, "hex", {
729
- enumerable: true,
730
- get: function () { return base.hex; }
731
- });
732
- Object.defineProperty(exports, "utf8", {
733
- enumerable: true,
734
- get: function () { return base.utf8; }
735
- });
736
- Object.defineProperty(exports, "ed25519", {
737
- enumerable: true,
738
- get: function () { return ed25519.ed25519; }
739
- });
740
- exports.DEV_MNEMONIC_ETHEREUM = DEV_MNEMONIC_ETHEREUM;
741
- exports.DEV_MNEMONIC_POLKADOT = DEV_MNEMONIC_POLKADOT;
742
- exports.addressEncodingFromCurve = addressEncodingFromCurve;
743
- exports.addressFromMnemonic = addressFromMnemonic;
744
- exports.addressFromPublicKey = addressFromPublicKey;
745
- exports.blake2b256 = blake2b256;
746
- exports.blake2b512 = blake2b512;
747
- exports.blake3 = blake3;
748
- exports.checksumEthereumAddress = checksumEthereumAddress;
749
- exports.decodeSs58Address = decodeSs58Address;
750
- exports.deriveKeypair = deriveKeypair;
751
- exports.detectAddressEncoding = detectAddressEncoding;
752
- exports.encodeAddressEthereum = encodeAddressEthereum;
753
- exports.encodeAddressSolana = encodeAddressSolana;
754
- exports.encodeAddressSs58 = encodeAddressSs58;
755
- exports.encodeAnyAddress = encodeAnyAddress;
756
- exports.encryptKemAead = encryptKemAead;
757
- exports.entropyToMnemonic = entropyToMnemonic;
758
- exports.entropyToSeed = entropyToSeed;
759
- exports.fromBase58Check = fromBase58Check;
760
- exports.fromBech32 = fromBech32;
761
- exports.fromBech32m = fromBech32m;
762
- exports.generateMnemonic = generateMnemonic;
763
- exports.getAccountPlatformFromAddress = getAccountPlatformFromAddress;
764
- exports.getAccountPlatformFromCurve = getAccountPlatformFromCurve;
765
- exports.getAccountPlatformFromEncoding = getAccountPlatformFromEncoding;
766
- exports.getDevSeed = getDevSeed;
767
- exports.getPublicKeyFromSecret = getPublicKeyFromSecret;
768
- exports.getSafeHash = getSafeHash;
769
- exports.isAddressEqual = isAddressEqual;
770
- exports.isAddressValid = isAddressValid;
771
- exports.isBase58CheckAddress = isBase58CheckAddress;
772
- exports.isBech32Address = isBech32Address;
773
- exports.isBech32mAddress = isBech32mAddress;
774
- exports.isBitcoinAddress = isBitcoinAddress;
775
- exports.isEthereumAddress = isEthereumAddress;
776
- exports.isSolanaAddress = isSolanaAddress;
777
- exports.isSs58Address = isSs58Address;
778
- exports.isValidDerivationPath = isValidDerivationPath;
779
- exports.isValidMnemonic = isValidMnemonic;
780
- exports.mnemonicToEntropy = mnemonicToEntropy;
781
- exports.normalizeAddress = normalizeAddress;
782
- exports.parseSecretKey = parseSecretKey;
783
- exports.pbkdf2 = pbkdf2;
784
- exports.removeHexPrefix = removeHexPrefix;