@twin.org/crypto 0.0.1-next.9 → 0.0.2-next.10

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 (38) hide show
  1. package/dist/cjs/index.cjs +156 -52
  2. package/dist/esm/index.mjs +128 -25
  3. package/dist/types/address/bip44.d.ts +15 -0
  4. package/dist/types/curves/ed25519.d.ts +12 -0
  5. package/dist/types/helpers/pemHelper.d.ts +19 -0
  6. package/dist/types/index.d.ts +1 -0
  7. package/dist/types/keys/bip39.d.ts +8 -0
  8. package/docs/changelog.md +526 -1
  9. package/docs/reference/classes/Bech32.md +15 -7
  10. package/docs/reference/classes/Bip32Path.md +17 -9
  11. package/docs/reference/classes/Bip39.md +62 -12
  12. package/docs/reference/classes/Bip44.md +121 -21
  13. package/docs/reference/classes/Blake2b.md +35 -17
  14. package/docs/reference/classes/Blake3.md +28 -14
  15. package/docs/reference/classes/ChaCha20Poly1305.md +18 -8
  16. package/docs/reference/classes/Ed25519.md +65 -9
  17. package/docs/reference/classes/HmacSha1.md +17 -9
  18. package/docs/reference/classes/HmacSha256.md +26 -12
  19. package/docs/reference/classes/HmacSha512.md +38 -16
  20. package/docs/reference/classes/Hotp.md +9 -5
  21. package/docs/reference/classes/PasswordGenerator.md +6 -4
  22. package/docs/reference/classes/PasswordValidator.md +23 -11
  23. package/docs/reference/classes/Pbkdf2.md +27 -11
  24. package/docs/reference/classes/PemHelper.md +69 -0
  25. package/docs/reference/classes/Secp256k1.md +21 -9
  26. package/docs/reference/classes/Sha1.md +11 -7
  27. package/docs/reference/classes/Sha256.md +17 -9
  28. package/docs/reference/classes/Sha3.md +23 -11
  29. package/docs/reference/classes/Sha512.md +23 -11
  30. package/docs/reference/classes/Slip0010.md +27 -11
  31. package/docs/reference/classes/Totp.md +42 -16
  32. package/docs/reference/classes/X25519.md +9 -5
  33. package/docs/reference/classes/Zip215.md +12 -6
  34. package/docs/reference/index.md +1 -0
  35. package/docs/reference/type-aliases/KeyType.md +1 -1
  36. package/docs/reference/variables/KeyType.md +1 -1
  37. package/locales/en.json +4 -0
  38. package/package.json +14 -13
@@ -1,20 +1,19 @@
1
1
  import { bech32 } from '@scure/base';
2
- import { Guards, BaseError, GeneralError, Is, Converter, GuardError, Base32, RandomHelper, Validation } from '@twin.org/core';
3
- import { ed25519, edwardsToMontgomeryPriv, edwardsToMontgomeryPub } from '@noble/curves/ed25519';
4
- import { secp256k1 } from '@noble/curves/secp256k1';
5
- import { blake2b } from '@noble/hashes/blake2b';
2
+ import { Guards, BaseError, GeneralError, Is, Uint8ArrayHelper, Converter, GuardError, Base32, RandomHelper, Validation } from '@twin.org/core';
3
+ import { ed25519 } from '@noble/curves/ed25519.js';
4
+ import { secp256k1 } from '@noble/curves/secp256k1.js';
5
+ import { blake2b } from '@noble/hashes/blake2.js';
6
6
  import { HDKey as HDKey$1 } from '@scure/bip32';
7
7
  import { HDKey } from 'micro-key-producer/slip10.js';
