fix: downgrade tiny-secp256k1 and remove xOnlyPointFromPoint

This commit is contained in:
abhishandy 2022-12-11 11:39:23 -05:00
parent d75b408931
commit 103abd8eb5
13 changed files with 219 additions and 86 deletions

184
package-lock.json generated
View File

@ -15,7 +15,7 @@
"bs58check": "^2.1.1",
"create-hmac": "^1.1.7",
"ecpair": "^2.0.1",
"tiny-secp256k1": "^2.2.1"
"tiny-secp256k1": "^1.1.6"
},
"devDependencies": {
"@types/chai": "^4.3.0",
@ -643,6 +643,14 @@
"node": ">=8"
}
},
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"dependencies": {
"file-uri-to-path": "1.0.0"
}
},
"node_modules/bip174": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/bip174/-/bip174-2.0.1.tgz",
@ -710,6 +718,11 @@
"safe-buffer": "^5.1.2"
}
},
"node_modules/bn.js": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -720,6 +733,11 @@
"concat-map": "0.0.1"
}
},
"node_modules/brorand": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
"integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w=="
},
"node_modules/browser-stdout": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
@ -1169,6 +1187,20 @@
"integrity": "sha512-3Vftv7cenJtQb+k00McEBZ2vVmZ/x+HEF7pcZONZIkOsESqAqVuACmBxMv0JhzX7u0YltU0vSqRqgBSTAhFUjA==",
"dev": true
},
"node_modules/elliptic": {
"version": "6.5.4",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
"integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
"dependencies": {
"bn.js": "^4.11.9",
"brorand": "^1.1.0",
"hash.js": "^1.0.0",
"hmac-drbg": "^1.0.1",
"inherits": "^2.0.4",
"minimalistic-assert": "^1.0.1",
"minimalistic-crypto-utils": "^1.0.1"
}
},
"node_modules/es6-error": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
@ -1206,6 +1238,11 @@
"node": ">=4"
}
},
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
},
"node_modules/find-cache-dir": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
@ -1535,6 +1572,15 @@
"node": ">=4"
}
},
"node_modules/hash.js": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
"dependencies": {
"inherits": "^2.0.3",
"minimalistic-assert": "^1.0.1"
}
},
"node_modules/hasha": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz",
@ -1572,6 +1618,16 @@
"he": "bin/he"
}
},
"node_modules/hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
"integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==",
"dependencies": {
"hash.js": "^1.0.3",
"minimalistic-assert": "^1.0.0",
"minimalistic-crypto-utils": "^1.0.1"
}
},
"node_modules/html-escaper": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
@ -1988,6 +2044,16 @@
"safe-buffer": "^5.1.2"
}
},
"node_modules/minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
},
"node_modules/minimalistic-crypto-utils": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
"integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg=="
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -2356,6 +2422,11 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"node_modules/nan": {
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz",
"integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ=="
},
"node_modules/nanoid": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
@ -3165,14 +3236,19 @@
}
},
"node_modules/tiny-secp256k1": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-2.2.1.tgz",
"integrity": "sha512-/U4xfVqnVxJXN4YVsru0E6t5wVncu2uunB8+RVR40fYUxkKYUPS10f+ePQZgFBoE/Jbf9H1NBveupF2VmB58Ng==",
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz",
"integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==",
"hasInstallScript": true,
"dependencies": {
"uint8array-tools": "0.0.7"
"bindings": "^1.3.0",
"bn.js": "^4.11.8",
"create-hmac": "^1.1.7",
"elliptic": "^6.4.0",
"nan": "^2.13.2"
},
"engines": {
"node": ">=14.0.0"
"node": ">=6.0.0"
}
},
"node_modules/to-fast-properties": {
@ -3390,14 +3466,6 @@
"node": ">=4.2.0"
}
},
"node_modules/uint8array-tools": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/uint8array-tools/-/uint8array-tools-0.0.7.tgz",
"integrity": "sha512-vrrNZJiusLWoFWBqz5Y5KMCgP9W9hnjZHzZiZRT8oNAkq3d5Z5Oe76jAvVVSRh4U8GGR90N2X1dWtrhvx6L8UQ==",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -4006,6 +4074,14 @@
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true
},
"bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"requires": {
"file-uri-to-path": "1.0.0"
}
},
"bip174": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/bip174/-/bip174-2.0.1.tgz",
@ -4068,6 +4144,11 @@
}
}
},
"bn.js": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -4078,6 +4159,11 @@
"concat-map": "0.0.1"
}
},
"brorand": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
"integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w=="
},
"browser-stdout": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
@ -4432,6 +4518,20 @@
"integrity": "sha512-3Vftv7cenJtQb+k00McEBZ2vVmZ/x+HEF7pcZONZIkOsESqAqVuACmBxMv0JhzX7u0YltU0vSqRqgBSTAhFUjA==",
"dev": true
},
"elliptic": {
"version": "6.5.4",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
"integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
"requires": {
"bn.js": "^4.11.9",
"brorand": "^1.1.0",
"hash.js": "^1.0.0",
"hmac-drbg": "^1.0.1",
"inherits": "^2.0.4",
"minimalistic-assert": "^1.0.1",
"minimalistic-crypto-utils": "^1.0.1"
}
},
"es6-error": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
@ -4456,6 +4556,11 @@
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
},
"find-cache-dir": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
@ -4688,6 +4793,15 @@
"safe-buffer": "^5.2.0"
}
},
"hash.js": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
"requires": {
"inherits": "^2.0.3",
"minimalistic-assert": "^1.0.1"
}
},
"hasha": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz",
@ -4712,6 +4826,16 @@
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true
},
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
"integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==",
"requires": {
"hash.js": "^1.0.3",
"minimalistic-assert": "^1.0.0",
"minimalistic-crypto-utils": "^1.0.1"
}
},
"html-escaper": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
@ -5021,6 +5145,16 @@
"safe-buffer": "^5.1.2"
}
},
"minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
},
"minimalistic-crypto-utils": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
"integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg=="
},
"minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -5284,6 +5418,11 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"nan": {
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz",
"integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ=="
},
"nanoid": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
@ -5885,11 +6024,15 @@
"dev": true
},
"tiny-secp256k1": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-2.2.1.tgz",
"integrity": "sha512-/U4xfVqnVxJXN4YVsru0E6t5wVncu2uunB8+RVR40fYUxkKYUPS10f+ePQZgFBoE/Jbf9H1NBveupF2VmB58Ng==",
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz",
"integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==",
"requires": {
"uint8array-tools": "0.0.7"
"bindings": "^1.3.0",
"bn.js": "^4.11.8",
"create-hmac": "^1.1.7",
"elliptic": "^6.4.0",
"nan": "^2.13.2"
}
},
"to-fast-properties": {
@ -6040,11 +6183,6 @@
"integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==",
"dev": true
},
"uint8array-tools": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/uint8array-tools/-/uint8array-tools-0.0.7.tgz",
"integrity": "sha512-vrrNZJiusLWoFWBqz5Y5KMCgP9W9hnjZHzZiZRT8oNAkq3d5Z5Oe76jAvVVSRh4U8GGR90N2X1dWtrhvx6L8UQ=="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",

