diff --git a/src/bip32.js b/src/bip32.js
index 9888131..9139d78 100644
--- a/src/bip32.js
+++ b/src/bip32.js
@@ -5,16 +5,12 @@ const crypto = require("./crypto");
const testecc_1 = require("./testecc");
const base_1 = require("@scure/base");
const sha256_1 = require("@noble/hashes/sha256");
+const uint8array_utils_1 = require("./uint8array-utils");
const typeforce = require('typeforce');
-const wif = require('wif');
-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)),
-};
+const bs58check = (0, base_1.base58check)(sha256_1.sha256);
function BIP32Factory(ecc) {
(0, testecc_1.testEcc)(ecc);
- const UINT256_TYPE = typeforce.BufferN(32);
+ const UINT256_TYPE = (0, uint8array_utils_1.Uint8ArrayTypeN)(32);
const NETWORK_TYPE = typeforce.compile({
wif: typeforce.UInt8,
bip32: {
@@ -42,7 +38,7 @@ function BIP32Factory(ecc) {
return typeforce.UInt32(value) && value <= UINT31_MAX;
}
function toXOnly(pubKey) {
- return pubKey.length === 32 ? pubKey : pubKey.slice(1, 33);
+ return pubKey.length === 32 ? pubKey : pubKey.subarray(1, 33);
}
class Bip32Signer {
constructor(__D, __Q) {
@@ -52,7 +48,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() {
@@ -64,18 +60,19 @@ 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);
+ const extraDataView = new DataView(extraData.buffer);
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));
+ extraDataView.setUint32(0, counter, true);
+ sig = ecc.sign(hash, this.privateKey, extraData);
}
return sig;
}
@@ -85,7 +82,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);
@@ -119,7 +116,7 @@ function BIP32Factory(ecc) {
return crypto.hash160(this.publicKey);
}
get fingerprint() {
- return this.identifier.slice(0, 4);
+ return this.identifier.subarray(0, 4);
}
get compressed() {
return true;
@@ -137,56 +134,53 @@ function BIP32Factory(ecc) {
const version = !this.isNeutered()
? network.bip32.private
: network.bip32.public;
- const buffer = Buffer.allocUnsafe(78);
+ const buffer = new Uint8Array(78);
+ const bufferView = new DataView(buffer.buffer);
// 4 bytes: version bytes
- buffer.writeUInt32BE(version, 0);
+ bufferView.setUint32(0, version, false);
// 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ....
- buffer.writeUInt8(this.depth, 4);
+ bufferView.setUint8(4, this.depth);
// 4 bytes: the fingerprint of the parent's key (0x00000000 if master key)
- buffer.writeUInt32BE(this.parentFingerprint, 5);
+ bufferView.setUint32(5, this.parentFingerprint, false);
// 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);
+ bufferView.setUint32(9, this.index, false);
// 32 bytes: the chain code
- 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);
+ bufferView.setUint8(45, 0);
+ buffer.set(this.privateKey, 46);
// 33 bytes: the public key
}
else {
// X9.62 encoding for public keys
- this.publicKey.copy(buffer, 45);
+ buffer.set(this.publicKey, 45);
}
return bs58check.encode(buffer);
}
- toWIF() {
- if (!this.privateKey)
- throw new TypeError('Missing private key');
- return wif.encode(this.network.wif, this.privateKey, true);
- }
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#child-key-derivation-ckd-functions
derive(index) {
typeforce(typeforce.UInt32, index);
const isHardened = index >= HIGHEST_BIT;
- const data = Buffer.allocUnsafe(37);
+ const data = new Uint8Array(37);
+ const dataView = new DataView(data.buffer);
// 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);
+ data.set(this.privateKey, 1);
+ dataView.setUint32(33, index, false);
// Normal child
}
else {
// data = serP(point(kpar)) || ser32(index)
// = serP(Kpar) || ser32(index)
- this.publicKey.copy(data, 0);
- data.writeUInt32BE(index, 33);
+ data.set(this.publicKey, 0);
+ dataView.setUint32(33, index, false);
}
const I = crypto.hmacSHA512(this.chainCode, data);
const IL = I.slice(0, 32);
@@ -198,21 +192,21 @@ 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, new DataView(this.fingerprint.buffer).getUint32(0, false));
// 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, new DataView(this.fingerprint.buffer).getUint32(0, false));
}
return hd;
}
@@ -253,13 +247,12 @@ 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([
- parityByte,
- tweakedPublicKey.xOnlyPubkey,
- ]);
+ const tweakedPublicKeyCompresed = new Uint8Array(tweakedPublicKey.xOnlyPubkey.length + 1);
+ tweakedPublicKeyCompresed.set(parityByte);
+ tweakedPublicKeyCompresed.set(tweakedPublicKey.xOnlyPubkey, 1);
return new Bip32Signer(undefined, tweakedPublicKeyCompresed);
}
tweakFromPrivateKey(t) {
@@ -276,44 +269,45 @@ 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) {
const buffer = bs58check.decode(inString);
+ const bufferView = new DataView(buffer.buffer);
if (buffer.length !== 78)
throw new TypeError('Invalid buffer length');
network = network || BITCOIN;
// 4 bytes: version bytes
- const version = buffer.readUInt32BE(0);
+ const version = bufferView.getUint32(0, false);
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 = bufferView.getUint32(5, false);
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 = bufferView.getUint32(9, false);
if (depth === 0 && index !== 0)
throw new TypeError('Invalid index');
// 32 bytes: the chain code
- const chainCode = buffer.slice(13, 45);
+ const chainCode = buffer.subarray(13, 45);
let hd;
// 33 bytes: private key data (0x00 + k)
if (version === network.bip32.private) {
- if (buffer.readUInt8(45) !== 0x00)
+ if (bufferView.getUint8(45) !== 0x00)
throw new TypeError('Invalid private key');
- const k = buffer.slice(46, 78);
+ const k = buffer.subarray(46, 78);
hd = fromPrivateKeyLocal(k, chainCode, network, depth, index, parentFingerprint);
// 33 bytes: public key data (0x02 + X or 0x03 + X)
}
else {
- const X = buffer.slice(45, 78);
+ const X = buffer.subarray(45, 78);
hd = fromPublicKeyLocal(X, chainCode, network, depth, index, parentFingerprint);
}
return hd;
@@ -336,7 +330,7 @@ function BIP32Factory(ecc) {
}
function fromPublicKeyLocal(publicKey, chainCode, network, depth, index, parentFingerprint) {
typeforce({
- publicKey: typeforce.BufferN(33),
+ publicKey: (0, uint8array_utils_1.Uint8ArrayTypeN)(33),
chainCode: UINT256_TYPE,
}, { publicKey, chainCode });
network = network || BITCOIN;
@@ -346,13 +340,14 @@ function BIP32Factory(ecc) {
return new BIP32(undefined, publicKey, chainCode, network, depth, index, parentFingerprint);
}
function fromSeed(seed, network) {
- typeforce(typeforce.Buffer, seed);
+ typeforce(uint8array_utils_1.Uint8ArrayType, seed);
if (seed.length < 16)
throw new TypeError('Seed should be at least 128 bits');
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 encoder = new TextEncoder();
+ const I = crypto.hmacSHA512(encoder.encode('Bitcoin seed'), seed);
const IL = I.slice(0, 32);
const IR = I.slice(32);
return fromPrivateKey(IL, IR, network);
diff --git a/src/crypto.js b/src/crypto.js
index a8a1b61..3180fe5 100644
--- a/src/crypto.js
+++ b/src/crypto.js
@@ -6,11 +6,10 @@ 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));
}
exports.hash160 = hash160;
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);
}
exports.hmacSHA512 = hmacSHA512;
diff --git a/src/testecc.js b/src/testecc.js
index a8bbfb3..68157eb 100644
--- a/src/testecc.js
+++ b/src/testecc.js
@@ -1,7 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.testEcc = void 0;
-const h = (hex) => Buffer.from(hex, 'hex');
+const utils_1 = require("@noble/hashes/utils");
+const uint8array_utils_1 = require("./uint8array-utils");
+const h = (hex) => (0, utils_1.hexToBytes)(hex);
function testEcc(ecc) {
assert(ecc.isPoint(h('0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')));
assert(!ecc.isPoint(h('030000000000000000000000000000000000000000000000000000000000000005')));
@@ -14,24 +16,24 @@ function testEcc(ecc) {
assert(!ecc.isPrivate(h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141')));
// order + 1
assert(!ecc.isPrivate(h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142')));
- assert(Buffer.from(ecc.pointFromScalar(h('b1121e4088a66a28f5b6b0f5844943ecd9f610196d7bb83b25214b60452c09af'))).equals(h('02b07ba9dca9523b7ef4bd97703d43d20399eb698e194704791a25ce77a400df99')));
+ assert((0, uint8array_utils_1.areUint8ArraysEqual)(ecc.pointFromScalar(h('b1121e4088a66a28f5b6b0f5844943ecd9f610196d7bb83b25214b60452c09af')), h('02b07ba9dca9523b7ef4bd97703d43d20399eb698e194704791a25ce77a400df99')));
if (ecc.xOnlyPointAddTweak) {
assert(ecc.xOnlyPointAddTweak(h('79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'), h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140')) === null);
let xOnlyRes = ecc.xOnlyPointAddTweak(h('1617d38ed8d8657da4d4761e8057bc396ea9e4b9d29776d4be096016dbd2509b'), h('a8397a935f0dfceba6ba9618f6451ef4d80637abf4e6af2669fbc9de6a8fd2ac'));
- assert(Buffer.from(xOnlyRes.xOnlyPubkey).equals(h('e478f99dab91052ab39a33ea35fd5e6e4933f4d28023cd597c9a1f6760346adf')) && xOnlyRes.parity === 1);
+ assert((0, uint8array_utils_1.areUint8ArraysEqual)(xOnlyRes.xOnlyPubkey, h('e478f99dab91052ab39a33ea35fd5e6e4933f4d28023cd597c9a1f6760346adf')) && xOnlyRes.parity === 1);
xOnlyRes = ecc.xOnlyPointAddTweak(h('2c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991'), h('823c3cd2142744b075a87eade7e1b8678ba308d566226a0056ca2b7a76f86b47'));
}
- assert(Buffer.from(ecc.pointAddScalar(h('0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'), h('0000000000000000000000000000000000000000000000000000000000000003'))).equals(h('02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5')));
- assert(Buffer.from(ecc.privateAdd(h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e'), h('0000000000000000000000000000000000000000000000000000000000000002'))).equals(h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140')));
+ assert((0, uint8array_utils_1.areUint8ArraysEqual)(ecc.pointAddScalar(h('0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'), h('0000000000000000000000000000000000000000000000000000000000000003')), h('02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5')));
+ assert((0, uint8array_utils_1.areUint8ArraysEqual)(ecc.privateAdd(h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e'), h('0000000000000000000000000000000000000000000000000000000000000002')), h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140')));
if (ecc.privateNegate) {
- assert(Buffer.from(ecc.privateNegate(h('0000000000000000000000000000000000000000000000000000000000000001'))).equals(h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140')));
- assert(Buffer.from(ecc.privateNegate(h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e'))).equals(h('0000000000000000000000000000000000000000000000000000000000000003')));
- assert(Buffer.from(ecc.privateNegate(h('b1121e4088a66a28f5b6b0f5844943ecd9f610196d7bb83b25214b60452c09af'))).equals(h('4eede1bf775995d70a494f0a7bb6bc11e0b8cccd41cce8009ab1132c8b0a3792')));
+ assert((0, uint8array_utils_1.areUint8ArraysEqual)(ecc.privateNegate(h('0000000000000000000000000000000000000000000000000000000000000001')), h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140')));
+ assert((0, uint8array_utils_1.areUint8ArraysEqual)(ecc.privateNegate(h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e')), h('0000000000000000000000000000000000000000000000000000000000000003')));
+ assert((0, uint8array_utils_1.areUint8ArraysEqual)(ecc.privateNegate(h('b1121e4088a66a28f5b6b0f5844943ecd9f610196d7bb83b25214b60452c09af')), h('4eede1bf775995d70a494f0a7bb6bc11e0b8cccd41cce8009ab1132c8b0a3792')));
}
- assert(Buffer.from(ecc.sign(h('5e9f0a0d593efdcf78ac923bc3313e4e7d408d574354ee2b3288c0da9fbba6ed'), h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140'))).equals(h('54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5')));
+ assert((0, uint8array_utils_1.areUint8ArraysEqual)(ecc.sign(h('5e9f0a0d593efdcf78ac923bc3313e4e7d408d574354ee2b3288c0da9fbba6ed'), h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140')), h('54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5')));
assert(ecc.verify(h('5e9f0a0d593efdcf78ac923bc3313e4e7d408d574354ee2b3288c0da9fbba6ed'), h('0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'), h('54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5')));
if (ecc.signSchnorr) {
- assert(Buffer.from(ecc.signSchnorr(h('7e2d58d8b3bcdf1abadec7829054f90dda9805aab56c77333024b9d0a508b75c'), h('c90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b14e5c9'), h('c87aa53824b4d7ae2eb035a2b5bbbccc080e76cdc6d1692c4b0b62d798e6d906'))).equals(h('5831aaeed7b44bb74e5eab94ba9d4294c49bcf2a60728d8b4c200f50dd313c1bab745879a5ad954a72c45a91c3a51d3c7adea98d82f8481e0e1e03674a6f3fb7')));
+ assert((0, uint8array_utils_1.areUint8ArraysEqual)(ecc.signSchnorr(h('7e2d58d8b3bcdf1abadec7829054f90dda9805aab56c77333024b9d0a508b75c'), h('c90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b14e5c9'), h('c87aa53824b4d7ae2eb035a2b5bbbccc080e76cdc6d1692c4b0b62d798e6d906')), h('5831aaeed7b44bb74e5eab94ba9d4294c49bcf2a60728d8b4c200f50dd313c1bab745879a5ad954a72c45a91c3a51d3c7adea98d82f8481e0e1e03674a6f3fb7')));
}
if (ecc.verifySchnorr) {
assert(ecc.verifySchnorr(h('7e2d58d8b3bcdf1abadec7829054f90dda9805aab56c77333024b9d0a508b75c'), h('dd308afec5777e13121fa72b9cc1b7cc0139715309b086c960e18fd969774eb8'), h('5831aaeed7b44bb74e5eab94ba9d4294c49bcf2a60728d8b4c200f50dd313c1bab745879a5ad954a72c45a91c3a51d3c7adea98d82f8481e0e1e03674a6f3fb7')));
diff --git a/src/uint8array-utils.js b/src/uint8array-utils.js
new file mode 100644
index 0000000..a6e67d9
--- /dev/null
+++ b/src/uint8array-utils.js
@@ -0,0 +1,50 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.areUint8ArraysEqual = exports.Uint8ArrayTypeN = exports.Uint8ArrayType = void 0;
+const typeforce = require('typeforce');
+/**
+ * Typeforce extensions
+ */
+function tfCustomError(expected, actual) {
+ return new typeforce.TfTypeError(expected, {}, actual);
+}
+function _LengthN(type, length) {
+ const name = type.toJSON();
+ function Length(value) {
+ if (!type(value))
+ return false;
+ if (value.length === length)
+ return true;
+ throw tfCustomError(name + '(Length: ' + length + ')', name + '(Length: ' + value.length + ')');
+ }
+ Length.toJSON = () => {
+ return name;
+ };
+ return Length;
+}
+function Uint8ArrayType(value) {
+ return value instanceof Uint8Array;
+}
+exports.Uint8ArrayType = Uint8ArrayType;
+Uint8ArrayType.toJSON = ((t) => {
+ return t;
+}).bind(null, 'Uint8Array');
+exports.Uint8ArrayTypeN = _LengthN.bind(null, Uint8ArrayType);
+/**
+ * Uint8Array comparison
+ */
+function areUint8ArraysEqual(a, b) {
+ if (a === b) {
+ return true;
+ }
+ if (a.length !== b.length) {
+ return false;
+ }
+ for (let index = 0; index < a.length; index++) {
+ if (a[index] !== b[index]) {
+ return false;
+ }
+ }
+ return true;
+}
+exports.areUint8ArraysEqual = areUint8ArraysEqual;
diff --git a/test/index.js b/test/index.js
index fa9a60d..174ba8d 100644
--- a/test/index.js
+++ b/test/index.js
@@ -1,4 +1,5 @@
let BIP32Creator = require('..').default
+let { bytesToHex, hexToBytes } = require('@noble/hashes/utils')
let tape = require('tape')
let fixtures = require('./fixtures/index.json')
let ecc
@@ -29,17 +30,15 @@ fixtures.valid.forEach((f) => {
})
function verify (t, hd, prv, f, network) {
- t.equal(hd.chainCode.toString('hex'), f.chainCode)
+ t.equal(bytesToHex(hd.chainCode), f.chainCode)
t.equal(hd.depth, f.depth >>> 0)
t.equal(hd.index, f.index >>> 0)
t.equal(hd.compressed, true)
- t.equal(hd.fingerprint.toString('hex'), f.fingerprint)
- t.equal(hd.identifier.toString('hex'), f.identifier)
- t.equal(hd.publicKey.toString('hex'), f.pubKey)
+ t.equal(bytesToHex(hd.fingerprint), f.fingerprint)
+ t.equal(bytesToHex(hd.identifier), f.identifier)
+ t.equal(bytesToHex(hd.publicKey), f.pubKey)
if (prv) t.equal(hd.toBase58(), f.base58Priv)
- if (prv) t.equal(hd.privateKey.toString('hex'), f.privKey)
- if (prv) t.equal(hd.toWIF(), f.wif)
- if (!prv) t.throws(() => hd.toWIF(), /Missing private key/)
+ if (prv) t.equal(bytesToHex(hd.privateKey), f.privKey)
if (!prv) t.equal(hd.privateKey, undefined)
t.equal(hd.neutered().toBase58(), f.base58)
t.equal(hd.isNeutered(), !prv)
@@ -90,7 +89,7 @@ validAll.forEach((ff) => {
verify(t, hd, false, ff, network)
if (ff.seed) {
- let seed = Buffer.from(ff.seed, 'hex')
+ let seed = hexToBytes(ff.seed)
hd = BIP32.fromSeed(seed, network)
verify(t, hd, true, ff, network)
}
@@ -153,7 +152,7 @@ tape('works for Public -> public', (t) => {
t.plan(2)
t.equal(c.base58, child.toBase58())
- const hdNeutered = BIP32.fromPublicKey(Buffer.from(f.master.pubKey, 'hex'), Buffer.from(f.master.chainCode, 'hex'))
+ const hdNeutered = BIP32.fromPublicKey(hexToBytes(f.master.pubKey), hexToBytes(f.master.chainCode))
t.equal(child.toBase58(), hdNeutered.derive(c.m).toBase58())
})
@@ -191,12 +190,12 @@ tape('throws on wrong types', (t) => {
}, /Expected BIP32Path, got/)
})
- let ZERO = Buffer.alloc(32, 0)
- let ONES = Buffer.alloc(32, 1)
+ let ZERO = new Uint8Array(32).fill(0);
+ let ONES = new Uint8Array(32).fill(1);
t.throws(() => {
- BIP32.fromPrivateKey(Buffer.alloc(2), ONES)
- }, /Expected property "privateKey" of type Buffer\(Length: 32\), got Buffer\(Length: 2\)/)
+ BIP32.fromPrivateKey(new Uint8Array(2), ONES)
+ }, /Expected property "privateKey" of type Uint8Array\(Length: 32\), got Uint8Array\(Length: 2\)/)
t.throws(() => {
BIP32.fromPrivateKey(ZERO, ONES)
@@ -210,9 +209,9 @@ tape('works when private key has leading zeros', (t) => {
let hdkey = BIP32.fromBase58(key)
t.plan(2)
- t.equal(hdkey.privateKey.toString('hex'), '00000055378cf5fafb56c711c674143f9b0ee82ab0ba2924f19b64f5ae7cdbfd')
+ t.equal(bytesToHex(hdkey.privateKey), '00000055378cf5fafb56c711c674143f9b0ee82ab0ba2924f19b64f5ae7cdbfd')
let child = hdkey.derivePath('m/44\'/0\'/0\'/0/0\'')
- t.equal(child.privateKey.toString('hex'), '3348069561d2a0fb925e74bf198762acc47dce7db27372257d2d959a9e6f8aeb')
+ t.equal(bytesToHex(child.privateKey), '3348069561d2a0fb925e74bf198762acc47dce7db27372257d2d959a9e6f8aeb')
})
tape('fromSeed', (t) => {
@@ -220,7 +219,7 @@ tape('fromSeed', (t) => {
// 'throws if IL is not within interval [1, n - 1] | IL === n || IL === 0'
fixtures.invalid.fromSeed.forEach((f) => {
t.throws(() => {
- BIP32.fromSeed(Buffer.from(f.seed, 'hex'))
+ BIP32.fromSeed(hexToBytes(f.seed))
}, new RegExp(f.exception))
})
@@ -228,17 +227,17 @@ tape('fromSeed', (t) => {
})
tape('ecdsa', (t) => {
- let seed = Buffer.alloc(32, 1)
- let hash = Buffer.alloc(32, 2)
- let signature = Buffer.from('9636ee2fac31b795a308856b821ebe297dda7b28220fb46ea1fbbd7285977cc04c82b734956246a0f15a9698f03f546d8d96fe006c8e7bd2256ca7c8229e6f5c', 'hex')
- let schnorrsig = Buffer.from('2fae8b517cb0e7302ca48a4109d1819e3d75af96bd58d297023e3058c4e98ff812fe6ae32a2b2bc4abab10f88f7fe56efbafc8a4e4fa437af78926f528b0585e', 'hex')
- let signatureLowR = Buffer.from('0587a40b391b76596c257bf59565b24eaff2cc42b45caa2640902e73fb97a6e702c3402ab89348a7dae1bf171c3e172fa60353d7b01621a94cb7caca59b995db', 'hex')
+ let seed = new Uint8Array(32).fill(1)
+ let hash = new Uint8Array(32).fill(2)
+ let signature = hexToBytes('9636ee2fac31b795a308856b821ebe297dda7b28220fb46ea1fbbd7285977cc04c82b734956246a0f15a9698f03f546d8d96fe006c8e7bd2256ca7c8229e6f5c')
+ let schnorrsig = hexToBytes('2fae8b517cb0e7302ca48a4109d1819e3d75af96bd58d297023e3058c4e98ff812fe6ae32a2b2bc4abab10f88f7fe56efbafc8a4e4fa437af78926f528b0585e')
+ let signatureLowR = hexToBytes('0587a40b391b76596c257bf59565b24eaff2cc42b45caa2640902e73fb97a6e702c3402ab89348a7dae1bf171c3e172fa60353d7b01621a94cb7caca59b995db')
let node = BIP32.fromSeed(seed)
t.plan(11)
- t.equal(node.sign(hash).toString('hex'), signature.toString('hex'))
- t.equal(node.sign(hash, true).toString('hex'), signatureLowR.toString('hex'))
- t.equal(node.signSchnorr(hash).toString('hex'), schnorrsig.toString('hex'))
+ t.equal(bytesToHex(node.sign(hash)), bytesToHex(signature))
+ t.equal(bytesToHex(node.sign(hash, true)), bytesToHex(signatureLowR))
+ t.equal(bytesToHex(node.signSchnorr(hash)), bytesToHex(schnorrsig))
t.equal(node.verify(hash, signature), true)
t.equal(node.verify(seed, signature), false)
t.equal(node.verify(hash, signatureLowR), true)
@@ -252,9 +251,9 @@ tape('ecdsa', (t) => {
})
tape('ecdsa - no schnorr', (t) => {
- let seed = Buffer.alloc(32, 1)
- let hash = Buffer.alloc(32, 2)
- const tweak = Buffer.alloc(32, 3)
+ let seed = new Uint8Array(32).fill(1)
+ let hash = new Uint8Array(32).fill(2)
+ const tweak = new Uint8Array(32).fill(3)
const bip32NoSchnorr = BIP32Creator({ ...ecc, signSchnorr: null, verifySchnorr: null })
const node = bip32NoSchnorr.fromSeed(seed)
@@ -272,8 +271,8 @@ tape('ecdsa - no schnorr', (t) => {
})
tape('ecc without tweak support', (t) => {
- let seed = Buffer.alloc(32, 1)
- const tweak = Buffer.alloc(32, 3)
+ let seed = new Uint8Array(32).fill(1)
+ const tweak = new Uint8Array(32).fill(3)
const bip32NoTweak = BIP32Creator({ ...ecc, xOnlyPointAddTweak: null, privateNegate: null })
const node = bip32NoTweak.fromSeed(seed)
@@ -285,18 +284,18 @@ tape('ecc without tweak support', (t) => {
})
tape('tweak', (t) => {
- const seed = Buffer.alloc(32, 1)
- const hash = Buffer.alloc(32, 2)
- const tweak = Buffer.alloc(32, 3)
- const signature = Buffer.from('5a38c6652feb5166c9c91cfa5fa4a4c7cec27445d4619499df8afdd05ebc823246d644b0c7d3b960625393df537f900528ec4b14e6ddab8fd0c7e87c98cfe9d0', 'hex')
- const schnorrsig = Buffer.from('20506478d341d0ab1afd32671eb1550b1c5329ad5179a19712212b857f06b3210d949964cd513ff25719e2e9b0087d5a9745afd5d38641ce0dfa86f67c86de63', 'hex')
- const signatureLowR = Buffer.from('5a38c6652feb5166c9c91cfa5fa4a4c7cec27445d4619499df8afdd05ebc823246d644b0c7d3b960625393df537f900528ec4b14e6ddab8fd0c7e87c98cfe9d0', 'hex')
+ const seed = new Uint8Array(32).fill(1)
+ const hash = new Uint8Array(32).fill(2)
+ const tweak = new Uint8Array(32).fill(3)
+ const signature = hexToBytes('5a38c6652feb5166c9c91cfa5fa4a4c7cec27445d4619499df8afdd05ebc823246d644b0c7d3b960625393df537f900528ec4b14e6ddab8fd0c7e87c98cfe9d0')
+ const schnorrsig = hexToBytes('20506478d341d0ab1afd32671eb1550b1c5329ad5179a19712212b857f06b3210d949964cd513ff25719e2e9b0087d5a9745afd5d38641ce0dfa86f67c86de63')
+ const signatureLowR = hexToBytes('5a38c6652feb5166c9c91cfa5fa4a4c7cec27445d4619499df8afdd05ebc823246d644b0c7d3b960625393df537f900528ec4b14e6ddab8fd0c7e87c98cfe9d0')
const signer = BIP32.fromSeed(seed).tweak(tweak)
t.plan(9)
- t.equal(signer.sign(hash).toString('hex'), signature.toString('hex'))
- t.equal(signer.sign(hash, true).toString('hex'), signatureLowR.toString('hex'))
- t.equal(signer.signSchnorr(hash).toString('hex'), schnorrsig.toString('hex'))
+ t.equal(bytesToHex(signer.sign(hash)), bytesToHex(signature))
+ t.equal(bytesToHex(signer.sign(hash, true)), bytesToHex(signatureLowR))
+ t.equal(bytesToHex(signer.signSchnorr(hash)), bytesToHex(schnorrsig))
t.equal(signer.verify(hash, signature), true)
t.equal(signer.verify(seed, signature), false)
t.equal(signer.verify(hash, signatureLowR), true)
@@ -306,18 +305,18 @@ tape('tweak', (t) => {
})
tape('tweak - neutered', (t) => {
- const seed = Buffer.alloc(32, 1)
- const hash = Buffer.alloc(32, 2)
- const tweak = Buffer.alloc(32, 3)
- const signature = Buffer.from('5a38c6652feb5166c9c91cfa5fa4a4c7cec27445d4619499df8afdd05ebc823246d644b0c7d3b960625393df537f900528ec4b14e6ddab8fd0c7e87c98cfe9d0', 'hex')
- const schnorrsig = Buffer.from('20506478d341d0ab1afd32671eb1550b1c5329ad5179a19712212b857f06b3210d949964cd513ff25719e2e9b0087d5a9745afd5d38641ce0dfa86f67c86de63', 'hex')
- const signatureLowR = Buffer.from('5a38c6652feb5166c9c91cfa5fa4a4c7cec27445d4619499df8afdd05ebc823246d644b0c7d3b960625393df537f900528ec4b14e6ddab8fd0c7e87c98cfe9d0', 'hex')
+ const seed = new Uint8Array(32).fill(1)
+ const hash = new Uint8Array(32).fill(2)
+ const tweak = new Uint8Array(32).fill(3)
+ const signature = hexToBytes('5a38c6652feb5166c9c91cfa5fa4a4c7cec27445d4619499df8afdd05ebc823246d644b0c7d3b960625393df537f900528ec4b14e6ddab8fd0c7e87c98cfe9d0')
+ const schnorrsig = hexToBytes('20506478d341d0ab1afd32671eb1550b1c5329ad5179a19712212b857f06b3210d949964cd513ff25719e2e9b0087d5a9745afd5d38641ce0dfa86f67c86de63')
+ const signatureLowR = hexToBytes('5a38c6652feb5166c9c91cfa5fa4a4c7cec27445d4619499df8afdd05ebc823246d644b0c7d3b960625393df537f900528ec4b14e6ddab8fd0c7e87c98cfe9d0')
const signer = BIP32.fromSeed(seed).neutered().tweak(tweak)
t.plan(8)
t.throws(() => signer.sign(hash), /Missing private key/)
t.throws(() => signer.signSchnorr(hash), /Missing private key/)
-
+
t.equal(signer.verify(hash, signature), true)
t.equal(signer.verify(seed, signature), false)
t.equal(signer.verify(hash, signatureLowR), true)
diff --git a/ts-src/bip32.ts b/ts-src/bip32.ts
index 85f88ad..1020307 100644
--- a/ts-src/bip32.ts
+++ b/ts-src/bip32.ts
@@ -2,13 +2,9 @@ import * as crypto from './crypto';
import { testEcc } from './testecc';
import { base58check } from '@scure/base';
import { sha256 } from '@noble/hashes/sha256';
+import { Uint8ArrayType, Uint8ArrayTypeN } from './uint8array-utils';
const typeforce = require('typeforce');
-const wif = require('wif');
-const _bs58check = base58check(sha256);
-const bs58check = {
- encode: (data: Buffer): string => _bs58check.encode(Uint8Array.from(data)),
- decode: (str: string): Buffer => Buffer.from(_bs58check.decode(str)),
-};
+const bs58check = base58check(sha256);
interface Network {
wif: number;
@@ -22,43 +18,42 @@ 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,
+ publicKey: Uint8Array,
+ chainCode: Uint8Array,
network?: Network,
): BIP32Interface;
fromPrivateKey(
- privateKey: Buffer,
- chainCode: Buffer,
+ privateKey: Uint8Array,
+ chainCode: Uint8Array,
network?: Network,
): BIP32Interface;
}
@@ -96,7 +91,7 @@ export interface TinySecp256k1Interface {
export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
testEcc(ecc);
- const UINT256_TYPE = typeforce.BufferN(32);
+ const UINT256_TYPE = Uint8ArrayTypeN(32);
const NETWORK_TYPE = typeforce.compile({
wif: typeforce.UInt8,
bip32: {
@@ -130,60 +125,61 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
return typeforce.UInt32(value) && value <= UINT31_MAX;
}
- function toXOnly(pubKey: Buffer) {
- return pubKey.length === 32 ? pubKey : pubKey.slice(1, 33);
+ function toXOnly(pubKey: Uint8Array) {
+ return pubKey.length === 32 ? pubKey : pubKey.subarray(1, 33);
}
class Bip32Signer implements Signer {
lowR: boolean = false;
constructor(
- protected __D: Buffer | undefined,
- protected __Q: Buffer | undefined,
+ protected __D: Uint8Array | undefined,
+ protected __Q: Uint8Array | undefined,
) {}
- get publicKey(): Buffer {
+ get publicKey(): Uint8Array {
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(): Buffer | undefined {
+ get privateKey(): Uint8Array | undefined {
return this.__D;
}
- sign(hash: Buffer, lowR?: boolean): Buffer {
+ sign(hash: Uint8Array, lowR?: boolean): Uint8Array {
if (!this.privateKey) throw new Error('Missing private key');
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);
+ const extraDataView = new DataView(extraData.buffer);
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));
+ extraDataView.setUint32(0, counter, true);
+ sig = ecc.sign(hash, this.privateKey, extraData);
}
return sig;
}
}
- signSchnorr(hash: Buffer): Buffer {
+ signSchnorr(hash: Uint8Array): Uint8Array {
if (!this.privateKey) 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: Buffer, signature: Buffer): boolean {
+ verify(hash: Uint8Array, signature: Uint8Array): boolean {
return ecc.verify(hash, this.publicKey, signature);
}
- verifySchnorr(hash: Buffer, signature: Buffer): boolean {
+ verifySchnorr(hash: Uint8Array, signature: Uint8Array): boolean {
if (!ecc.verifySchnorr)
throw new Error('verifySchnorr not supported by ecc library');
return ecc.verifySchnorr(hash, this.publicKey.subarray(1, 33), signature);
@@ -192,9 +188,9 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
class BIP32 extends Bip32Signer implements BIP32Interface {
constructor(
- __D: Buffer | undefined,
- __Q: Buffer | undefined,
- public chainCode: Buffer,
+ __D: Uint8Array | undefined,
+ __Q: Uint8Array | undefined,
+ public chainCode: Uint8Array,
public network: Network,
private __DEPTH = 0,
private __INDEX = 0,
@@ -216,12 +212,12 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
return this.__PARENT_FINGERPRINT;
}
- get identifier(): Buffer {
+ get identifier(): Uint8Array {
return crypto.hash160(this.publicKey);
}
- get fingerprint(): Buffer {
- return this.identifier.slice(0, 4);
+ get fingerprint(): Uint8Array {
+ return this.identifier.subarray(0, 4);
}
get compressed(): boolean {
@@ -250,50 +246,47 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
const version = !this.isNeutered()
? network.bip32.private
: network.bip32.public;
- const buffer = Buffer.allocUnsafe(78);
+ const buffer = new Uint8Array(78);
+ const bufferView = new DataView(buffer.buffer);
// 4 bytes: version bytes
- buffer.writeUInt32BE(version, 0);
+ bufferView.setUint32(0, version, false);
// 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ....
- buffer.writeUInt8(this.depth, 4);
+ bufferView.setUint8(4, this.depth);
// 4 bytes: the fingerprint of the parent's key (0x00000000 if master key)
- buffer.writeUInt32BE(this.parentFingerprint, 5);
+ bufferView.setUint32(5, this.parentFingerprint, false);
// 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);
+ bufferView.setUint32(9, this.index, false);
// 32 bytes: the chain code
- 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);
+ bufferView.setUint8(45, 0);
+ buffer.set(this.privateKey!, 46);
// 33 bytes: the public key
} else {
// X9.62 encoding for public keys
- this.publicKey.copy(buffer, 45);
+ buffer.set(this.publicKey, 45);
}
return bs58check.encode(buffer);
}
- toWIF(): string {
- if (!this.privateKey) throw new TypeError('Missing private key');
- return wif.encode(this.network.wif, this.privateKey, true);
- }
-
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#child-key-derivation-ckd-functions
derive(index: number): BIP32Interface {
typeforce(typeforce.UInt32, index);
const isHardened = index >= HIGHEST_BIT;
- const data = Buffer.allocUnsafe(37);
+ const data = new Uint8Array(37);
+ const dataView = new DataView(data.buffer);
// Hardened child
if (isHardened) {
@@ -302,15 +295,15 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
// data = 0x00 || ser256(kpar) || ser32(index)
data[0] = 0x00;
- this.privateKey!.copy(data, 1);
- data.writeUInt32BE(index, 33);
+ data.set(this.privateKey!, 1);
+ dataView.setUint32(33, index, false);
// Normal child
} else {
// data = serP(point(kpar)) || ser32(index)
// = serP(Kpar) || ser32(index)
- this.publicKey.copy(data, 0);
- data.writeUInt32BE(index, 33);
+ data.set(this.publicKey, 0);
+ dataView.setUint32(33, index, false);
}
const I = crypto.hmacSHA512(this.chainCode, data);
@@ -324,7 +317,7 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
let hd: BIP32Interface;
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);
@@ -335,14 +328,14 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
this.network,
this.depth + 1,
index,
- this.fingerprint.readUInt32BE(0),
+ new DataView(this.fingerprint.buffer).getUint32(0, false),
);
// 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);
@@ -353,7 +346,7 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
this.network,
this.depth + 1,
index,
- this.fingerprint.readUInt32BE(0),
+ new DataView(this.fingerprint.buffer).getUint32(0, false),
);
}
@@ -393,30 +386,32 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
);
}
- tweak(t: Buffer): Signer {
+ tweak(t: Uint8Array): Signer {
if (this.privateKey) return this.tweakFromPrivateKey(t);
return this.tweakFromPublicKey(t);
}
- private tweakFromPublicKey(t: Buffer): Signer {
+ private tweakFromPublicKey(t: Uint8Array): Signer {
const xOnlyPubKey = toXOnly(this.publicKey);
if (!ecc.xOnlyPointAddTweak)
throw new Error('xOnlyPointAddTweak not supported by ecc library');
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([
- parityByte,
- tweakedPublicKey.xOnlyPubkey,
- ]);
+
+ const tweakedPublicKeyCompresed = new Uint8Array(
+ tweakedPublicKey.xOnlyPubkey.length + 1,
+ );
+ tweakedPublicKeyCompresed.set(parityByte);
+ tweakedPublicKeyCompresed.set(tweakedPublicKey.xOnlyPubkey, 1);
return new Bip32Signer(undefined, tweakedPublicKeyCompresed);
}
- private tweakFromPrivateKey(t: Buffer): Signer {
+ private tweakFromPrivateKey(t: Uint8Array): Signer {
const hasOddY =
this.publicKey[0] === 3 ||
(this.publicKey[0] === 4 && (this.publicKey[64] & 1) === 1);
@@ -429,17 +424,18 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
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: string, network?: Network): BIP32Interface {
const buffer = bs58check.decode(inString);
+ const bufferView = new DataView(buffer.buffer);
if (buffer.length !== 78) throw new TypeError('Invalid buffer length');
network = network || BITCOIN;
// 4 bytes: version bytes
- const version = buffer.readUInt32BE(0);
+ const version = bufferView.getUint32(0, false);
if (version !== network.bip32.private && version !== network.bip32.public)
throw new TypeError('Invalid network version');
@@ -447,7 +443,7 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
const depth = buffer[4];
// 4 bytes: the fingerprint of the parent's key (0x00000000 if master key)
- const parentFingerprint = buffer.readUInt32BE(5);
+ const parentFingerprint = bufferView.getUint32(5, false);
if (depth === 0) {
if (parentFingerprint !== 0x00000000)
throw new TypeError('Invalid parent fingerprint');
@@ -455,18 +451,18 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
// 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 = bufferView.getUint32(9, false);
if (depth === 0 && index !== 0) throw new TypeError('Invalid index');
// 32 bytes: the chain code
- const chainCode = buffer.slice(13, 45);
+ const chainCode = buffer.subarray(13, 45);
let hd: BIP32Interface;
// 33 bytes: private key data (0x00 + k)
if (version === network.bip32.private) {
- if (buffer.readUInt8(45) !== 0x00)
+ if (bufferView.getUint8(45) !== 0x00)
throw new TypeError('Invalid private key');
- const k = buffer.slice(46, 78);
+ const k = buffer.subarray(46, 78);
hd = fromPrivateKeyLocal(
k,
@@ -479,7 +475,7 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
// 33 bytes: public key data (0x02 + X or 0x03 + X)
} else {
- const X = buffer.slice(45, 78);
+ const X = buffer.subarray(45, 78);
hd = fromPublicKeyLocal(
X,
@@ -495,16 +491,16 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
}
function fromPrivateKey(
- privateKey: Buffer,
- chainCode: Buffer,
+ privateKey: Uint8Array,
+ chainCode: Uint8Array,
network?: Network,
): BIP32Interface {
return fromPrivateKeyLocal(privateKey, chainCode, network);
}
function fromPrivateKeyLocal(
- privateKey: Buffer,
- chainCode: Buffer,
+ privateKey: Uint8Array,
+ chainCode: Uint8Array,
network?: Network,
depth?: number,
index?: number,
@@ -533,16 +529,16 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
}
function fromPublicKey(
- publicKey: Buffer,
- chainCode: Buffer,
+ publicKey: Uint8Array,
+ chainCode: Uint8Array,
network?: Network,
): BIP32Interface {
return fromPublicKeyLocal(publicKey, chainCode, network);
}
function fromPublicKeyLocal(
- publicKey: Buffer,
- chainCode: Buffer,
+ publicKey: Uint8Array,
+ chainCode: Uint8Array,
network?: Network,
depth?: number,
index?: number,
@@ -550,7 +546,7 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
): BIP32Interface {
typeforce(
{
- publicKey: typeforce.BufferN(33),
+ publicKey: Uint8ArrayTypeN(33),
chainCode: UINT256_TYPE,
},
{ publicKey, chainCode },
@@ -571,15 +567,17 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
);
}
- function fromSeed(seed: Buffer, network?: Network): BIP32Interface {
- typeforce(typeforce.Buffer, seed);
+ function fromSeed(seed: Uint8Array, network?: Network): BIP32Interface {
+ typeforce(Uint8ArrayType, seed);
if (seed.length < 16)
throw new TypeError('Seed should be at least 128 bits');
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 encoder = new TextEncoder();
+
+ const I = crypto.hmacSHA512(encoder.encode('Bitcoin seed'), seed);
const IL = I.slice(0, 32);
const IR = I.slice(32);
diff --git a/ts-src/crypto.ts b/ts-src/crypto.ts
index 00542e7..093c534 100644
--- a/ts-src/crypto.ts
+++ b/ts-src/crypto.ts
@@ -3,11 +3,10 @@ import { ripemd160 } from '@noble/hashes/ripemd160';
import { sha256 } from '@noble/hashes/sha256';
import { sha512 } from '@noble/hashes/sha512';
-export function hash160(buffer: Buffer): Buffer {
- const sha256Hash = sha256(Uint8Array.from(buffer));
- return Buffer.from(ripemd160(sha256Hash));
+export function hash160(buffer: Uint8Array): Uint8Array {
+ return ripemd160(sha256(buffer));
}
-export function hmacSHA512(key: Buffer, data: Buffer): Buffer {
- return Buffer.from(hmac(sha512, key, data));
+export function hmacSHA512(key: Uint8Array, data: Uint8Array): Uint8Array {
+ return hmac(sha512, key, data);
}
diff --git a/ts-src/testecc.ts b/ts-src/testecc.ts
index 666b868..94fcc2b 100644
--- a/ts-src/testecc.ts
+++ b/ts-src/testecc.ts
@@ -1,6 +1,8 @@
+import { hexToBytes } from '@noble/hashes/utils';
import { TinySecp256k1Interface } from './bip32';
+import { areUint8ArraysEqual } from './uint8array-utils';
-const h = (hex: string): Buffer => Buffer.from(hex, 'hex');
+const h = (hex: string): Uint8Array => hexToBytes(hex);
export function testEcc(ecc: TinySecp256k1Interface): void {
assert(
@@ -43,11 +45,10 @@ export function testEcc(ecc: TinySecp256k1Interface): void {
),
);
assert(
- Buffer.from(
+ areUint8ArraysEqual(
ecc.pointFromScalar(
h('b1121e4088a66a28f5b6b0f5844943ecd9f610196d7bb83b25214b60452c09af'),
)!,
- ).equals(
h('02b07ba9dca9523b7ef4bd97703d43d20399eb698e194704791a25ce77a400df99'),
),
);
@@ -64,7 +65,8 @@ export function testEcc(ecc: TinySecp256k1Interface): void {
h('a8397a935f0dfceba6ba9618f6451ef4d80637abf4e6af2669fbc9de6a8fd2ac'),
);
assert(
- Buffer.from(xOnlyRes!.xOnlyPubkey).equals(
+ areUint8ArraysEqual(
+ xOnlyRes!.xOnlyPubkey,
h('e478f99dab91052ab39a33ea35fd5e6e4933f4d28023cd597c9a1f6760346adf'),
) && xOnlyRes!.parity === 1,
);
@@ -75,61 +77,55 @@ export function testEcc(ecc: TinySecp256k1Interface): void {
);
}
assert(
- Buffer.from(
+ areUint8ArraysEqual(
ecc.pointAddScalar(
h('0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'),
h('0000000000000000000000000000000000000000000000000000000000000003'),
)!,
- ).equals(
h('02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5'),
),
);
assert(
- Buffer.from(
+ areUint8ArraysEqual(
ecc.privateAdd(
h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e'),
h('0000000000000000000000000000000000000000000000000000000000000002'),
)!,
- ).equals(
h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140'),
),
);
if (ecc.privateNegate) {
assert(
- Buffer.from(
+ areUint8ArraysEqual(
ecc.privateNegate(
h('0000000000000000000000000000000000000000000000000000000000000001'),
),
- ).equals(
h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140'),
),
);
assert(
- Buffer.from(
+ areUint8ArraysEqual(
ecc.privateNegate(
h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e'),
),
- ).equals(
h('0000000000000000000000000000000000000000000000000000000000000003'),
),
);
assert(
- Buffer.from(
+ areUint8ArraysEqual(
ecc.privateNegate(
h('b1121e4088a66a28f5b6b0f5844943ecd9f610196d7bb83b25214b60452c09af'),
),
- ).equals(
h('4eede1bf775995d70a494f0a7bb6bc11e0b8cccd41cce8009ab1132c8b0a3792'),
),
);
}
assert(
- Buffer.from(
+ areUint8ArraysEqual(
ecc.sign(
h('5e9f0a0d593efdcf78ac923bc3313e4e7d408d574354ee2b3288c0da9fbba6ed'),
h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140'),
)!,
- ).equals(
h(
'54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5',
),
@@ -146,13 +142,12 @@ export function testEcc(ecc: TinySecp256k1Interface): void {
);
if (ecc.signSchnorr) {
assert(
- Buffer.from(
+ areUint8ArraysEqual(
ecc.signSchnorr(
h('7e2d58d8b3bcdf1abadec7829054f90dda9805aab56c77333024b9d0a508b75c'),
h('c90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b14e5c9'),
h('c87aa53824b4d7ae2eb035a2b5bbbccc080e76cdc6d1692c4b0b62d798e6d906'),
)!,
- ).equals(
h(
'5831aaeed7b44bb74e5eab94ba9d4294c49bcf2a60728d8b4c200f50dd313c1bab745879a5ad954a72c45a91c3a51d3c7adea98d82f8481e0e1e03674a6f3fb7',
),
diff --git a/ts-src/uint8array-utils.ts b/ts-src/uint8array-utils.ts
new file mode 100644
index 0000000..f5ee282
--- /dev/null
+++ b/ts-src/uint8array-utils.ts
@@ -0,0 +1,58 @@
+const typeforce = require('typeforce');
+
+/**
+ * Typeforce extensions
+ */
+function tfCustomError(expected: any, actual: any) {
+ return new typeforce.TfTypeError(expected, {}, actual);
+}
+
+function _LengthN(type: any, length: number) {
+ const name = type.toJSON();
+
+ function Length(value: any) {
+ if (!type(value)) return false;
+ if (value.length === length) return true;
+
+ throw tfCustomError(
+ name + '(Length: ' + length + ')',
+ name + '(Length: ' + value.length + ')',
+ );
+ }
+ Length.toJSON = () => {
+ return name;
+ };
+
+ return Length;
+}
+
+export function Uint8ArrayType(value: unknown): value is Uint8Array {
+ return value instanceof Uint8Array;
+}
+
+Uint8ArrayType.toJSON = ((t: any) => {
+ return t;
+}).bind(null, 'Uint8Array');
+
+export const Uint8ArrayTypeN = _LengthN.bind(null, Uint8ArrayType);
+
+/**
+ * Uint8Array comparison
+ */
+export function areUint8ArraysEqual(a: Uint8Array, b: Uint8Array) {
+ if (a === b) {
+ return true;
+ }
+
+ if (a.length !== b.length) {
+ return false;
+ }
+
+ for (let index = 0; index < a.length; index++) {
+ if (a[index] !== b[index]) {
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/types/bip32.d.ts b/types/bip32.d.ts
index de1ccee..044571b 100644
--- a/types/bip32.d.ts
+++ b/types/bip32.d.ts
@@ -1,4 +1,3 @@
-///
interface Network {
wif: number;
bip32: {
@@ -11,36 +10,35 @@ 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;
diff --git a/types/crypto.d.ts b/types/crypto.d.ts
index 4b033af..f0bd050 100644
--- a/types/crypto.d.ts
+++ b/types/crypto.d.ts
@@ -1,3 +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;
diff --git a/types/uint8array-utils.d.ts b/types/uint8array-utils.d.ts
new file mode 100644
index 0000000..39c4abf
--- /dev/null
+++ b/types/uint8array-utils.d.ts
@@ -0,0 +1,12 @@
+export declare function Uint8ArrayType(value: unknown): value is Uint8Array;
+export declare namespace Uint8ArrayType {
+ var toJSON: () => any;
+}
+export declare const Uint8ArrayTypeN: (length: number) => {
+ (value: any): boolean;
+ toJSON(): any;
+};
+/**
+ * Uint8Array comparison
+ */
+export declare function areUint8ArraysEqual(a: Uint8Array, b: Uint8Array): boolean;