8
- import { chacha20poly1305 } from '@noble/ciphers/chacha';
9
- import { blake3 } from '@noble/hashes/blake3';
10
- import { hmac } from '@noble/hashes/hmac';
11
- import { sha1 } from '@noble/hashes/sha1';
12
- import { sha256, sha224 } from '@noble/hashes/sha256';
13
- import { sha512_224, sha512_256, sha384, sha512 } from '@noble/hashes/sha512';
14
- import { pbkdf2 } from '@noble/hashes/pbkdf2';
15
- import { sha3_224, sha3_256, sha3_384, sha3_512 } from '@noble/hashes/sha3';
8
+ import { chacha20poly1305 } from '@noble/ciphers/chacha.js';
9
+ import { blake3 } from '@noble/hashes/blake3.js';
10
+ import { hmac } from '@noble/hashes/hmac.js';
11
+ import { sha1 } from '@noble/hashes/legacy.js';
12
+ import { sha256, sha224, sha512_224, sha512_256, sha384, sha512 } from '@noble/hashes/sha2.js';
13
+ import { pbkdf2 } from '@noble/hashes/pbkdf2.js';
14
+ import { sha3_224, sha3_256, sha3_384, sha3_512 } from '@noble/hashes/sha3.js';
16
15
  import * as bip39 from '@scure/bip39';
17
- import { wordlist } from '@scure/bip39/wordlists/english';
16
+ import { wordlist } from '@scure/bip39/wordlists/english.js';
18
17
  import * as otp from 'micro-key-producer/otp.js';
19
18
 
20
19
  // Copyright 2024 IOTA Stiftung.
@@ -166,6 +165,39 @@ class Ed25519 {
166
165
  return false;
167
166
  }
168
167
  }
168
+ /**
169
+ * Convert a private key in PKCS8 format.
170
+ * @param privateKey The private key to convert.
171
+ * @returns The private key in PKCS8 format.
172
+ */
173
+ static async privateKeyToPkcs8(privateKey) {
174
+ Guards.uint8Array(Ed25519._CLASS_NAME, "privateKey", privateKey);
175
+ if (privateKey.length !== Ed25519.PRIVATE_KEY_SIZE) {
176
+ throw new GeneralError(Ed25519._CLASS_NAME, "privateKeyLength", {
177
+ requiredSize: Ed25519.PRIVATE_KEY_SIZE,
178
+ actualSize: privateKey.length
179
+ });
180
+ }
181
+ // crypto.subtle.importKey does not support Ed25519 keys in raw format.
182
+ // We need to convert the key to PKCS8 format before importing.
183
+ // The PKCS8 format is the raw key prefixed with the ASN.1 sequence for an Ed25519 private key.
184
+ // The ASN.1 sequence is 48 46 02 01 00 30 05 06 03 2b 65 70 04 20 04 20 (0x302e020100300506032b657004220420)
185
+ const pkcs8Prefix = new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]);
186
+ const fullKey = Uint8ArrayHelper.concat([pkcs8Prefix, privateKey]);
187
+ return crypto.subtle.importKey("pkcs8", new Uint8Array(fullKey), "Ed25519", true, ["sign"]);
188
+ }
189
+ /**
190
+ * Convert a crypto key to raw private key.
191
+ * @param cryptoKey The crypto key to convert.
192
+ * @returns The raw private key.
193
+ */
194
+ static async pkcs8ToPrivateKey(cryptoKey) {
195
+ Guards.defined(Ed25519._CLASS_NAME, "cryptoKey", cryptoKey);
196
+ // crypto.subtle.exportKey does not support Ed25519 keys in raw format.
197
+ // so we export as PKCS8 and remove the ASN.1 sequence prefix.
198
+ const pkcs8Bytes = await crypto.subtle.exportKey("pkcs8", cryptoKey);
199
+ return new Uint8Array(pkcs8Bytes.slice(16));
200
+ }
169
201
  }
170
202
 
171
203
  // Copyright 2024 IOTA Stiftung.
@@ -216,11 +248,11 @@ class Secp256k1 {
216
248
  if (privateKey.length !== Secp256k1.PRIVATE_KEY_SIZE) {
217
249
  throw new GeneralError(Secp256k1._CLASS_NAME, "privateKeyLength", {
218
250
  requiredSize: Secp256k1.PRIVATE_KEY_SIZE,
219
- actualSize: privateKey ? privateKey.length : 0
251
+ actualSize: privateKey.length
220
252
  });
221
253
  }
222
- const res = secp256k1.sign(block, privateKey);
223
- return res.toCompactRawBytes();
254
+ const res = secp256k1.sign(block, privateKey, { prehash: false });
255
+ return res;
224
256
  }
