@pezkuwi/util-crypto 14.0.1

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 (234) hide show
  1. package/README.md +17 -0
  2. package/package.json +45 -0
  3. package/src/address/addressToEvm.spec.ts +16 -0
  4. package/src/address/addressToEvm.ts +12 -0
  5. package/src/address/check.spec.ts +44 -0
  6. package/src/address/check.ts +34 -0
  7. package/src/address/checksum.spec.ts +45 -0
  8. package/src/address/checksum.ts +25 -0
  9. package/src/address/decode.spec.ts +138 -0
  10. package/src/address/decode.ts +41 -0
  11. package/src/address/defaults.ts +12 -0
  12. package/src/address/derive.spec.ts +26 -0
  13. package/src/address/derive.ts +36 -0
  14. package/src/address/encode.spec.ts +177 -0
  15. package/src/address/encode.ts +43 -0
  16. package/src/address/encodeDerived.spec.ts +14 -0
  17. package/src/address/encodeDerived.ts +19 -0
  18. package/src/address/encodeMulti.spec.ts +18 -0
  19. package/src/address/encodeMulti.ts +18 -0
  20. package/src/address/eq.spec.ts +45 -0
  21. package/src/address/eq.ts +24 -0
  22. package/src/address/evmToAddress.spec.ts +20 -0
  23. package/src/address/evmToAddress.ts +24 -0
  24. package/src/address/index.ts +21 -0
  25. package/src/address/is.spec.ts +113 -0
  26. package/src/address/is.ts +14 -0
  27. package/src/address/keyDerived.spec.ts +24 -0
  28. package/src/address/keyDerived.ts +22 -0
  29. package/src/address/keyMulti.spec.ts +20 -0
  30. package/src/address/keyMulti.ts +23 -0
  31. package/src/address/setSS58Format.spec.ts +21 -0
  32. package/src/address/setSS58Format.ts +20 -0
  33. package/src/address/sort.spec.ts +22 -0
  34. package/src/address/sort.ts +17 -0
  35. package/src/address/sshash.ts +12 -0
  36. package/src/address/types.ts +6 -0
  37. package/src/address/util.ts +8 -0
  38. package/src/address/validate.spec.ts +113 -0
  39. package/src/address/validate.ts +10 -0
  40. package/src/base32/bs32.ts +53 -0
  41. package/src/base32/decode.spec.ts +34 -0
  42. package/src/base32/encode.spec.ts +30 -0
  43. package/src/base32/helpers.ts +93 -0
  44. package/src/base32/index.ts +8 -0
  45. package/src/base32/is.spec.ts +32 -0
  46. package/src/base32/validate.spec.ts +44 -0
  47. package/src/base58/bs58.ts +43 -0
  48. package/src/base58/decode.spec.ts +31 -0
  49. package/src/base58/encode.spec.ts +26 -0
  50. package/src/base58/index.ts +8 -0
  51. package/src/base58/validate.spec.ts +20 -0
  52. package/src/base64/bs64.ts +43 -0
  53. package/src/base64/decode.spec.ts +42 -0
  54. package/src/base64/encode.spec.ts +14 -0
  55. package/src/base64/index.ts +10 -0
  56. package/src/base64/pad.spec.ts +14 -0
  57. package/src/base64/pad.ts +10 -0
  58. package/src/base64/trim.spec.ts +14 -0
  59. package/src/base64/trim.ts +14 -0
  60. package/src/base64/validate.spec.ts +32 -0
  61. package/src/blake2/asHex.spec.ts +57 -0
  62. package/src/blake2/asU8a.spec.ts +74 -0
  63. package/src/blake2/asU8a.ts +40 -0
  64. package/src/blake2/index.ts +8 -0
  65. package/src/bn.ts +15 -0
  66. package/src/bundle.ts +33 -0
  67. package/src/bundleInit.ts +11 -0
  68. package/src/crypto.spec.ts +18 -0
  69. package/src/crypto.ts +18 -0
  70. package/src/ed25519/deriveHard.ts +18 -0
  71. package/src/ed25519/index.ts +13 -0
  72. package/src/ed25519/pair/fromRandom.spec.ts +28 -0
  73. package/src/ed25519/pair/fromRandom.ts +25 -0
  74. package/src/ed25519/pair/fromSecret.spec.ts +33 -0
  75. package/src/ed25519/pair/fromSecret.ts +29 -0
  76. package/src/ed25519/pair/fromSeed.spec.ts +42 -0
  77. package/src/ed25519/pair/fromSeed.ts +41 -0
  78. package/src/ed25519/pair/fromString.spec.ts +17 -0
  79. package/src/ed25519/pair/fromString.ts +31 -0
  80. package/src/ed25519/sign.spec.ts +40 -0
  81. package/src/ed25519/sign.ts +38 -0
  82. package/src/ed25519/verify.spec.ts +84 -0
  83. package/src/ed25519/verify.ts +41 -0
  84. package/src/ethereum/encode.spec.ts +59 -0
  85. package/src/ethereum/encode.ts +39 -0
  86. package/src/ethereum/index.ts +6 -0
  87. package/src/ethereum/isAddress.spec.ts +34 -0
  88. package/src/ethereum/isAddress.ts +16 -0
  89. package/src/ethereum/isChecksum.ts +27 -0
  90. package/src/ethereum/isCheksum.spec.ts +30 -0
  91. package/src/hd/ethereum/index.spec.ts +54 -0
  92. package/src/hd/ethereum/index.ts +69 -0
  93. package/src/hd/index.ts +6 -0
  94. package/src/hd/ledger/derivePrivate.ts +34 -0
  95. package/src/hd/ledger/index.spec.ts +64 -0
  96. package/src/hd/ledger/index.ts +42 -0
  97. package/src/hd/ledger/master.spec.ts +19 -0
  98. package/src/hd/ledger/master.ts +26 -0
  99. package/src/hd/validatePath.spec.ts +30 -0
  100. package/src/hd/validatePath.ts +24 -0
  101. package/src/helpers.ts +38 -0
  102. package/src/hmac/index.ts +4 -0
  103. package/src/hmac/shaAsU8a.spec.ts +45 -0
  104. package/src/hmac/shaAsU8a.ts +48 -0
  105. package/src/index.ts +6 -0
  106. package/src/json/constants.ts +11 -0
  107. package/src/json/decrypt.ts +25 -0
  108. package/src/json/decryptData.ts +45 -0
  109. package/src/json/encrypt.ts +25 -0
  110. package/src/json/encryptFormat.ts +20 -0
  111. package/src/json/index.ts +7 -0
  112. package/src/json/types.ts +22 -0
  113. package/src/keccak/asHex.spec.ts +30 -0
  114. package/src/keccak/asU8a.spec.ts +56 -0
  115. package/src/keccak/asU8a.ts +45 -0
  116. package/src/keccak/index.ts +8 -0
  117. package/src/key/DeriveJunction.ts +79 -0
  118. package/src/key/extractPath.spec.ts +51 -0
  119. package/src/key/extractPath.ts +37 -0
  120. package/src/key/extractSuri.spec.ts +147 -0
  121. package/src/key/extractSuri.ts +40 -0
  122. package/src/key/fromPath.ts +28 -0
  123. package/src/key/hdkdDerive.ts +17 -0
  124. package/src/key/hdkdEcdsa.ts +8 -0
  125. package/src/key/hdkdEd25519.ts +7 -0
  126. package/src/key/hdkdSr25519.ts +14 -0
  127. package/src/key/index.ts +12 -0
  128. package/src/mnemonic/bip39.spec.ts +80 -0
  129. package/src/mnemonic/bip39.ts +127 -0
  130. package/src/mnemonic/generate.spec.ts +58 -0
  131. package/src/mnemonic/generate.ts +25 -0
  132. package/src/mnemonic/index.ts +11 -0
  133. package/src/mnemonic/toEntropy.spec.ts +36 -0
  134. package/src/mnemonic/toEntropy.ts +13 -0
  135. package/src/mnemonic/toLegacySeed.spec.ts +52 -0
  136. package/src/mnemonic/toLegacySeed.ts +39 -0
  137. package/src/mnemonic/toMiniSecret.spec.ts +67 -0
  138. package/src/mnemonic/toMiniSecret.ts +23 -0
  139. package/src/mnemonic/toMiniSecretCmp.spec.ts +64 -0
  140. package/src/mnemonic/validate.spec.ts +39 -0
  141. package/src/mnemonic/validate.ts +26 -0
  142. package/src/mnemonic/wordlists/en.ts +7 -0
  143. package/src/mnemonic/wordlists/es.ts +7 -0
  144. package/src/mnemonic/wordlists/fr.ts +7 -0
  145. package/src/mnemonic/wordlists/index.ts +11 -0
  146. package/src/mnemonic/wordlists/it.ts +7 -0
  147. package/src/mnemonic/wordlists/jp.ts +7 -0
  148. package/src/mnemonic/wordlists/ko.ts +7 -0
  149. package/src/mnemonic/wordlists/zh-s.ts +7 -0
  150. package/src/mnemonic/wordlists/zh-t.ts +7 -0
  151. package/src/mod.ts +4 -0
  152. package/src/nacl/decrypt.spec.ts +26 -0
  153. package/src/nacl/decrypt.ts +22 -0
  154. package/src/nacl/encrypt.spec.ts +20 -0
  155. package/src/nacl/encrypt.ts +31 -0
  156. package/src/nacl/index.ts +8 -0
  157. package/src/nacl/tweetnacl-secretbox-data.spec.ts +4629 -0
  158. package/src/nacl/tweetnacl-secretbox.spec.ts +161 -0
  159. package/src/nacl/tweetnacl.ts +1159 -0
  160. package/src/networks.ts +5 -0
  161. package/src/packageDetect.ts +14 -0
  162. package/src/packageInfo.ts +6 -0
  163. package/src/pbkdf2/encode.spec.ts +54 -0
  164. package/src/pbkdf2/encode.ts +29 -0
  165. package/src/pbkdf2/index.ts +4 -0
  166. package/src/random/asHex.spec.ts +38 -0
  167. package/src/random/asNumber.spec.ts +16 -0
  168. package/src/random/asNumber.ts +28 -0
  169. package/src/random/asU8a.spec.ts +36 -0
  170. package/src/random/asU8a.ts +30 -0
  171. package/src/random/index.ts +9 -0
  172. package/src/scrypt/defaults.ts +19 -0
  173. package/src/scrypt/encode.spec.ts +43 -0
  174. package/src/scrypt/encode.ts +30 -0
  175. package/src/scrypt/fromU8a.ts +44 -0
  176. package/src/scrypt/index.ts +6 -0
  177. package/src/scrypt/toU8a.ts +17 -0
  178. package/src/scrypt/types.ts +9 -0
  179. package/src/secp256k1/compress.spec.ts +47 -0
  180. package/src/secp256k1/compress.ts +21 -0
  181. package/src/secp256k1/deriveHard.ts +17 -0
  182. package/src/secp256k1/expand.spec.ts +47 -0
  183. package/src/secp256k1/expand.ts +30 -0
  184. package/src/secp256k1/hasher.spec.ts +24 -0
  185. package/src/secp256k1/hasher.ts +13 -0
  186. package/src/secp256k1/index.ts +10 -0
  187. package/src/secp256k1/pair/fromSeed.spec.ts +75 -0
  188. package/src/secp256k1/pair/fromSeed.ts +42 -0
  189. package/src/secp256k1/recover.spec.ts +35 -0
  190. package/src/secp256k1/recover.ts +36 -0
  191. package/src/secp256k1/sign.spec.ts +39 -0
  192. package/src/secp256k1/sign.ts +37 -0
  193. package/src/secp256k1/signVerify.spec.ts +94 -0
  194. package/src/secp256k1/tweakAdd.spec.ts +35 -0
  195. package/src/secp256k1/tweakAdd.ts +65 -0
  196. package/src/secp256k1/types.ts +4 -0
  197. package/src/secp256k1/verify.spec.ts +81 -0
  198. package/src/secp256k1/verify.ts +32 -0
  199. package/src/sha/asU8a.ts +30 -0
  200. package/src/sha/asU8a256.spec.ts +55 -0
  201. package/src/sha/asU8a512.spec.ts +33 -0
  202. package/src/sha/index.ts +8 -0
  203. package/src/signature/index.ts +8 -0
  204. package/src/signature/verify.spec.ts +230 -0
  205. package/src/signature/verify.ts +114 -0
  206. package/src/sr25519/agreement.spec.ts +31 -0
  207. package/src/sr25519/agreement.ts +23 -0
  208. package/src/sr25519/derive.ts +21 -0
  209. package/src/sr25519/deriveHard.ts +9 -0
  210. package/src/sr25519/derivePublic.ts +18 -0
  211. package/src/sr25519/deriveSoft.ts +9 -0
  212. package/src/sr25519/index.ts +12 -0
  213. package/src/sr25519/pair/fromSeed.spec.ts +35 -0
  214. package/src/sr25519/pair/fromSeed.ts +28 -0
  215. package/src/sr25519/pair/fromU8a.ts +23 -0
  216. package/src/sr25519/pair/testing.spec.ts +161 -0
  217. package/src/sr25519/pair/toU8a.ts +10 -0
  218. package/src/sr25519/sign.spec.ts +28 -0
  219. package/src/sr25519/sign.ts +22 -0
  220. package/src/sr25519/verify.spec.ts +42 -0
  221. package/src/sr25519/verify.ts +23 -0
  222. package/src/sr25519/vrfSign.ts +24 -0
  223. package/src/sr25519/vrfSignVerify.spec.ts +73 -0
  224. package/src/sr25519/vrfVerify.ts +25 -0
  225. package/src/test/index.ts +8 -0
  226. package/src/test/performance.ts +17 -0
  227. package/src/types.ts +33 -0
  228. package/src/xxhash/asHex.spec.ts +36 -0
  229. package/src/xxhash/asU8a.spec.ts +48 -0
  230. package/src/xxhash/asU8a.ts +45 -0
  231. package/src/xxhash/index.ts +8 -0
  232. package/src/xxhash/xxhash64.ts +155 -0
  233. package/tsconfig.build.json +18 -0
  234. package/tsconfig.spec.json +20 -0
