Skip to content

Commit 02d11a7

Browse files
authored
Merge pull request #18 from paulmillr/master
Re-use noble-hashes crypto module
2 parents 2fe90d9 + e155e37 commit 02d11a7

File tree

9 files changed

+40
-83
lines changed

9 files changed

+40
-83
lines changed

README.md

+2-13
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ const { utf8ToBytes } = require("ethereum-cryptography/utils");
113113
sha256(utf8ToBytes("abc"))
114114

115115
// If you need hex
116-
const { toHex } = require("ethereum-cryptography/utils");
116+
const { bytesToHex as toHex } = require("ethereum-cryptography/utils");
117117
toHex(sha256(utf8ToBytes("abc")))
118118
```
119119

@@ -208,7 +208,7 @@ Note: if you've been using ethereum-cryptography v0.1, it had different API. We'
208208
This module exports a single class `HDKey`, which should be used like this:
209209

210210
```ts
211-
const { HDKey } = require("ethereum-cryptography/secp256k1");
211+
const { HDKey } = require("ethereum-cryptography/hdkey");
212212
const hdkey1 = HDKey.fromMasterSeed(seed);
213213
const hdkey2 = HDKey.fromExtendedKey(base58key);
214214
const hdkey3 = HDKey.fromJSON({ xpriv: string });
@@ -269,17 +269,6 @@ but it's backed by this package's primitives, and has built-in TypeScript types.
269269
Its only difference is that it has to be be used with a named import.
270270
The implementation is [loosely based on hdkey, which has MIT License](#LICENSE).
271271

272-
```js
273-
const { HDKey } = require("ethereum-cryptography/hdkey");
274-
const { hexToBytes } = require("ethereum-cryptography/utils");
275-
276-
const seed = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542";
277-
const hdkey = HDKey.fromMasterSeed(hexToBytes(seed));
278-
const childkey = hdkey.derive("m/0/2147483647'/1");
279-
280-
console.log(childkey.privateExtendedKey);
281-
```
282-
283272
## BIP39 Mnemonic Seed Phrase
284273

285274
```ts

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
"*.d.ts.map"
2525
],
2626
"dependencies": {
27-
"micro-base": "^0.10.0",
28-
"@noble/hashes": "^0.5.2",
27+
"micro-base": "^0.10.1",
28+
"@noble/hashes": "^0.5.7",
2929
"@noble/secp256k1": "^1.3.3"
3030
},
3131
"browser": {

src/aes.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { concatBytes, crypto, equalsBytes } from "./utils";
1+
import { crypto } from "@noble/hashes/crypto";
2+
import { concatBytes, equalsBytes } from "./utils";
23

34
function validateOpt(key: Uint8Array, iv: Uint8Array, mode: string) {
45
if (!mode.startsWith("aes-")) {

src/bip39/index.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { pbkdf2, pbkdf2Async } from "@noble/hashes/pbkdf2";
22
import { sha256 } from "@noble/hashes/sha256";
33
import { sha512 } from "@noble/hashes/sha512";
4-
import { assertBytes, assertNumber } from "@noble/hashes/utils";
4+
import { assertBytes, assertNumber, randomBytes } from "@noble/hashes/utils";
55
import { utils as baseUtils } from "micro-base";
6-
import { getRandomBytesSync } from "../random";
76

87
const isJapanese = (wordlist: string[]) =>
98
wordlist[0] === "\u3042\u3044\u3053\u304f\u3057\u3093"; // Japanese wordlist
@@ -27,7 +26,7 @@ export function generateMnemonic(
2726
if (strength % 32 !== 0) {
2827
throw new TypeError("Invalid entropy");
2928
}
30-
return entropyToMnemonic(getRandomBytesSync(strength / 8), wordlist);
29+
return entropyToMnemonic(randomBytes(strength / 8), wordlist);
3130
}
3231

3332
const checksum = (entropy: Uint8Array) => {

src/hdkey.ts

+6-11
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ import { hmac } from "@noble/hashes/hmac";
22
import { ripemd160 } from "@noble/hashes/ripemd160";
33
import { sha256 } from "@noble/hashes/sha256";
44
import { sha512 } from "@noble/hashes/sha512";
5-
import { bytesToHex } from "@noble/hashes/utils";
6-
import { base58check } from "micro-base";
7-
import * as secp from "./secp256k1";
85
import {
96
assertBytes,
7+
bytesToHex,
108
concatBytes,
119
createView,
1210
hexToBytes,
1311
utf8ToBytes
14-
} from "./utils";
12+
} from "@noble/hashes/utils";
13+
import { base58check } from "micro-base";
14+
import * as secp from "./secp256k1";
1515
const base58c = base58check(sha256);
1616

1717
function bytesToNumber(bytes: Uint8Array): bigint {
@@ -40,8 +40,8 @@ export interface Versions {
4040
const hash160 = (data: Uint8Array) => ripemd160(sha256(data));
4141
const fromU32 = (data: Uint8Array) => createView(data).getUint32(0, false);
4242
const toU32 = (n: number) => {
43-
if (!Number.isSafeInteger(n) || n < 0 || n >= 2 ** 32) {
44-
throw new Error(`Invalid number=${n}. Should be [0, 2**32)`);
43+
if (!Number.isSafeInteger(n) || n < 0 || n > 2 ** 32 - 1) {
44+
throw new Error(`Invalid number=${n}. Should be from 0 to 2 ** 32 - 1`);
4545
}
4646
const buf = new Uint8Array(4);
4747
createView(buf).setUint32(0, n, false);
@@ -219,11 +219,6 @@ export class HDKey {
219219
}
220220

221221
public deriveChild(index: number): HDKey {
222-
if (!Number.isSafeInteger(index) || index < 0 || index >= 2 ** 32) {
223-
throw new Error(
224-
`Child index should be positive 32-bit integer, not ${index}`
225-
);
226-
}
227222
if (!this.pubKey || !this.chainCode) {
228223
throw new Error("No publicKey or chainCode set");
229224
}

src/random.ts

+2-8
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
1-
import { crypto } from "./utils";
1+
import { randomBytes } from "@noble/hashes/utils";
22

33
export function getRandomBytesSync(bytes: number): Uint8Array {
4-
if (crypto.web) {
5-
return crypto.web.getRandomValues(new Uint8Array(bytes));
6-
} else if (crypto.node) {
7-
return new Uint8Array(crypto.node.randomBytes(bytes).buffer);
8-
} else {
9-
throw new Error("The environment doesn't have randomBytes function");
10-
}
4+
return randomBytes(bytes);
115
}
126

137
export async function getRandomBytes(bytes: number): Promise<Uint8Array> {

src/secp256k1-compat.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as secp from "./secp256k1";
33
import { assertBool, assertBytes, hexToBytes, toHex } from "./utils";
44

55
// Legacy compatibility layer for elliptic via noble-secp256k1
6-
// Use `curve-secp256k1` module directly instead
6+
// Use `secp256k1` module directly instead
77

88
// Copy-paste from secp256k1, maybe export it?
99
const bytesToNumber = (bytes: Uint8Array) => hexToNumber(toHex(bytes));
@@ -166,6 +166,9 @@ export function privateKeyTweakAdd(
166166
assertBytes(privateKey, 32);
167167
assertBytes(tweak, 32);
168168
let bn = bytesToNumber(tweak);
169+
if (bn === 0n) {
170+
throw new Error("Tweak must not be zero");
171+
}
169172
if (bn >= ORDER) {
170173
throw new Error("Tweak bigger than curve order");
171174
}
@@ -231,6 +234,9 @@ export function publicKeyTweakAdd(
231234
assertBool(compressed);
232235
const p1 = secp.Point.fromHex(publicKey);
233236
const p2 = secp.Point.fromPrivateKey(tweak);
237+
if (p2.equals(secp.Point.ZERO)) {
238+
throw new Error("Tweak must not be zero");
239+
}
234240
const point = p1.add(p2);
235241
return output(out, compressed ? 33 : 65, point.toRawBytes(compressed));
236242
}
@@ -245,6 +251,9 @@ export function publicKeyTweakMul(
245251
assertBytes(tweak, 32);
246252
assertBool(compressed);
247253
const bn = bytesToNumber(tweak);
254+
if (bn === 0n) {
255+
throw new Error("Tweak must not be zero");
256+
}
248257
if (bn <= 0 || bn >= ORDER) {
249258
throw new Error("Tweak is zero or bigger than curve order");
250259
}
@@ -259,6 +268,9 @@ export function privateKeyTweakMul(
259268
assertBytes(privateKey, 32);
260269
assertBytes(tweak, 32);
261270
let bn = bytesToNumber(tweak);
271+
if (bn === 0n) {
272+
throw new Error("Tweak must not be zero");
273+
}
262274
if (bn >= ORDER) {
263275
throw new Error("Tweak bigger than curve order");
264276
}

src/secp256k1.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ export {
1010
utils,
1111
Point,
1212
Signature,
13-
CURVE
13+
CURVE,
14+
schnorr
1415
} from "@noble/secp256k1";
1516

1617
// Enable sync API for noble-secp256k1

src/utils.ts

+9-43
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,24 @@
11
// buf.toString('hex') -> toHex(buf)
22
import { assertBytes } from "@noble/hashes/utils";
33
export {
4+
assertBool,
45
assertBytes,
6+
bytesToHex,
57
bytesToHex as toHex,
6-
createView
8+
concatBytes,
9+
hexToBytes,
10+
createView,
11+
utf8ToBytes
712
} from "@noble/hashes/utils";
8-
// Buffer.from(hex, 'hex') -> hexToBytes(hex)
9-
export function hexToBytes(hex: string): Uint8Array {
10-
if (typeof hex !== "string") {
11-
throw new TypeError(`hexToBytes: expected string, got ${typeof hex}`);
12-
}
13-
if (hex.length % 2) {
14-
throw new Error("hexToBytes: received invalid unpadded hex");
15-
}
16-
const array = new Uint8Array(hex.length / 2);
17-
for (let i = 0; i < array.length; i++) {
18-
const j = i * 2;
19-
array[i] = Number.parseInt(hex.slice(j, j + 2), 16);
20-
}
21-
return array;
22-
}
23-
// Buffer.from(s, 'utf8') -> utf8ToBytes(s)
24-
export function utf8ToBytes(s: string) {
25-
if (typeof s !== "string") {
26-
throw new TypeError(`utf8ToBytes expected string, got ${typeof s}`);
27-
}
28-
return new TextEncoder().encode(s);
29-
}
13+
3014
// buf.toString('utf8') -> bytesToUtf8(buf)
3115
export function bytesToUtf8(data: Uint8Array): string {
3216
if (!(data instanceof Uint8Array)) {
3317
throw new TypeError(`bytesToUtf8 expected Uint8Array, got ${typeof data}`);
3418
}
3519
return new TextDecoder().decode(data);
3620
}
21+
3722
// buf.equals(buf2) -> equalsBytes(buf, buf2)
3823
export function equalsBytes(a: Uint8Array, b: Uint8Array): boolean {
3924
if (a.length !== b.length) {
@@ -46,27 +31,8 @@ export function equalsBytes(a: Uint8Array, b: Uint8Array): boolean {
4631
}
4732
return true;
4833
}
49-
// Buffer.concat([buf1, buf2]) -> concatBytes(buf1, buf2)
50-
export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
51-
if (arrays.length === 1) {
52-
return arrays[0];
53-
}
54-
const length = arrays.reduce((a, arr) => a + arr.length, 0);
55-
const result = new Uint8Array(length);
56-
for (let i = 0, pad = 0; i < arrays.length; i++) {
57-
const arr = arrays[i];
58-
result.set(arr, pad);
59-
pad += arr.length;
60-
}
61-
return result;
62-
}
63-
// Internal utils
64-
export function assertBool(b: boolean) {
65-
if (typeof b !== "boolean") {
66-
throw new Error(`Expected boolean, not ${b}`);
67-
}
68-
}
6934

35+
// Internal utils
7036
export function wrapHash(hash: (msg: Uint8Array) => Uint8Array) {
7137
return (msg: Uint8Array) => {
7238
assertBytes(msg);

0 commit comments

Comments
 (0)