225
257
  /**
226
258
  * Verify reports whether sig is a valid signature of block by publicKey.
@@ -241,7 +273,7 @@ class Secp256k1 {
241
273
  });
242
274
  }
243
275
  try {
244
- return secp256k1.verify(signature, block, publicKey);
276
+ return secp256k1.verify(signature, block, publicKey, { prehash: false });
245
277
  }
246
278
  catch {
247
279
  return false;
@@ -429,7 +461,6 @@ const KeyType = {
429
461
 
430
462
  // Copyright 2024 IOTA Stiftung.
431
463
  // SPDX-License-Identifier: Apache-2.0.
432
- /* eslint-disable no-bitwise */
433
464
  /**
434
465
  * Class to help with slip0010 key derivation
435
466
  * https://github.com/satoshilabs/slips/blob/master/slip-0010.md.
@@ -570,6 +601,24 @@ class Bip44 {
570
601
  static basePath(coinType) {
571
602
  return `m/44'/${coinType}'`;
572
603
  }
604
+ /**
605
+ * Generate an address from the seed and parts.
606
+ * @param seed The account seed.
607
+ * @param keyType The key type.
608
+ * @param coinType The coin type.
609
+ * @param accountIndex The account index.
610
+ * @param isInternal Is this an internal address.
611
+ * @param addressIndex The address index.
612
+ * @returns The generated path and the associated keypair.
613
+ */
614
+ static address(seed, keyType, coinType, accountIndex, isInternal, addressIndex) {
615
+ const keyPair = Bip44.keyPair(seed, keyType, coinType, accountIndex, isInternal, addressIndex);
616
+ const addressData = Blake2b.sum256(keyPair.publicKey);
617
+ return {
618
+ address: Converter.bytesToHex(addressData, true),
619
+ ...keyPair
620
+ };
621
+ }
573
622
  /**
574
623
  * Generate a bech32 address from the seed and parts.
575
624
  * @param seed The account seed.
@@ -643,9 +692,6 @@ class ChaCha20Poly1305 {
643
692
 
644
693
  // Copyright 2024 IOTA Stiftung.
645
694
  // SPDX-License-Identifier: Apache-2.0.
646
- /**
647
- * This is a TypeScript port of https://github.com/katzenpost/core/blob/master/crypto/extra25519/extra25519.go.
648
- */
649
695
  /**
650
696
  * Implementation of X25519.
651
697
  */
@@ -662,7 +708,7 @@ class X25519 {
662
708
  */
663
709
  static convertPrivateKeyToX25519(ed25519PrivateKey) {
664
710
  Guards.uint8Array(X25519._CLASS_NAME, "ed25519PrivateKey", ed25519PrivateKey);
665
- return edwardsToMontgomeryPriv(ed25519PrivateKey);
711
+ return ed25519.utils.toMontgomerySecret(ed25519PrivateKey.slice(0, Ed25519.PRIVATE_KEY_SIZE));
666
712
  }
667
713
  /**
668
714
  * Convert Ed25519 public key to X25519 public key.
@@ -672,7 +718,7 @@ class X25519 {
672
718
  */
673
719
  static convertPublicKeyToX25519(ed25519PublicKey) {
674
720
  Guards.uint8Array(X25519._CLASS_NAME, "ed25519PublicKey", ed25519PublicKey);
675
- return edwardsToMontgomeryPub(ed25519PublicKey);
721
+ return ed25519.utils.toMontgomery(ed25519PublicKey);
676
722
  }
677
723
  }
678
724
 
@@ -1453,6 +1499,48 @@ class Sha512 {
1453
1499
  }
1454
1500
  }
1455
1501
 
