Skip to content

Commit

Permalink
feat: replace buffer with uint8array
Browse files Browse the repository at this point in the history
  • Loading branch information
Nesopie committed Aug 10, 2024
1 parent 94599a8 commit 15160f9
Show file tree
Hide file tree
Showing 19 changed files with 326 additions and 319 deletions.
17 changes: 13 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@
"url": "git+https://github.com/bitcoinjs/bip32.git"
},
"files": [
"src",
"types"
"src"
],
"dependencies": {
"@noble/hashes": "^1.2.0",
"@scure/base": "^1.1.1",
"uint8array-tools": "^0.0.8",
"valibot": "^0.37.0",
"wif": "^5.0.0"
},
Expand Down
91 changes: 56 additions & 35 deletions src/cjs/bip32.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ const sha256_1 = require("@noble/hashes/sha256");
const v = __importStar(require("valibot"));
const types_js_1 = require("./types.cjs");
const wif = __importStar(require("wif"));
const tools = __importStar(require("uint8array-tools"));
const _bs58check = (0, base_1.base58check)(sha256_1.sha256);
const bs58check = {
encode: (data) => _bs58check.encode(Uint8Array.from(data)),
decode: (str) => Buffer.from(_bs58check.decode(str)),
encode: (data) => _bs58check.encode(data),
decode: (str) => _bs58check.decode(str),
};
function BIP32Factory(ecc) {
(0, testecc_js_1.testEcc)(ecc);
Expand Down Expand Up @@ -83,7 +84,7 @@ function BIP32Factory(ecc) {
}
get publicKey() {
if (this.__Q === undefined)
this.__Q = Buffer.from(ecc.pointFromScalar(this.__D, true));
this.__Q = ecc.pointFromScalar(this.__D, true);
return this.__Q;
}
get privateKey() {
Expand All @@ -95,18 +96,18 @@ function BIP32Factory(ecc) {
if (lowR === undefined)
lowR = this.lowR;
if (lowR === false) {
return Buffer.from(ecc.sign(hash, this.privateKey));
return ecc.sign(hash, this.privateKey);
}
else {
let sig = Buffer.from(ecc.sign(hash, this.privateKey));
const extraData = Buffer.alloc(32, 0);
let sig = ecc.sign(hash, this.privateKey);
const extraData = new Uint8Array(32);
let counter = 0;
// if first try is lowR, skip the loop
// for second try and on, add extra entropy counting up
while (sig[0] > 0x7f) {
counter++;
extraData.writeUIntLE(counter, 0, 6);
sig = Buffer.from(ecc.sign(hash, this.privateKey, extraData));
tools.writeUInt32(extraData, 0, counter, 'LE');
sig = ecc.sign(hash, this.privateKey, extraData);
}
return sig;
}
Expand All @@ -116,7 +117,7 @@ function BIP32Factory(ecc) {
throw new Error('Missing private key');
if (!ecc.signSchnorr)
throw new Error('signSchnorr not supported by ecc library');
return Buffer.from(ecc.signSchnorr(hash, this.privateKey));
return ecc.signSchnorr(hash, this.privateKey);
}
verify(hash, signature) {
return ecc.verify(hash, this.publicKey, signature);
Expand Down Expand Up @@ -174,28 +175,36 @@ function BIP32Factory(ecc) {
const version = !this.isNeutered()
? network.bip32.private
: network.bip32.public;
const buffer = Buffer.allocUnsafe(78);
const buffer = new Uint8Array(78);
// 4 bytes: version bytes
buffer.writeUInt32BE(version, 0);
// buffer.writeUInt32BE(version, 0);
tools.writeUInt32(buffer, 0, version, 'BE');
// 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ....
buffer.writeUInt8(this.depth, 4);
// buffer.writeUInt8(this.depth, 4);
tools.writeUInt8(buffer, 4, this.depth);
// 4 bytes: the fingerprint of the parent's key (0x00000000 if master key)
buffer.writeUInt32BE(this.parentFingerprint, 5);
// buffer.writeUInt32BE(this.parentFingerprint, 5);
tools.writeUInt32(buffer, 5, this.parentFingerprint, 'BE');
// 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized.
// This is encoded in big endian. (0x00000000 if master key)
buffer.writeUInt32BE(this.index, 9);
// buffer.writeUInt32BE(this.index, 9);
tools.writeUInt32(buffer, 9, this.index, 'BE');
// 32 bytes: the chain code
this.chainCode.copy(buffer, 13);
// this.chainCode.copy(buffer, 13);
buffer.set(this.chainCode, 13);
// 33 bytes: the public key or private key data
if (!this.isNeutered()) {
// 0x00 + k for private keys
buffer.writeUInt8(0, 45);
this.privateKey.copy(buffer, 46);
// buffer.writeUInt8(0, 45);
tools.writeUInt8(buffer, 45, 0);
// this.privateKey!.copy(buffer, 46);
buffer.set(this.privateKey, 46);
// 33 bytes: the public key
}
else {
// X9.62 encoding for public keys
this.publicKey.copy(buffer, 45);
// this.publicKey.copy(buffer, 45);
buffer.set(this.publicKey, 45);
}
return bs58check.encode(buffer);
}
Expand All @@ -213,22 +222,26 @@ function BIP32Factory(ecc) {
// typeforce(typeforce.UInt32, index);
v.parse(types_js_1.Uint32Schema, index);
const isHardened = index >= HIGHEST_BIT;
const data = Buffer.allocUnsafe(37);
const data = new Uint8Array(37);
// Hardened child
if (isHardened) {
if (this.isNeutered())
throw new TypeError('Missing private key for hardened child key');
// data = 0x00 || ser256(kpar) || ser32(index)
data[0] = 0x00;
this.privateKey.copy(data, 1);
data.writeUInt32BE(index, 33);
// this.privateKey!.copy(data, 1);
data.set(this.privateKey, 1);
// data.writeUInt32BE(index, 33);
tools.writeUInt32(data, 33, index, 'BE');
// Normal child
}
else {
// data = serP(point(kpar)) || ser32(index)
// = serP(Kpar) || ser32(index)
this.publicKey.copy(data, 0);
data.writeUInt32BE(index, 33);
// this.publicKey.copy(data, 0);
data.set(this.publicKey, 0);
// data.writeUInt32BE(index, 33);
tools.writeUInt32(data, 33, index, 'BE');
}
const I = crypto.hmacSHA512(this.chainCode, data);
const IL = I.slice(0, 32);
Expand All @@ -240,21 +253,25 @@ function BIP32Factory(ecc) {
let hd;
if (!this.isNeutered()) {
// ki = parse256(IL) + kpar (mod n)
const ki = Buffer.from(ecc.privateAdd(this.privateKey, IL));
const ki = ecc.privateAdd(this.privateKey, IL);
// In case ki == 0, proceed with the next value for i
if (ki == null)
return this.derive(index + 1);
hd = fromPrivateKeyLocal(ki, IR, this.network, this.depth + 1, index, this.fingerprint.readUInt32BE(0));
hd = fromPrivateKeyLocal(ki, IR, this.network, this.depth + 1, index,
// this.fingerprint.readUInt32BE(0),
tools.readUInt32(this.fingerprint, 0, 'BE'));
// Public parent key -> public child key
}
else {
// Ki = point(parse256(IL)) + Kpar
// = G*IL + Kpar
const Ki = Buffer.from(ecc.pointAddScalar(this.publicKey, IL, true));
const Ki = ecc.pointAddScalar(this.publicKey, IL, true);
// In case Ki is the point at infinity, proceed with the next value for i
if (Ki === null)
return this.derive(index + 1);
hd = fromPublicKeyLocal(Ki, IR, this.network, this.depth + 1, index, this.fingerprint.readUInt32BE(0));
hd = fromPublicKeyLocal(Ki, IR, this.network, this.depth + 1, index,
// this.fingerprint.readUInt32BE(0),
tools.readUInt32(this.fingerprint, 0, 'BE'));
}
return hd;
}
Expand Down Expand Up @@ -297,10 +314,10 @@ function BIP32Factory(ecc) {
const tweakedPublicKey = ecc.xOnlyPointAddTweak(xOnlyPubKey, t);
if (!tweakedPublicKey || tweakedPublicKey.xOnlyPubkey === null)
throw new Error('Cannot tweak public key!');
const parityByte = Buffer.from([
const parityByte = Uint8Array.from([
tweakedPublicKey.parity === 0 ? 0x02 : 0x03,
]);
const tweakedPublicKeyCompresed = Buffer.concat([
const tweakedPublicKeyCompresed = tools.concat([
parityByte,
tweakedPublicKey.xOnlyPubkey,
]);
Expand All @@ -320,7 +337,7 @@ function BIP32Factory(ecc) {
const tweakedPrivateKey = ecc.privateAdd(privateKey, t);
if (!tweakedPrivateKey)
throw new Error('Invalid tweaked private key!');
return new Bip32Signer(Buffer.from(tweakedPrivateKey), undefined);
return new Bip32Signer(tweakedPrivateKey, undefined);
}
}
function fromBase58(inString, network) {
Expand All @@ -329,28 +346,32 @@ function BIP32Factory(ecc) {
throw new TypeError('Invalid buffer length');
network = network || BITCOIN;
// 4 bytes: version bytes
const version = buffer.readUInt32BE(0);
// const version = buffer.readUInt32BE(0);
const version = tools.readUInt32(buffer, 0, 'BE');
if (version !== network.bip32.private && version !== network.bip32.public)
throw new TypeError('Invalid network version');
// 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ...
const depth = buffer[4];
// 4 bytes: the fingerprint of the parent's key (0x00000000 if master key)
const parentFingerprint = buffer.readUInt32BE(5);
// const parentFingerprint = buffer.readUInt32BE(5);
const parentFingerprint = tools.readUInt32(buffer, 5, 'BE');
if (depth === 0) {
if (parentFingerprint !== 0x00000000)
throw new TypeError('Invalid parent fingerprint');
}
// 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized.
// This is encoded in MSB order. (0x00000000 if master key)
const index = buffer.readUInt32BE(9);
// const index = buffer.readUInt32BE(9);
const index = tools.readUInt32(buffer, 9, 'BE');
if (depth === 0 && index !== 0)
throw new TypeError('Invalid index');
// 32 bytes: the chain code
const chainCode = buffer.slice(13, 45);
let hd;
// 33 bytes: private key data (0x00 + k)
if (version === network.bip32.private) {
if (buffer.readUInt8(45) !== 0x00)
// if (buffer.readUInt8(45) !== 0x00)
if (buffer[45] !== 0x00)
throw new TypeError('Invalid private key');
const k = buffer.slice(46, 78);
hd = fromPrivateKeyLocal(k, chainCode, network, depth, index, parentFingerprint);
Expand Down Expand Up @@ -392,7 +413,7 @@ function BIP32Factory(ecc) {
if (seed.length > 64)
throw new TypeError('Seed should be at most 512 bits');
network = network || BITCOIN;
const I = crypto.hmacSHA512(Buffer.from('Bitcoin seed', 'utf8'), seed);
const I = crypto.hmacSHA512(tools.fromUtf8('Bitcoin seed'), seed);
const IL = I.slice(0, 32);
const IR = I.slice(32);
return fromPrivateKey(IL, IR, network);
Expand Down
26 changes: 13 additions & 13 deletions src/cjs/bip32.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,36 @@ interface Network {
scriptHash?: number;
}
export interface Signer {
publicKey: Buffer;
publicKey: Uint8Array;
lowR: boolean;
sign(hash: Buffer, lowR?: boolean): Buffer;
verify(hash: Buffer, signature: Buffer): boolean;
signSchnorr(hash: Buffer): Buffer;
verifySchnorr(hash: Buffer, signature: Buffer): boolean;
sign(hash: Uint8Array, lowR?: boolean): Uint8Array;
verify(hash: Uint8Array, signature: Uint8Array): boolean;
signSchnorr(hash: Uint8Array): Uint8Array;
verifySchnorr(hash: Uint8Array, signature: Uint8Array): boolean;
}
export interface BIP32Interface extends Signer {
chainCode: Buffer;
chainCode: Uint8Array;
network: Network;
depth: number;
index: number;
parentFingerprint: number;
privateKey?: Buffer;
identifier: Buffer;
fingerprint: Buffer;
privateKey?: Uint8Array;
identifier: Uint8Array;
fingerprint: Uint8Array;
isNeutered(): boolean;
neutered(): BIP32Interface;
toBase58(): string;
toWIF(): string;
derive(index: number): BIP32Interface;
deriveHardened(index: number): BIP32Interface;
derivePath(path: string): BIP32Interface;
tweak(t: Buffer): Signer;
tweak(t: Uint8Array): Signer;
}
export interface BIP32API {
fromSeed(seed: Buffer, network?: Network): BIP32Interface;
fromSeed(seed: Uint8Array, network?: Network): BIP32Interface;
fromBase58(inString: string, network?: Network): BIP32Interface;
fromPublicKey(publicKey: Buffer, chainCode: Buffer, network?: Network): BIP32Interface;
fromPrivateKey(privateKey: Buffer, chainCode: Buffer, network?: Network): BIP32Interface;
fromPublicKey(publicKey: Uint8Array, chainCode: Uint8Array, network?: Network): BIP32Interface;
fromPrivateKey(privateKey: Uint8Array, chainCode: Uint8Array, network?: Network): BIP32Interface;
}
interface XOnlyPointAddTweakResult {
parity: 1 | 0;
Expand Down
5 changes: 2 additions & 3 deletions src/cjs/crypto.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ const ripemd160_1 = require("@noble/hashes/ripemd160");
const sha256_1 = require("@noble/hashes/sha256");
const sha512_1 = require("@noble/hashes/sha512");
function hash160(buffer) {
const sha256Hash = (0, sha256_1.sha256)(Uint8Array.from(buffer));
return Buffer.from((0, ripemd160_1.ripemd160)(sha256Hash));
return (0, ripemd160_1.ripemd160)((0, sha256_1.sha256)(buffer));
}
function hmacSHA512(key, data) {
return Buffer.from((0, hmac_1.hmac)(sha512_1.sha512, key, data));
return (0, hmac_1.hmac)(sha512_1.sha512, key, data);
}
4 changes: 2 additions & 2 deletions src/cjs/crypto.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export declare function hash160(buffer: Buffer): Buffer;
export declare function hmacSHA512(key: Buffer, data: Buffer): Buffer;
export declare function hash160(buffer: Uint8Array): Uint8Array;
export declare function hmacSHA512(key: Uint8Array, data: Uint8Array): Uint8Array;
Loading

0 comments on commit 15160f9

Please sign in to comment.