View File

@ -40,7 +40,7 @@
"bs58check": "^2.1.1",
"create-hmac": "^1.1.7",
"ecpair": "^2.0.1",
"tiny-secp256k1": "^2.2.1"
"tiny-secp256k1": "^1.1.6"
},
"devDependencies": {
"@types/chai": "^4.3.0",

View File

@ -12,7 +12,7 @@ function BIP47Factory(ecc) {
// TODO: implement a test assertion function for ecc
const bip32 = (0, bip32_1.default)(ecc);
const G = Buffer.from('0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798', 'hex');
const { getPublicPaymentCodeNodeFromBase58, getRootPaymentCodeNodeFromSeedHex, getRootPaymentCodeNodeFromBIP39Seed, uintArrayToBuffer, getSharedSecret, toInternalByteOrder } = (0, utils_1.default)(ecc, bip32);
const { getPublicPaymentCodeNodeFromBase58, getRootPaymentCodeNodeFromSeedHex, getRootPaymentCodeNodeFromBIP39Seed, uintArrayToBuffer, getSharedSecret, toInternalByteOrder, } = (0, utils_1.default)(ecc, bip32);
class BIP47 {
constructor(network, RootPaymentCodeNode) {
this.network = network;
@ -105,7 +105,7 @@ function BIP47Factory(ecc) {
const a = privateKey;
const B = bobBIP47.getNotificationNode().publicKey;
const S = uintArrayToBuffer(ecc.pointMultiply(B, a));
const x = uintArrayToBuffer(ecc.xOnlyPointFromPoint(S));
const x = uintArrayToBuffer(S.slice(1, 33));
const o = outpoint;
const s = crypto.hmacSHA512(o, x);
const binaryPaymentCode = this.getBinaryPaymentCode();
@ -144,9 +144,9 @@ function BIP47Factory(ecc) {
const A = pubKey;
const b = this.getNotificationNode().privateKey;
const S = uintArrayToBuffer(ecc.pointMultiply(A, b));
const x = uintArrayToBuffer(ecc.xOnlyPointFromPoint(S));
const x = uintArrayToBuffer(S.slice(1, 33));
const s = crypto.hmacSHA512(outpoint, x);
const opReturnOutput = tx.outs.find(o => o.script.toString('hex').startsWith('6a4c50'));
const opReturnOutput = tx.outs.find((o) => o.script.toString('hex').startsWith('6a4c50'));
if (!opReturnOutput)
throw new Error('No OP_RETURN output in notification');
const binaryPaymentCode = opReturnOutput.script.slice(3);

View File

@ -26,7 +26,7 @@ function getUtils(ecc, bip32) {
return b;
};
const getSharedSecret = (B, a) => {
const S = uintArrayToBuffer(ecc.xOnlyPointFromPoint(ecc.pointMultiply(B, a, true)));
const S = uintArrayToBuffer(ecc.pointMultiply(B, a, true).slice(1, 33));
let s = bitcoin.crypto.sha256(S);
if (!ecc.isPrivate(s))
throw new Error('Shared secret is not a valid private key');

View File

@ -1,8 +1,8 @@
import { BIP32Interface } from 'bip32';
import { expect } from 'chai';
import ECPairFactory from 'ecpair';
import * as ecc from 'tiny-secp256k1';
import BIP47Factory from '../ts-src';
const ecc = require('tiny-secp256k1');
const ECPair = ECPairFactory(ecc);
@ -57,7 +57,7 @@ const privateKeysAliceToBobWallets = [
'f103fe818377ff6040eb9aac10677384bb1ca6ff7f9d33e5a6306c3e402daec9',
'5bd0be3fa5ae87ea97301b1e637915478efb45afcfe64133954eae93a29553a0',
'0c4d3825253bc7a28a83097cfaf8affd9924d9a9ac921dbb9ce20a684253d349',
'118951327db87b267c2ed385631cda9c2905078cff8e63a053dc586d624c7899'
'118951327db87b267c2ed385631cda9c2905078cff8e63a053dc586d624c7899',
];
describe('Payment codes and notification addresses', () => {
@ -84,15 +84,17 @@ describe('Payment codes and notification addresses', () => {
});
describe('Payment Addresses and Private keys', () => {
it('should generate alice to bob payment addresses from Alice\'s node', () => {
it("should generate alice to bob payment addresses from Alice's node", () => {
const aliceBip47 = BIP47Factory(ecc).fromBip39Seed(alice.seedPhrase);
const bobBip47 = BIP47Factory(ecc).fromPaymentCode(bob.paymentCode);
const bobPaymentCodeNode: BIP32Interface = bobBip47.getPaymentCodeNode();
for (let i = 0; i < aliceToBobAddresses.length; i++)
expect(aliceBip47.getPaymentAddress(bobPaymentCodeNode, i)).to.equal(aliceToBobAddresses[i]);
expect(aliceBip47.getPaymentAddress(bobPaymentCodeNode, i)).to.equal(
aliceToBobAddresses[i],
);
});
it('Should generate alice to bob payment addresses and private keys from Bob\'s node', () => {
it("Should generate alice to bob payment addresses and private keys from Bob's node", () => {
const bobBip47 = BIP47Factory(ecc).fromBip39Seed(bob.seedPhrase);
const aliceBip47 = BIP47Factory(ecc).fromPaymentCode(alice.paymentCode);
const alicePaymentNode: BIP32Interface = aliceBip47.getPaymentCodeNode();
@ -102,15 +104,19 @@ describe('Payment Addresses and Private keys', () => {
alicePaymentNode,
i,
);
expect(bobBip47.getAddressFromNode(wallet, bobBip47.network)).to.equal(aliceToBobAddresses[i])
expect(bobBip47.getAddressFromNode(wallet, bobBip47.network)).to.equal(
aliceToBobAddresses[i],
);
// check private key
expect(wallet.privateKey?.toString('hex')).to.equal(privateKeysAliceToBobWallets[i]);
expect(wallet.privateKey?.toString('hex')).to.equal(
privateKeysAliceToBobWallets[i],
);
}
});
});
describe('Notification Transaction and blinded payment code exchange', () => {
it('should generate Alice\'s blinded payment code for Bob', () => {
it("should generate Alice's blinded payment code for Bob", () => {
const aliceBip47 = BIP47Factory(ecc).fromBip39Seed(alice.seedPhrase);
const bobBip47 = BIP47Factory(ecc).fromPaymentCode(bob.paymentCode);
const keyPair = ECPair.fromWIF(alice.privateKeyWIF);
@ -123,13 +129,12 @@ describe('Notification Transaction and blinded payment code exchange', () => {
expect(blindedPaymentCode).to.equal(alice.blindedPaymentCode);
});
it('Bob should be able to retrieve Alice\'s payment code, from Alice\'s notification transaction', () => {
it("Bob should be able to retrieve Alice's payment code, from Alice's notification transaction", () => {
const bobBip47 = BIP47Factory(ecc).fromBip39Seed(bob.seedPhrase);
const p = bobBip47.getPaymentCodeFromRawNotificationTransaction(
aliceToBobRawNotificationHex,
);
expect(p).to.equal(alice.paymentCode);
})
});
});

View File

@ -3,7 +3,13 @@ import * as bitcoin from 'bitcoinjs-lib';
import * as crypto from './crypto';
import { xor } from './xor';
import { BIP47API, BIP47Interface, NetworkCoin, PublicKeyOutpoint, TinySecp256k1Interface } from './interfaces';
import {
BIP47API,
BIP47Interface,
NetworkCoin,
PublicKeyOutpoint,
TinySecp256k1Interface,
} from './interfaces';
import { mainnetData } from './networks';
import getUtils from './utils';
const bs58check = require('bs58check');
@ -23,7 +29,7 @@ export function BIP47Factory(ecc: TinySecp256k1Interface): BIP47API {
getRootPaymentCodeNodeFromBIP39Seed,
uintArrayToBuffer,
getSharedSecret,
toInternalByteOrder
toInternalByteOrder,
} = getUtils(ecc, bip32);
class BIP47 implements BIP47Interface {
@ -35,27 +41,27 @@ export function BIP47Factory(ecc: TinySecp256k1Interface): BIP47API {
this.RootPaymentCodeNode = RootPaymentCodeNode;
}
getPaymentWallet(
aliceNode: BIP32Interface,
index: number,
): BIP32Interface {
getPaymentWallet(aliceNode: BIP32Interface, index: number): BIP32Interface {
if (!this.network || !this.RootPaymentCodeNode)
throw new Error('Root Payment code node or network not set');
const bobNode: BIP32Interface = this.RootPaymentCodeNode.derive(index);
if (bobNode.privateKey === undefined)
throw new Error('Missing private key to generate payment wallets')
throw new Error('Missing private key to generate payment wallets');
const firstAliceNode: BIP32Interface = aliceNode.derive(0);
const s: Buffer = getSharedSecret(
firstAliceNode.publicKey,
bobNode.privateKey,
);
const prvKeyUint8: Uint8Array | null = ecc.privateAdd(bobNode.privateKey, s);
const prvKeyUint8: Uint8Array | null = ecc.privateAdd(
bobNode.privateKey,
s,
);
if (prvKeyUint8 === null)
throw new Error('Could not calculate private key')
throw new Error('Could not calculate private key');
const prvKey: Buffer = uintArrayToBuffer(prvKeyUint8);
return bip32.fromPrivateKey(
@ -81,26 +87,23 @@ export function BIP47Factory(ecc: TinySecp256k1Interface): BIP47API {
bobsRootPaymentCodeNode.derive(index);
if (firstAlicePaymentCodeNode.privateKey === undefined)
throw new Error('Missing private key to generate payment address')
throw new Error('Missing private key to generate payment address');
const a: Buffer = firstAlicePaymentCodeNode.privateKey;
const B: Buffer = bobPaymentCodeNode.publicKey;
const s = getSharedSecret(B, a);
const sGUint: Uint8Array | null = ecc.pointMultiply(G, s, true);
if (sGUint === null)
throw new Error('Could not compute sG')
if (sGUint === null) throw new Error('Could not compute sG');
const sG: Buffer = uintArrayToBuffer(sGUint);
const BPrimeUint: Uint8Array | null = ecc.pointAdd(B, sG, true);
if (BPrimeUint === null)
throw new Error('Could not calculate pubkey');
if (BPrimeUint === null) throw new Error('Could not calculate pubkey');
const BPrime: Buffer = uintArrayToBuffer(BPrimeUint);
if (!ecc.isPoint(BPrime))
throw new Error('Calculate Pubkey is invalid');
if (!ecc.isPoint(BPrime)) throw new Error('Calculate Pubkey is invalid');
const node: BIP32Interface = bip32.fromPublicKey(
BPrime,
@ -175,7 +178,7 @@ export function BIP47Factory(ecc: TinySecp256k1Interface): BIP47API {
const B: Buffer = bobBIP47.getNotificationNode().publicKey;
const S: Buffer = uintArrayToBuffer(ecc.pointMultiply(B, a) as Buffer);
const x: Buffer = uintArrayToBuffer(ecc.xOnlyPointFromPoint(S) as Buffer);
const x: Buffer = uintArrayToBuffer(S.slice(1, 33));
const o: Buffer = outpoint;
const s = crypto.hmacSHA512(o, x);
@ -238,14 +241,15 @@ export function BIP47Factory(ecc: TinySecp256k1Interface): BIP47API {
const A: Buffer = pubKey;
const b: Buffer = this.getNotificationNode().privateKey as Buffer;
const S: Buffer = uintArrayToBuffer(ecc.pointMultiply(A, b) as Buffer);
const x: Buffer = uintArrayToBuffer(ecc.xOnlyPointFromPoint(S));
const x: Buffer = uintArrayToBuffer(S.slice(1, 33));
const s = crypto.hmacSHA512(outpoint, x);
const opReturnOutput = tx.outs.find(o =>
const opReturnOutput = tx.outs.find((o) =>
o.script.toString('hex').startsWith('6a4c50'),
);
if (!opReturnOutput) throw new Error('No OP_RETURN output in notification');
if (!opReturnOutput)
throw new Error('No OP_RETURN output in notification');
const binaryPaymentCode: Buffer = opReturnOutput.script.slice(3);

View File

@ -1,5 +1,5 @@
export { BIP47Factory as default, BIP47Factory } from './bip47';
export {
export type {
NetworkCoin,
TinySecp256k1Interface,
BIP47Interface,

View File

@ -30,8 +30,6 @@ export interface TinySecp256k1Interface extends TinySecp256k1InterfaceBIP32 {
pB: Uint8Array,
compressed?: boolean,
): Uint8Array | null;
xOnlyPointFromPoint(p: Uint8Array): Uint8Array;
}
export interface BIP47Interface {

View File

@ -47,13 +47,12 @@ export default function getUtils(ecc: TinySecp256k1Interface, bip32: BIP32API) {
const getSharedSecret = (B: Buffer, a: Buffer): Buffer => {
const S: Buffer = uintArrayToBuffer(
ecc.xOnlyPointFromPoint(
ecc.pointMultiply(B, a, true) as Buffer
))
(ecc.pointMultiply(B, a, true) as Buffer).slice(1, 33),
);
let s: Buffer = bitcoin.crypto.sha256(S);
if (!ecc.isPrivate(s))
throw new Error('Shared secret is not a valid private key')
throw new Error('Shared secret is not a valid private key');
return s;
};
@ -62,7 +61,7 @@ export default function getUtils(ecc: TinySecp256k1Interface, bip32: BIP32API) {
let start = 0;
let length = data.length;
while(length - start >= 1) {
while (length - start >= 1) {
const tmp = data[start];
const lastIndex = length - 1;
data[start] = data[lastIndex];
@ -71,8 +70,7 @@ export default function getUtils(ecc: TinySecp256k1Interface, bip32: BIP32API) {
start++;
}
return data;
}
};
return {
getPublicPaymentCodeNodeFromBase58,
getRootPaymentCodeNodeFromSeedHex,

View File

@ -5,9 +5,7 @@
"declaration": true,
"declarationDir": "./types",
"esModuleInterop": false,
"lib": [
"es2017"
],
"lib": ["es2017"],
"module": "commonjs",
"noImplicitAny": true,
"noImplicitThis": true,
@ -19,16 +17,8 @@
"strictNullChecks": true,
"strictPropertyInitialization": true,
"target": "es2017",
"types": [
"@types/mocha",
"node"
]
"types": ["@types/mocha", "node"]
},
"include": [
"ts-src/*.ts"
],
"exclude": [
"**/*.spec.ts",
"node_modules/**/*"
]
"include": ["ts-src/*.ts", "types/tiny-secp256k1.d.ts"],
"exclude": ["**/*.spec.ts", "node_modules/**/*"]
}

2
types/index.d.ts vendored
View File

@ -1,3 +1,3 @@
export { BIP47Factory as default, BIP47Factory } from './bip47';
export { NetworkCoin, TinySecp256k1Interface, BIP47Interface, BIP47API, PublicKeyOutpoint, } from './interfaces';
export type { NetworkCoin, TinySecp256k1Interface, BIP47Interface, BIP47API, PublicKeyOutpoint, } from './interfaces';
export { testnetData, mainnetData } from './networks';

View File

@ -15,7 +15,6 @@ export interface TinySecp256k1Interface extends TinySecp256k1InterfaceBIP32 {
privateAdd(d: Uint8Array, tweak: Uint8Array): Uint8Array | null;
pointMultiply(p: Uint8Array, tweak: Uint8Array, compressed?: boolean): Uint8Array | null;
pointAdd(pA: Uint8Array, pB: Uint8Array, compressed?: boolean): Uint8Array | null;
xOnlyPointFromPoint(p: Uint8Array): Uint8Array;
}
export interface BIP47Interface {
network: NetworkCoin;

1
types/tiny-secp256k1.d.ts vendored Normal file
View File

@ -0,0 +1 @@
declare module 'tiny-secp256k1';