1502
+ // Copyright 2024 IOTA Stiftung.
1503
+ // SPDX-License-Identifier: Apache-2.0.
1504
+ /**
1505
+ * Helper class for working with PEM (Privacy-Enhanced Mail) formatted data.
1506
+ */
1507
+ class PemHelper {
1508
+ /**
1509
+ * Runtime name for the class.
1510
+ * @internal
1511
+ */
1512
+ static _CLASS_NAME = "PemHelper";
1513
+ /**
1514
+ * Strip the PEM content of its headers, footers, and newlines.
1515
+ * @param pemContent The PEM content to strip.
1516
+ * @returns The stripped PEM content in bas64 format.
1517
+ */
1518
+ static stripPemMarkers(pemContent) {
1519
+ Guards.string(PemHelper._CLASS_NAME, "pemContent", pemContent);
1520
+ return pemContent
1521
+ .replace(/-----BEGIN.*-----/, "")
1522
+ .replace(/-----END.*-----/, "")
1523
+ .replace(/\n/g, "")
1524
+ .trim();
1525
+ }
1526
+ /**
1527
+ * Format the PEM content to have a specific line length.
1528
+ * @param marker The marker for the PEM content, e.g. RSA PRIVATE KEY
1529
+ * @param base64Content The base64 content to format.
1530
+ * @param lineLength The length of each line in the PEM content, default is 64 characters.
1531
+ * @returns The formatted PEM content.
1532
+ */
1533
+ static formatPem(marker, base64Content, lineLength = 64) {
1534
+ Guards.stringValue(PemHelper._CLASS_NAME, "marker", marker);
1535
+ Guards.stringBase64(PemHelper._CLASS_NAME, "base64Content", base64Content);
1536
+ const lines = [];
1537
+ for (let i = 0; i < base64Content.length; i += lineLength) {
1538
+ lines.push(base64Content.slice(i, i + lineLength));
1539
+ }
1540
+ return [`-----BEGIN ${marker}-----`, ...lines, `-----END ${marker}-----`].join("\n");
1541
+ }
1542
+ }
1543
+
1456
1544
  // Copyright 2024 IOTA Stiftung.
1457
1545
  // SPDX-License-Identifier: Apache-2.0.
1458
1546
  /**
@@ -1516,11 +1604,26 @@ class Bip39 {
1516
1604
  Guards.arrayValue(Bip39._CLASS_NAME, "words", words);
1517
1605
  return bip39.mnemonicToEntropy(mnemonic, words);
1518
1606
  }
1607
+ /**
1608
+ * Validate the mnemonic.
1609
+ * @param mnemonic The mnemonic to validate.
1610
+ * @param wordCount The expected number of words in the mnemonic, defaults to 24.
1611
+ * @param words The wordlist to use, defaults to the English wordlist.
1612
+ * @returns True if the mnemonic is valid.
1613
+ */
1614
+ static validateMnemonic(mnemonic, wordCount = 24, words = wordlist) {
1615
+ Guards.string(Bip39._CLASS_NAME, "mnemonic", mnemonic);
1616
+ Guards.integer(Bip39._CLASS_NAME, "wordCount", wordCount);
1617
+ const mnemonicSplit = mnemonic.split(/\s+/);
1618
+ if (mnemonicSplit.length !== wordCount) {
1619
+ return false;
1620
+ }
1621
+ return bip39.validateMnemonic(mnemonic, words);
1622
+ }
1519
1623
  }
1520
1624
 
1521
1625
  // Copyright 2024 IOTA Stiftung.
1522
1626
  // SPDX-License-Identifier: Apache-2.0.
1523
- /* eslint-disable no-bitwise */
1524
1627
  /**
1525
1628
  * Perform HOTP.
1526
1629
  * Implementation of https://datatracker.ietf.org/doc/html/rfc4226 .
@@ -1723,4 +1826,4 @@ class PasswordValidator {
1723
1826
  }
1724
1827
  }
1725
1828
 
1726
- export { Bech32, Bip32Path, Bip39, Bip44, Blake2b, Blake3, ChaCha20Poly1305, Ed25519, HmacSha1, HmacSha256, HmacSha512, Hotp, KeyType, PasswordGenerator, PasswordValidator, Pbkdf2, Secp256k1, Sha1, Sha256, Sha3, Sha512, Slip0010, Totp, X25519, Zip215 };
1829
+ export { Bech32, Bip32Path, Bip39, Bip44, Blake2b, Blake3, ChaCha20Poly1305, Ed25519, HmacSha1, HmacSha256, HmacSha512, Hotp, KeyType, PasswordGenerator, PasswordValidator, Pbkdf2, PemHelper, Secp256k1, Sha1, Sha256, Sha3, Sha512, Slip0010, Totp, X25519, Zip215 };
@@ -34,6 +34,21 @@ export declare class Bip44 {
34
34
  * @returns The bip44 address base path.
35
35
  */