@@ -0,0 +1,40 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@polkadot/dev-test/globals.d.ts" />
5
+
6
+ import { stringToU8a } from '@pezkuwi/util';
7
+ import { waitReady } from '@pezkuwi/wasm-crypto';
8
+
9
+ import { perfWasm } from '../test/index.js';
10
+ import { ed25519PairFromSeed, ed25519Sign } from './index.js';
11
+
12
+ const PAIR = ed25519PairFromSeed(
13
+ stringToU8a('12345678901234567890123456789012')
14
+ );
15
+
16
+ describe('ed25519Sign', (): void => {
17
+ beforeEach(async (): Promise<void> => {
18
+ await waitReady();
19
+ });
20
+
21
+ for (const onlyJs of [false, true]) {
22
+ describe(`onlyJs=${(onlyJs && 'true') || 'false'}`, (): void => {
23
+ it('returns a valid signature for the message', (): void => {
24
+ expect(
25
+ ed25519Sign(
26
+ new Uint8Array([0x61, 0x62, 0x63, 0x64]),
27
+ PAIR,
28
+ onlyJs
29
+ )
30
+ ).toEqual(
31
+ new Uint8Array([28, 58, 206, 239, 249, 70, 59, 191, 166, 40, 219, 218, 235, 170, 25, 79, 10, 94, 9, 197, 34, 126, 1, 150, 246, 68, 28, 238, 36, 26, 172, 163, 168, 90, 202, 211, 126, 246, 57, 212, 43, 24, 88, 197, 240, 113, 118, 76, 37, 81, 91, 110, 236, 50, 144, 134, 100, 223, 220, 238, 34, 185, 211, 7])
32
+ );
33
+ });
34
+ });
35
+ }
36
+
37
+ perfWasm('ed25519Sign', 250, (input, onlyJs) =>
38
+ ed25519Sign(input, PAIR, onlyJs)
39
+ );
40
+ });
@@ -0,0 +1,38 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { Keypair } from '../types.js';
5
+
6
+ import { ed25519 } from '@noble/curves/ed25519';
7
+
8
+ import { hasBigInt, u8aToU8a } from '@pezkuwi/util';
9
+ import { ed25519Sign as wasmSign, isReady } from '@pezkuwi/wasm-crypto';
10
+
11
+ /**
12
+ * @name ed25519Sign
13
+ * @summary Signs a message using the supplied secretKey
14
+ * @description
15
+ * Returns message signature of `message`, using the `secretKey`.
16
+ * @example
17
+ * <BR>
18
+ *
19
+ * ```javascript
20
+ * import { ed25519Sign } from '@pezkuwi/util-crypto';
21
+ *
22
+ * ed25519Sign([...], [...]); // => [...]
23
+ * ```
24
+ */
25
+ export function ed25519Sign (message: string | Uint8Array, { publicKey, secretKey }: Partial<Keypair>, onlyJs?: boolean): Uint8Array {
26
+ if (!secretKey) {
27
+ throw new Error('Expected a valid secretKey');
28
+ } else if (!publicKey) {
29
+ throw new Error('Expected a valid publicKey');
30
+ }
31
+
32
+ const messageU8a = u8aToU8a(message);
33
+ const privateU8a = secretKey.subarray(0, 32);
34
+
35
+ return !hasBigInt || (!onlyJs && isReady())
36
+ ? wasmSign(publicKey, privateU8a, messageU8a)
37
+ : ed25519.sign(messageU8a, privateU8a);
38
+ }
@@ -0,0 +1,84 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@polkadot/dev-test/globals.d.ts" />
5
+
6
+ import { stringToU8a } from '@pezkuwi/util';
7
+ import { waitReady } from '@pezkuwi/wasm-crypto';
8
+
9
+ import { ed25519PairFromSeed, ed25519Verify } from './index.js';
10
+
11
+ describe('ed25519Verify', (): void => {
12
+ let publicKey: Uint8Array;
13
+ let signature: Uint8Array;
14
+
15
+ beforeEach(async (): Promise<void> => {
16
+ await waitReady();
17
+
18
+ publicKey = ed25519PairFromSeed(
19
+ stringToU8a('12345678901234567890123456789012')
20
+ ).publicKey;
21
+ signature = new Uint8Array([28, 58, 206, 239, 249, 70, 59, 191, 166, 40, 219, 218, 235, 170, 25, 79, 10, 94, 9, 197, 34, 126, 1, 150, 246, 68, 28, 238, 36, 26, 172, 163, 168, 90, 202, 211, 126, 246, 57, 212, 43, 24, 88, 197, 240, 113, 118, 76, 37, 81, 91, 110, 236, 50, 144, 134, 100, 223, 220, 238, 34, 185, 211, 7]);
22
+ });
23
+
24
+ for (const onlyJs of [false, true]) {
25
+ describe(`onlyJs=${(onlyJs && 'true') || 'false'}`, (): void => {
26
+ it('validates a correctly signed message', (): void => {
27
+ expect(
28
+ ed25519Verify(
29
+ new Uint8Array([0x61, 0x62, 0x63, 0x64]),
30
+ signature,
31
+ publicKey,
32
+ onlyJs
33
+ )
34
+ ).toEqual(true);
35
+ });
36
+
37
+ it('fails a correctly signed message (message changed)', (): void => {
38
+ expect(
39
+ ed25519Verify(
40
+ new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65]),
41
+ signature,
42
+ publicKey,
43
+ onlyJs
44
+ )
45
+ ).toEqual(false);
46
+ });
47
+
48
+ it('fails a correctly signed message (signature changed)', (): void => {
49
+ signature[0] = 0xff;
50
+
51
+ expect(
52
+ ed25519Verify(
53
+ new Uint8Array([0x61, 0x62, 0x63, 0x64]),
54
+ signature,
55
+ publicKey,
56
+ onlyJs
57
+ )
58
+ ).toEqual(false);
59
+ });
60
+
61
+ it('throws error when publicKey lengths do not match', (): void => {
62
+ expect(
63
+ () => ed25519Verify(
64
+ new Uint8Array([0x61, 0x62, 0x63, 0x64]),
65
+ signature,
66
+ new Uint8Array([1, 2]),
67
+ onlyJs
68
+ )
69
+ ).toThrow(/Invalid publicKey/);
70
+ });
71
+
72
+ it('throws error when signature lengths do not match', (): void => {
73
+ expect(
74
+ () => ed25519Verify(
75
+ new Uint8Array([0x61, 0x62, 0x63, 0x64]),
76
+ new Uint8Array([1, 2]),
77
+ publicKey,
78
+ onlyJs
79
+ )
80
+ ).toThrow(/Invalid signature/);
81
+ });
82
+ });
83
+ }
84
+ });
@@ -0,0 +1,41 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { ed25519 } from '@noble/curves/ed25519';
5
+
6
+ import { hasBigInt, u8aToU8a } from '@pezkuwi/util';
7
+ import { ed25519Verify as wasmVerify, isReady } from '@pezkuwi/wasm-crypto';
8
+
9
+ /**
10
+ * @name ed25519Sign
11
+ * @summary Verifies the signature on the supplied message.
12
+ * @description
13
+ * Verifies the `signature` on `message` with the supplied `publicKey`. Returns `true` on sucess, `false` otherwise.
14
+ * @example
15
+ * <BR>
16
+ *
17
+ * ```javascript
18
+ * import { ed25519Verify } from '@pezkuwi/util-crypto';
19
+ *
20
+ * ed25519Verify([...], [...], [...]); // => true/false
21
+ * ```
22
+ */
23
+ export function ed25519Verify (message: string | Uint8Array, signature: string | Uint8Array, publicKey: string | Uint8Array, onlyJs?: boolean): boolean {
24
+ const messageU8a = u8aToU8a(message);
25
+ const publicKeyU8a = u8aToU8a(publicKey);
26
+ const signatureU8a = u8aToU8a(signature);
27
+
28
+ if (publicKeyU8a.length !== 32) {
29
+ throw new Error(`Invalid publicKey, received ${publicKeyU8a.length}, expected 32`);
30
+ } else if (signatureU8a.length !== 64) {
31
+ throw new Error(`Invalid signature, received ${signatureU8a.length} bytes, expected 64`);
32
+ }
33
+
34
+ try {
35
+ return !hasBigInt || (!onlyJs && isReady())
36
+ ? wasmVerify(signatureU8a, messageU8a, publicKeyU8a)
37
+ : ed25519.verify(signatureU8a, messageU8a, publicKeyU8a);
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
@@ -0,0 +1,59 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@polkadot/dev-test/globals.d.ts" />
5
+
6
+ import { keccakAsU8a } from '../keccak/index.js';
7
+ import { ethereumEncode } from './index.js';
8
+
9
+ describe('formatAddress', () => {
10
+ describe('address to address encoding', (): void => {
11
+ const ADDRESS = '0x00a329c0648769A73afAc7F9381E08FB43dBEA72';
12
+
13
+ it('returns 0x for no address', () => {
14
+ expect(ethereumEncode()).toBe('0x');
15
+ });
16
+
17
+ it('returns fails on invalid address', () => {
18
+ expect(
19
+ () => ethereumEncode('0xnotaddress')
20
+ ).toThrow(/Invalid address or publicKey provided/);
21
+ });
22
+
23
+ it('converts lowercase to the checksummed address', () => {
24
+ expect(ethereumEncode(ADDRESS.toLowerCase())).toBe(ADDRESS);
25
+ });
26
+
27
+ it('converts uppercase to the checksummed address', () => {
28
+ expect(ethereumEncode(ADDRESS.toUpperCase().replace('0X', '0x'))).toBe(ADDRESS);
29
+ });
30
+
31
+ it('returns formatted address on checksum input', () => {
32
+ expect(ethereumEncode(ADDRESS)).toBe(ADDRESS);
33
+ });
34
+ });
35
+
36
+ describe('from publicKey', (): void => {
37
+ const ADDRESS = '0x4119b2e6c3Cb618F4f0B93ac77f9BeeC7FF02887';
38
+
39
+ it('encodes a compressed publicKey', (): void => {
40
+ expect(
41
+ ethereumEncode('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
42
+ ).toEqual(ADDRESS);
43
+ });
44
+
45
+ it('encodes an expanded publicKey', (): void => {
46
+ expect(
47
+ ethereumEncode('0x04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
48
+ ).toEqual(ADDRESS);
49
+ });
50
+
51
+ it('encodes a pre-hashed key', (): void => {
52
+ expect(
53
+ ethereumEncode(
54
+ keccakAsU8a('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
55
+ )
56
+ ).toEqual(ADDRESS);
57
+ });
58
+ });
59
+ });
@@ -0,0 +1,39 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { HexString } from '@pezkuwi/util/types';
5
+
6
+ import { u8aToHex, u8aToU8a } from '@pezkuwi/util';
7
+
8
+ import { keccakAsU8a } from '../keccak/index.js';
9
+ import { secp256k1Expand } from '../secp256k1/index.js';
10
+
11
+ function getH160 (u8a: Uint8Array): Uint8Array {
12
+ if ([33, 65].includes(u8a.length)) {
13
+ u8a = keccakAsU8a(secp256k1Expand(u8a));
14
+ }
15
+
16
+ return u8a.slice(-20);
17
+ }
18
+
19
+ export function ethereumEncode (addressOrPublic?: string | Uint8Array): HexString {
20
+ if (!addressOrPublic) {
21
+ return '0x';
22
+ }
23
+
24
+ const u8aAddress = u8aToU8a(addressOrPublic);
25
+
26
+ if (![20, 32, 33, 65].includes(u8aAddress.length)) {
27
+ throw new Error(`Invalid address or publicKey provided, received ${u8aAddress.length} bytes input`);
28
+ }
29
+
30
+ const address = u8aToHex(getH160(u8aAddress), -1, false);
31
+ const hash = u8aToHex(keccakAsU8a(address), -1, false);
32
+ let result = '';
33
+
34
+ for (let i = 0; i < 40; i++) {
35
+ result = `${result}${parseInt(hash[i], 16) > 7 ? address[i].toUpperCase() : address[i]}`;
36
+ }
37
+
38
+ return `0x${result}`;
39
+ }
@@ -0,0 +1,6 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export { ethereumEncode } from './encode.js';
5
+ export { isEthereumAddress } from './isAddress.js';
6
+ export { isEthereumChecksum } from './isChecksum.js';
@@ -0,0 +1,34 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@polkadot/dev-test/globals.d.ts" />
5
+
6
+ import { isEthereumAddress } from './index.js';
7
+
8
+ const ADDRESS = '0x00a329c0648769A73afAc7F9381E08FB43dBEA72';
9
+
10
+ describe('isEthereumAddress', () => {
11
+ it('returns true when fully lowercase', () => {
12
+ expect(isEthereumAddress(ADDRESS.toLowerCase())).toBe(true);
13
+ });
14
+
15
+ it('returns true when fully uppercase', () => {
16
+ expect(isEthereumAddress(ADDRESS.toUpperCase().replace('0X', '0x'))).toBe(true);
17
+ });
18
+
19
+ it('returns true when checksummed', () => {
20
+ expect(isEthereumAddress(ADDRESS)).toBe(true);
21
+ });
22
+
23
+ it('returns false when empty address', () => {
24
+ expect(isEthereumAddress()).toBe(false);
25
+ });
26
+
27
+ it('returns false when invalid address', () => {
28
+ expect(isEthereumAddress('0xinvalid')).toBe(false);
29
+ });
30
+
31
+ it('returns false when invalid address of correct length', () => {
32
+ expect(isEthereumAddress('0xinvalid000123456789012345678901234567890')).toBe(false);
33
+ });
34
+ });
@@ -0,0 +1,16 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { isHex } from '@pezkuwi/util';
5
+
6
+ import { isEthereumChecksum } from './isChecksum.js';
7
+
8
+ export function isEthereumAddress (address?: string): boolean {
9
+ if (!address || address.length !== 42 || !isHex(address)) {
10
+ return false;
11
+ } else if (/^(0x)?[0-9a-f]{40}$/.test(address) || /^(0x)?[0-9A-F]{40}$/.test(address)) {
12
+ return true;
13
+ }
14
+
15
+ return isEthereumChecksum(address);
16
+ }
@@ -0,0 +1,27 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { u8aToHex } from '@pezkuwi/util';
5
+
6
+ import { keccakAsU8a } from '../keccak/index.js';
7
+
8
+ function isInvalidChar (char: string, byte: number): boolean {
9
+ return char !== (
10
+ byte > 7
11
+ ? char.toUpperCase()
12
+ : char.toLowerCase()
13
+ );
14
+ }
15
+
16
+ export function isEthereumChecksum (_address: string): boolean {
17
+ const address = _address.replace('0x', '');
18
+ const hash = u8aToHex(keccakAsU8a(address.toLowerCase()), -1, false);
19
+
20
+ for (let i = 0; i < 40; i++) {
21
+ if (isInvalidChar(address[i], parseInt(hash[i], 16))) {
22
+ return false;
23
+ }
24
+ }
25
+
26
+ return true;
27
+ }
@@ -0,0 +1,30 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@polkadot/dev-test/globals.d.ts" />
5
+
6
+ import { isEthereumChecksum } from './index.js';
7
+
8
+ const ADDRESS = '0x00a329c0648769A73afAc7F9381E08FB43dBEA72';
9
+
10
+ describe('isEthereumChecksum', () => {
11
+ it('returns false on invalid address', () => {
12
+ expect(isEthereumChecksum('0x00a329c0648769')).toBe(false);
13
+ });
14
+
15
+ it('returns false on non-checksum address', () => {
16
+ expect(isEthereumChecksum('0x1234567890abcdeedcba1234567890abcdeedcba')).toBe(false);
17
+ });
18
+
19
+ it('returns false when fully lowercase', () => {
20
+ expect(isEthereumChecksum(ADDRESS.toLowerCase())).toBe(false);
21
+ });
22
+
23
+ it('returns false when fully uppercase', () => {
24
+ expect(isEthereumChecksum(ADDRESS.toUpperCase().replace('0X', '0x'))).toBe(false);
25
+ });
26
+
27
+ it('returns true on a checksummed address', () => {
28
+ expect(isEthereumChecksum(ADDRESS)).toBe(true);
29
+ });
30
+ });
@@ -0,0 +1,54 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@polkadot/dev-test/globals.d.ts" />
5
+
6
+ import { mnemonicToLegacySeed } from '@pezkuwi/util-crypto';
7
+
8
+ import { hdEthereum } from './index.js';
9
+
10
+ describe('hdEthereum', (): void => {
11
+ const PHRASE = 'seed sock milk update focus rotate barely fade car face mechanic mercy';
12
+ const derivationPath = 'm/44\'/60\'/0\'/0/0';
13
+ const PUBLIC = new Uint8Array([
14
+ 3, 118, 64, 77, 247, 27, 4, 157,
15
+ 236, 206, 251, 221, 230, 244, 154, 147,
16
+ 189, 131, 249, 169, 102, 78, 3, 185,
17
+ 153, 19, 89, 40, 24, 25, 139, 131,
18
+ 93
19
+ ]);
20
+ const SECRET = new Uint8Array([
21
+ 166, 162, 203, 17, 2, 206, 110, 176,
22
+ 18, 102, 230, 144, 90, 158, 25, 232,
23
+ 43, 180, 176, 49, 189, 149, 3, 71,
24
+ 243, 228, 223, 104, 125, 132, 58, 228
25
+ ]
26
+ );
27
+ const PUBLICDERIVED = new Uint8Array([
28
+ 3, 129, 53, 27, 27, 70, 210, 96,
29
+ 43, 9, 146, 187, 93, 85, 49, 249,
30
+ 193, 105, 107, 8, 18, 254, 178, 83,
31
+ 75, 104, 132, 173, 196, 126, 46, 29,
32
+ 139
33
+ ]);
34
+ const SECRETDERIVED = new Uint8Array([
35
+ 7, 13, 195, 17, 115, 0, 1, 25,
36
+ 24, 226, 107, 2, 23, 105, 69, 204,
37
+ 21, 195, 213, 72, 207, 73, 253, 132,
38
+ 24, 217, 127, 147, 175, 105, 158, 70
39
+ ]);
40
+
41
+ it('derives the right key pair from a mnemonic', (): void => {
42
+ const key = hdEthereum(mnemonicToLegacySeed(PHRASE, '', false, 64));
43
+
44
+ expect(key.publicKey).toEqual(PUBLIC);
45
+ expect(key.secretKey).toEqual(SECRET);
46
+ });
47
+
48
+ it('derives the right key pair from a mnemonic and a derivation path', (): void => {
49
+ const key = hdEthereum(mnemonicToLegacySeed(PHRASE, '', false, 64), derivationPath);
50
+
51
+ expect(key.publicKey).toEqual(PUBLICDERIVED);
52
+ expect(key.secretKey).toEqual(SECRETDERIVED);
53
+ });
54
+ });
@@ -0,0 +1,69 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { Keypair } from '../../types.js';
5
+
6
+ import { bnToU8a, stringToU8a, u8aConcat } from '@pezkuwi/util';
7
+
8
+ import { BN_BE_32_OPTS } from '../../bn.js';
9
+ import { hmacShaAsU8a } from '../../hmac/index.js';
10
+ import { secp256k1PairFromSeed, secp256k1PrivateKeyTweakAdd } from '../../secp256k1/index.js';
11
+ import { HARDENED, hdValidatePath } from '../validatePath.js';
12
+
13
+ interface CodedKeypair extends Keypair {
14
+ chainCode: Uint8Array;
15
+ }
16
+
17
+ const MASTER_SECRET = stringToU8a('Bitcoin seed');
18
+
19
+ function createCoded (secretKey: Uint8Array, chainCode: Uint8Array): CodedKeypair {
20
+ return {
21
+ chainCode,
22
+ publicKey: secp256k1PairFromSeed(secretKey).publicKey,
23
+ secretKey
24
+ };
25
+ }
26
+
27
+ function deriveChild (hd: CodedKeypair, index: number): CodedKeypair {
28
+ const indexBuffer = bnToU8a(index, BN_BE_32_OPTS);
29
+ const data = index >= HARDENED
30
+ ? u8aConcat(new Uint8Array(1), hd.secretKey, indexBuffer)
31
+ : u8aConcat(hd.publicKey, indexBuffer);
32
+
33
+ try {
34
+ const I = hmacShaAsU8a(hd.chainCode, data, 512);
35
+
36
+ return createCoded(
37
+ secp256k1PrivateKeyTweakAdd(hd.secretKey, I.slice(0, 32)),
38
+ I.slice(32)
39
+ );
40
+ } catch {
41
+ // In case parse256(IL) >= n or ki == 0, proceed with the next value for i
42
+ return deriveChild(hd, index + 1);
43
+ }
44
+ }
45
+
46
+ export function hdEthereum (seed: Uint8Array, path = ''): Keypair {
47
+ const I = hmacShaAsU8a(MASTER_SECRET, seed, 512);
48
+ let hd = createCoded(I.slice(0, 32), I.slice(32));
49
+
50
+ if (!path || path === 'm' || path === 'M' || path === "m'" || path === "M'") {
51
+ return hd;
52
+ }
53
+
54
+ if (!hdValidatePath(path)) {
55
+ throw new Error('Invalid derivation path');
56
+ }
57
+
58
+ const parts = path.split('/').slice(1);
59
+
60
+ for (const p of parts) {
61
+ hd = deriveChild(hd, parseInt(p, 10) + (
62
+ (p.length > 1) && p.endsWith("'")
63
+ ? HARDENED
64
+ : 0
65
+ ));
66
+ }
67
+
68
+ return hd;
69
+ }
@@ -0,0 +1,6 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export { hdEthereum } from './ethereum/index.js';
5
+ export { hdLedger } from './ledger/index.js';
6
+ export { hdValidatePath } from './validatePath.js';
@@ -0,0 +1,34 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { BN_EIGHT, bnToU8a, u8aConcat, u8aToBn } from '@pezkuwi/util';
5
+
6
+ import { BN_LE_32_OPTS, BN_LE_512_OPTS, BN_LE_OPTS } from '../../bn.js';
7
+ import { hmacShaAsU8a } from '../../hmac/index.js';
8
+
9
+ // performs hard-only derivation on the xprv
10
+ export function ledgerDerivePrivate (xprv: Uint8Array, index: number): Uint8Array {
11
+ const kl = xprv.subarray(0, 32);
12
+ const kr = xprv.subarray(32, 64);
13
+ const cc = xprv.subarray(64, 96);
14
+ const data = u8aConcat([0], kl, kr, bnToU8a(index, BN_LE_32_OPTS));
15
+ const z = hmacShaAsU8a(cc, data, 512);
16
+
17
+ data[0] = 0x01;
18
+
19
+ return u8aConcat(
20
+ bnToU8a(
21
+ u8aToBn(kl, BN_LE_OPTS).iadd(
22
+ u8aToBn(z.subarray(0, 28), BN_LE_OPTS).imul(BN_EIGHT)
23
+ ),
24
+ BN_LE_512_OPTS
25
+ ).subarray(0, 32),
26
+ bnToU8a(
27
+ u8aToBn(kr, BN_LE_OPTS).iadd(
28
+ u8aToBn(z.subarray(32, 64), BN_LE_OPTS)
29
+ ),
30
+ BN_LE_512_OPTS
31
+ ).subarray(0, 32),
32
+ hmacShaAsU8a(cc, data, 512).subarray(32, 64)
33
+ );
34
+ }
@@ -0,0 +1,64 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@polkadot/dev-test/globals.d.ts" />
5
+
6
+ import { u8aToHex } from '@pezkuwi/util';
7
+
8
+ import { hdLedger } from '../index.js';
9
+
10
+ const MNE_0 = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
11
+ const MNE_1 = 'open jelly jeans corn ketchup supreme brief element armed lens vault weather original scissors rug priority vicious lesson raven spot gossip powder person volcano';
12
+ const MNE_P = `${MNE_1} testing`;
13
+
14
+ const TESTS = {
15
+ Kusama: {
16
+ slip44: 0x01b2,
17
+ tests: [
18
+ {
19
+ ed25519: '0x98cb4e14e0e08ea876f88d728545ea7572dc07dbbe69f1731c418fb827e69d41',
20
+ index: [0, 0],
21
+ mnemonic: MNE_0
22
+ },
23
+ {
24
+ ed25519: '0x70e9010e84c81095aaa5f63b1c5a6a66a1dcbec017a23c2f3b7a1b08fe5ea65a',
25
+ index: [0, 0],
26
+ mnemonic: MNE_1
27
+ },
28
+ {
29
+ ed25519: '0xf06730efb1e6ea59ac752a7c3620fade3909062fb88597856cc3af72045fa65a',
30
+ index: [5, 7],
31
+ mnemonic: MNE_1
32
+ }
33
+ ]
34
+ },
35
+ Polkadot: {
36
+ slip44: 0x0162,
37
+ tests: [
38
+ {
39
+ ed25519: '0xe8c68348586d53e4e8d1a864b0e4e17c75e4eb06e0c63c1432bef2ba29e69d41',
40
+ index: [0, 0],
41
+ mnemonic: MNE_0
42
+ },
43
+ {
44
+ ed25519: '0x3890e8db837eba3f8f25215c753e1091062298ce671a51441e7ef89a7adc4f48',
45
+ index: [0, 0],
46
+ mnemonic: MNE_P
47
+ }
48
+ ]
49
+ }
50
+ };
51
+
52
+ describe('ledgerDerive', (): void => {
53
+ Object.entries(TESTS).forEach(([network, { slip44, tests }]): void => {
54
+ tests.forEach(({ ed25519, index: [account, address], mnemonic }, index): void => {
55
+ it(`derives a known ed25519 seed for ${network} (${index})`, (): void => {
56
+ expect(u8aToHex(
57
+ hdLedger(mnemonic, `m/44'/${slip44}'/${account}'/0'/${address}'`)
58
+ .secretKey
59
+ .slice(0, 32)
60
+ )).toEqual(ed25519);
61
+ });
62
+ });
63
+ });
64
+ });