Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to 4.x #2

Merged
merged 13 commits into from
Jan 26, 2024
1,239 changes: 744 additions & 495 deletions package-lock.json

Large diffs are not rendered by default.

13 changes: 5 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bip32grs",
"version": "3.1.0",
"version": "4.0.0",
"description": "A BIP32 compatible library",
"keywords": [
"bip32grs",
Expand Down Expand Up @@ -37,22 +37,19 @@
"types"
],
"dependencies": {
"bs58grscheck": "^2.1.2",
"create-hash": "^1.2.0",
"create-hmac": "^1.1.7",
"ripemd160": "^2.0.2",
"bs58grscheck": "^3.0.1",
"hashes-grs": "^1.2.0",
"typeforce": "^1.11.5",
"wifgrs": "^2.0.6"
},
"devDependencies": {
"@types/node": "10.12.18",
"@types/ripemd160": "^2.0.0",
"@types/node": "18.x",
"nyc": "^15.0.0",
"prettier": "1.16.4",
"tape": "^4.13.2",
"tiny-secp256k1": "^2.2.1",
"tslint": "^6.1.0",
"typescript": "3.3.3333"
"typescript": "4.7"
},
"author": "Groestlcoin Developers",
"license": "MIT",
Expand Down
22 changes: 17 additions & 5 deletions src/bip32.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BIP32Factory = void 0;
const crypto = require("./crypto");
const testecc_1 = require("./testecc");
const bs58grscheck = require('bs58grscheck');
const _bs58grscheck = require('bs58grscheck');
const typeforce = require('typeforce');
const wif = require('wifgrs');
const bs58grscheck = {
encode: (data) => _bs58grscheck.encode(Uint8Array.from(data)),
decode: (str) => Buffer.from(_bs58grscheck.decode(str)),
};
function BIP32Factory(ecc) {
testecc_1.testEcc(ecc);
(0, testecc_1.testEcc)(ecc);
const UINT256_TYPE = typeforce.BufferN(32);
const NETWORK_TYPE = typeforce.compile({
wif: typeforce.UInt8,
Expand Down Expand Up @@ -241,6 +246,8 @@ function BIP32Factory(ecc) {
}
tweakFromPublicKey(t) {
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!');
Expand All @@ -256,9 +263,14 @@ function BIP32Factory(ecc) {
tweakFromPrivateKey(t) {
const hasOddY = this.publicKey[0] === 3 ||
(this.publicKey[0] === 4 && (this.publicKey[64] & 1) === 1);
const privateKey = hasOddY
? ecc.privateNegate(this.privateKey)
: this.privateKey;
const privateKey = (() => {
if (!hasOddY)
return this.privateKey;
else if (!ecc.privateNegate)
throw new Error('privateNegate not supported by ecc library');
else
return ecc.privateNegate(this.privateKey);
})();
const tweakedPrivateKey = ecc.privateAdd(privateKey, t);
if (!tweakedPrivateKey)
throw new Error('Invalid tweaked private key!');
Expand Down
32 changes: 8 additions & 24 deletions src/crypto.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const RipeMd160 = require("ripemd160");
const createHash = require('create-hash');
const createHmac = require('create-hmac');
exports.hmacSHA512 = exports.hash160 = void 0;
const hmac_1 = require("hashes-grs/hmac");
const ripemd160_1 = require("hashes-grs/ripemd160");
const sha256_1 = require("hashes-grs/sha256");
const sha512_1 = require("hashes-grs/sha512");
function hash160(buffer) {
const sha256Hash = createHash('sha256')
.update(buffer)
.digest();
try {
return createHash('rmd160')
.update(sha256Hash)
.digest();
}
catch (err) {
try {
return createHash('ripemd160')
.update(sha256Hash)
.digest();
}
catch (err2) {
return new RipeMd160().update(sha256Hash).digest();
}
}
const sha256Hash = (0, sha256_1.sha256)(Uint8Array.from(buffer));
return Buffer.from((0, ripemd160_1.ripemd160)(sha256Hash));
}
exports.hash160 = hash160;
function hmacSHA512(key, data) {
return createHmac('sha512', key)
.update(data)
.digest();
return Buffer.from((0, hmac_1.hmac)(sha512_1.sha512, key, data));
}
exports.hmacSHA512 = hmacSHA512;
5 changes: 3 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BIP32Factory = exports.default = void 0;
var bip32_1 = require("./bip32");
exports.default = bip32_1.BIP32Factory;
exports.BIP32Factory = bip32_1.BIP32Factory;
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return bip32_1.BIP32Factory; } });
Object.defineProperty(exports, "BIP32Factory", { enumerable: true, get: function () { return bip32_1.BIP32Factory; } });
19 changes: 12 additions & 7 deletions src/testecc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.testEcc = void 0;
const h = (hex) => Buffer.from(hex, 'hex');
function testEcc(ecc) {
assert(ecc.isPoint(h('0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')));
Expand All @@ -14,15 +15,19 @@ function testEcc(ecc) {
// order + 1
assert(!ecc.isPrivate(h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142')));
assert(Buffer.from(ecc.pointFromScalar(h('b1121e4088a66a28f5b6b0f5844943ecd9f610196d7bb83b25214b60452c09af'))).equals(h('02b07ba9dca9523b7ef4bd97703d43d20399eb698e194704791a25ce77a400df99')));
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);
xOnlyRes = ecc.xOnlyPointAddTweak(h('2c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991'), h('823c3cd2142744b075a87eade7e1b8678ba308d566226a0056ca2b7a76f86b47'));
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);
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(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')));
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(Buffer.from(ecc.sign(h('5e9f0a0d593efdcf78ac923bc3313e4e7d408d574354ee2b3288c0da9fbba6ed'), h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140'))).equals(h('54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5')));
assert(ecc.verify(h('5e9f0a0d593efdcf78ac923bc3313e4e7d408d574354ee2b3288c0da9fbba6ed'), h('0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'), h('54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5')));
if (ecc.signSchnorr) {
Expand Down
13 changes: 13 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,19 @@ tape('ecdsa - no schnorr', (t) => {
t.throws(() => signer.verifySchnorr(hash), /verifySchnorr not supported by ecc library/)
})

tape('ecc without tweak support', (t) => {
let seed = Buffer.alloc(32, 1)
const tweak = Buffer.alloc(32, 3)

const bip32NoTweak = BIP32Creator({ ...ecc, xOnlyPointAddTweak: null, privateNegate: null })
const node = bip32NoTweak.fromSeed(seed)
const nodeWithoutPrivKey = bip32NoTweak.fromPublicKey(node.publicKey, node.chainCode)

t.plan(2)
t.throws(() => node.tweak(tweak), /privateNegate not supported by ecc library/)
t.throws(() => nodeWithoutPrivKey.tweak(tweak), /xOnlyPointAddTweak not supported by ecc library/)
})

tape('tweak', (t) => {
const seed = Buffer.alloc(32, 1)
const hash = Buffer.alloc(32, 2)
Expand Down
22 changes: 15 additions & 7 deletions ts-src/bip32.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import * as crypto from './crypto';
import { testEcc } from './testecc';
const bs58grscheck = require('bs58grscheck');
const _bs58grscheck = require('bs58grscheck');
const typeforce = require('typeforce');
const wif = require('wifgrs');
const bs58grscheck = {
encode: (data: Buffer): string => _bs58grscheck.encode(Uint8Array.from(data)),
decode: (str: string): Buffer => Buffer.from(_bs58grscheck.decode(str)),
};

interface Network {
wif: number;
Expand Down Expand Up @@ -81,11 +85,11 @@ export interface TinySecp256k1Interface {
strict?: boolean,
): boolean;
verifySchnorr?(h: Uint8Array, Q: Uint8Array, signature: Uint8Array): boolean;
xOnlyPointAddTweak(
xOnlyPointAddTweak?(
p: Uint8Array,
tweak: Uint8Array,
): XOnlyPointAddTweakResult | null;
privateNegate(d: Uint8Array): Uint8Array;
privateNegate?(d: Uint8Array): Uint8Array;
}

export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
Expand Down Expand Up @@ -394,6 +398,8 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {

private tweakFromPublicKey(t: Buffer): 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!');
Expand All @@ -412,10 +418,12 @@ export function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API {
const hasOddY =
this.publicKey[0] === 3 ||
(this.publicKey[0] === 4 && (this.publicKey[64] & 1) === 1);
const privateKey = hasOddY
? ecc.privateNegate(this.privateKey!)
: this.privateKey;

const privateKey = (() => {
if (!hasOddY) return this.privateKey;
else if (!ecc.privateNegate)
throw new Error('privateNegate not supported by ecc library');
else return ecc.privateNegate(this.privateKey!);
})();
const tweakedPrivateKey = ecc.privateAdd(privateKey!, t);
if (!tweakedPrivateKey) throw new Error('Invalid tweaked private key!');

Expand Down
29 changes: 7 additions & 22 deletions ts-src/crypto.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,13 @@
import * as RipeMd160 from 'ripemd160';
const createHash = require('create-hash');
const createHmac = require('create-hmac');
import { hmac } from 'hashes-grs/hmac';
import { ripemd160 } from 'hashes-grs/ripemd160';
import { sha256 } from 'hashes-grs/sha256';
import { sha512 } from 'hashes-grs/sha512';

export function hash160(buffer: Buffer): Buffer {
const sha256Hash: Buffer = createHash('sha256')
.update(buffer)
.digest();
try {
return createHash('rmd160')
.update(sha256Hash)
.digest();
} catch (err) {
try {
return createHash('ripemd160')
.update(sha256Hash)
.digest();
} catch (err2) {
return new RipeMd160().update(sha256Hash).digest();
}
}
const sha256Hash = sha256(Uint8Array.from(buffer));
return Buffer.from(ripemd160(sha256Hash));
}

export function hmacSHA512(key: Buffer, data: Buffer): Buffer {
return createHmac('sha512', key)
.update(data)
.digest();
return Buffer.from(hmac(sha512, key, data));
}
90 changes: 47 additions & 43 deletions ts-src/testecc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,27 +51,29 @@ export function testEcc(ecc: TinySecp256k1Interface): void {
h('02b07ba9dca9523b7ef4bd97703d43d20399eb698e194704791a25ce77a400df99'),
),
);
assert(
ecc.xOnlyPointAddTweak(
h('79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'),
h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140'),
) === null,
);
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,
);
let xOnlyRes = ecc.xOnlyPointAddTweak(
h('1617d38ed8d8657da4d4761e8057bc396ea9e4b9d29776d4be096016dbd2509b'),
h('a8397a935f0dfceba6ba9618f6451ef4d80637abf4e6af2669fbc9de6a8fd2ac'),
);
assert(
Buffer.from(xOnlyRes!.xOnlyPubkey).equals(
h('e478f99dab91052ab39a33ea35fd5e6e4933f4d28023cd597c9a1f6760346adf'),
) && xOnlyRes!.parity === 1,
);

xOnlyRes = ecc.xOnlyPointAddTweak(
h('2c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991'),
h('823c3cd2142744b075a87eade7e1b8678ba308d566226a0056ca2b7a76f86b47'),
);
xOnlyRes = ecc.xOnlyPointAddTweak(
h('2c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991'),
h('823c3cd2142744b075a87eade7e1b8678ba308d566226a0056ca2b7a76f86b47'),
);
}
assert(
Buffer.from(
ecc.pointAddScalar(
Expand All @@ -92,33 +94,35 @@ export function testEcc(ecc: TinySecp256k1Interface): void {
h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140'),
),
);
assert(
Buffer.from(
ecc.privateNegate(
h('0000000000000000000000000000000000000000000000000000000000000001'),
if (ecc.privateNegate) {
assert(
Buffer.from(
ecc.privateNegate(
h('0000000000000000000000000000000000000000000000000000000000000001'),
),
).equals(
h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140'),
),
).equals(
h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140'),
),
);
assert(
Buffer.from(
ecc.privateNegate(
h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e'),
);
assert(
Buffer.from(
ecc.privateNegate(
h('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e'),
),
).equals(
h('0000000000000000000000000000000000000000000000000000000000000003'),
),
).equals(
h('0000000000000000000000000000000000000000000000000000000000000003'),
),
);
assert(
Buffer.from(
ecc.privateNegate(
h('b1121e4088a66a28f5b6b0f5844943ecd9f610196d7bb83b25214b60452c09af'),
);
assert(
Buffer.from(
ecc.privateNegate(
h('b1121e4088a66a28f5b6b0f5844943ecd9f610196d7bb83b25214b60452c09af'),
),
).equals(
h('4eede1bf775995d70a494f0a7bb6bc11e0b8cccd41cce8009ab1132c8b0a3792'),
),
).equals(
h('4eede1bf775995d70a494f0a7bb6bc11e0b8cccd41cce8009ab1132c8b0a3792'),
),
);
);
}
assert(
Buffer.from(
ecc.sign(
Expand Down
4 changes: 2 additions & 2 deletions types/bip32.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ export interface TinySecp256k1Interface {
signSchnorr?(h: Uint8Array, d: Uint8Array, e?: Uint8Array): Uint8Array;
verify(h: Uint8Array, Q: Uint8Array, signature: Uint8Array, strict?: boolean): boolean;
verifySchnorr?(h: Uint8Array, Q: Uint8Array, signature: Uint8Array): boolean;
xOnlyPointAddTweak(p: Uint8Array, tweak: Uint8Array): XOnlyPointAddTweakResult | null;
privateNegate(d: Uint8Array): Uint8Array;
xOnlyPointAddTweak?(p: Uint8Array, tweak: Uint8Array): XOnlyPointAddTweakResult | null;
privateNegate?(d: Uint8Array): Uint8Array;
}
export declare function BIP32Factory(ecc: TinySecp256k1Interface): BIP32API;
export {};
Loading