36
36
  static basePath(coinType: number): string;
37
+ /**
38
+ * Generate an address from the seed and parts.
39
+ * @param seed The account seed.
40
+ * @param keyType The key type.
41
+ * @param coinType The coin type.
42
+ * @param accountIndex The account index.
43
+ * @param isInternal Is this an internal address.
44
+ * @param addressIndex The address index.
45
+ * @returns The generated path and the associated keypair.
46
+ */
47
+ static address(seed: Uint8Array, keyType: KeyType, coinType: number, accountIndex: number, isInternal: boolean, addressIndex: number): {
48
+ address: string;
49
+ privateKey: Uint8Array;
50
+ publicKey: Uint8Array;
51
+ };
37
52
  /**
38
53
  * Generate a bech32 address from the seed and parts.
39
54
  * @param seed The account seed.
@@ -34,4 +34,16 @@ export declare class Ed25519 {
34
34
  * @throws Error if the public key is not the correct length.
35
35
  */
36
36
  static verify(publicKey: Uint8Array, block: Uint8Array, signature: Uint8Array): boolean;
37
+ /**
38
+ * Convert a private key in PKCS8 format.
39
+ * @param privateKey The private key to convert.
40
+ * @returns The private key in PKCS8 format.
41
+ */
42
+ static privateKeyToPkcs8(privateKey: Uint8Array): Promise<CryptoKey>;
43
+ /**
44
+ * Convert a crypto key to raw private key.
45
+ * @param cryptoKey The crypto key to convert.
46
+ * @returns The raw private key.
47
+ */
48
+ static pkcs8ToPrivateKey(cryptoKey: CryptoKey): Promise<Uint8Array>;
37
49
  }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Helper class for working with PEM (Privacy-Enhanced Mail) formatted data.
3
+ */
4
+ export declare class PemHelper {
5
+ /**
6
+ * Strip the PEM content of its headers, footers, and newlines.
7
+ * @param pemContent The PEM content to strip.
8
+ * @returns The stripped PEM content in bas64 format.
9
+ */
10
+ static stripPemMarkers(pemContent: string): string;
11
+ /**
12
+ * Format the PEM content to have a specific line length.
13
+ * @param marker The marker for the PEM content, e.g. RSA PRIVATE KEY
14
+ * @param base64Content The base64 content to format.
15
+ * @param lineLength The length of each line in the PEM content, default is 64 characters.
16
+ * @returns The formatted PEM content.
17
+ */
18
+ static formatPem(marker: string, base64Content: string, lineLength?: number): string;
19
+ }
@@ -15,6 +15,7 @@ export * from "./hashes/sha1";
15
15
  export * from "./hashes/sha256";
16
16
  export * from "./hashes/sha3";
17
17
  export * from "./hashes/sha512";
18
+ export * from "./helpers/pemHelper";
18
19
  export * from "./keys/bip32Path";
19
20
  export * from "./keys/bip39";
20
21
  export * from "./keys/slip0010";
@@ -33,4 +33,12 @@ export declare class Bip39 {
33
33
  * @throws Error if the number of words is not a multiple of 3.
34
34
  */
35
35
  static mnemonicToEntropy(mnemonic: string, words?: string[]): Uint8Array;
36
+ /**
37
+ * Validate the mnemonic.
38
+ * @param mnemonic The mnemonic to validate.
39
+ * @param wordCount The expected number of words in the mnemonic, defaults to 24.
40
+ * @param words The wordlist to use, defaults to the English wordlist.
41
+ * @returns True if the mnemonic is valid.
42
+ */
43
+ static validateMnemonic(mnemonic: string, wordCount?: number, words?: string[]): boolean;
36
44
  }