diff --git a/web3.js/package-lock.json b/web3.js/package-lock.json index beb1c70daa8d8a..0d15fe154757de 100644 --- a/web3.js/package-lock.json +++ b/web3.js/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@babel/runtime": "^7.12.5", "@noble/hashes": "^1.1.2", + "@noble/secp256k1": "^1.6.3", "@solana/buffer-layout": "^4.0.0", "bigint-buffer": "^1.1.5", "bn.js": "^5.0.0", @@ -22,7 +23,6 @@ "js-sha3": "^0.8.0", "node-fetch": "2", "rpc-websockets": "^7.5.0", - "secp256k1": "^4.0.2", "superstruct": "^0.14.2", "tweetnacl": "^1.0.3" }, @@ -2766,6 +2766,17 @@ } ] }, + "node_modules/@noble/secp256k1": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.6.3.tgz", + "integrity": "sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -4586,7 +4597,8 @@ "node_modules/brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true }, "node_modules/brotli-wasm": { "version": "1.1.0", @@ -5670,6 +5682,7 @@ "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -5683,7 +5696,8 @@ "node_modules/elliptic/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==" + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -7514,6 +7528,7 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, "dependencies": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" @@ -7557,6 +7572,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, "dependencies": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -7935,7 +7951,8 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "node_modules/ini": { "version": "1.3.8", @@ -9282,12 +9299,14 @@ "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==" + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true }, "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": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true }, "node_modules/minimatch": { "version": "3.1.2", @@ -9690,7 +9709,8 @@ "node_modules/node-addon-api": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", - "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", + "dev": true }, "node_modules/node-emoji": { "version": "1.11.0", @@ -9733,6 +9753,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", + "devOptional": true, "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -14500,6 +14521,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "dev": true, "hasInstallScript": true, "dependencies": { "elliptic": "^6.5.4", @@ -18553,6 +18575,11 @@ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz", "integrity": "sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==" }, + "@noble/secp256k1": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.6.3.tgz", + "integrity": "sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ==" + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -20005,7 +20032,8 @@ "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true }, "brotli-wasm": { "version": "1.1.0", @@ -20821,6 +20849,7 @@ "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, "requires": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -20834,7 +20863,8 @@ "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==" + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true } } }, @@ -22243,6 +22273,7 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, "requires": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" @@ -22276,6 +22307,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, "requires": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -22558,7 +22590,8 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "ini": { "version": "1.3.8", @@ -23585,12 +23618,14 @@ "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==" + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true }, "minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true }, "minimatch": { "version": "3.1.2", @@ -23917,7 +23952,8 @@ "node-addon-api": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", - "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", + "dev": true }, "node-emoji": { "version": "1.11.0", @@ -23945,7 +23981,8 @@ "node-gyp-build": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", - "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==" + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", + "devOptional": true }, "node-preload": { "version": "0.2.1", @@ -27478,6 +27515,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "dev": true, "requires": { "elliptic": "^6.5.4", "node-addon-api": "^2.0.0", diff --git a/web3.js/package.json b/web3.js/package.json index 77b98d62ff598f..fadd5e81b6a8e2 100644 --- a/web3.js/package.json +++ b/web3.js/package.json @@ -59,6 +59,7 @@ "dependencies": { "@babel/runtime": "^7.12.5", "@noble/hashes": "^1.1.2", + "@noble/secp256k1": "^1.6.3", "@solana/buffer-layout": "^4.0.0", "bigint-buffer": "^1.1.5", "bn.js": "^5.0.0", @@ -70,7 +71,6 @@ "js-sha3": "^0.8.0", "node-fetch": "2", "rpc-websockets": "^7.5.0", - "secp256k1": "^4.0.2", "superstruct": "^0.14.2", "tweetnacl": "^1.0.3" }, diff --git a/web3.js/src/programs/secp256k1.ts b/web3.js/src/programs/secp256k1.ts index b3a358d1886cc8..13bfa50cbfa08d 100644 --- a/web3.js/src/programs/secp256k1.ts +++ b/web3.js/src/programs/secp256k1.ts @@ -1,15 +1,13 @@ import {Buffer} from 'buffer'; import * as BufferLayout from '@solana/buffer-layout'; -import secp256k1 from 'secp256k1'; import sha3 from 'js-sha3'; import {PublicKey} from '../publickey'; import {TransactionInstruction} from '../transaction'; import assert from '../utils/assert'; +import {publicKeyCreate, ecdsaSign} from '../utils/secp256k1'; import {toBuffer} from '../utils/to-buffer'; -const {publicKeyCreate, ecdsaSign} = secp256k1; - const PRIVATE_KEY_BYTES = 32; const ETHEREUM_ADDRESS_BYTES = 20; const PUBLIC_KEY_BYTES = 64; @@ -209,11 +207,14 @@ export class Secp256k1Program { try { const privateKey = toBuffer(pkey); - const publicKey = publicKeyCreate(privateKey, false).slice(1); // throw away leading byte + const publicKey = publicKeyCreate( + privateKey, + false /* isCompressed */, + ).slice(1); // throw away leading byte const messageHash = Buffer.from( sha3.keccak_256.update(toBuffer(message)).digest(), ); - const {signature, recid: recoveryId} = ecdsaSign(messageHash, privateKey); + const [signature, recoveryId] = ecdsaSign(messageHash, privateKey); return this.createInstructionWithPublicKey({ publicKey, diff --git a/web3.js/src/utils/secp256k1.ts b/web3.js/src/utils/secp256k1.ts new file mode 100644 index 00000000000000..ccecaa33aa2a1a --- /dev/null +++ b/web3.js/src/utils/secp256k1.ts @@ -0,0 +1,18 @@ +import {hmac} from '@noble/hashes/hmac'; +import {sha256} from '@noble/hashes/sha256'; +import * as secp256k1 from '@noble/secp256k1'; + +// Supply a synchronous hashing algorithm to make this +// library interoperable with the synchronous APIs in web3.js. +secp256k1.utils.hmacSha256Sync = (key: Uint8Array, ...msgs: Uint8Array[]) => { + const h = hmac.create(sha256, key); + msgs.forEach(msg => h.update(msg)); + return h.digest(); +}; + +export const ecdsaSign = ( + msgHash: Parameters[0], + privKey: Parameters[1], +) => secp256k1.signSync(msgHash, privKey, {der: false, recovered: true}); +export const isValidPrivateKey = secp256k1.utils.isValidPrivateKey; +export const publicKeyCreate = secp256k1.getPublicKey; diff --git a/web3.js/test/program-tests/secp256k1.test.ts b/web3.js/test/program-tests/secp256k1.test.ts index b2def5e2ddacb8..d2e0d22adcf292 100644 --- a/web3.js/test/program-tests/secp256k1.test.ts +++ b/web3.js/test/program-tests/secp256k1.test.ts @@ -1,7 +1,11 @@ import {Buffer} from 'buffer'; import {keccak_256} from 'js-sha3'; -import {privateKeyVerify, ecdsaSign, publicKeyCreate} from 'secp256k1'; +import { + ecdsaSign, + isValidPrivateKey, + publicKeyCreate, +} from '../../src/utils/secp256k1'; import { Connection, Keypair, @@ -16,14 +20,17 @@ const randomPrivateKey = () => { let privateKey; do { privateKey = Keypair.generate().secretKey.slice(0, 32); - } while (!privateKeyVerify(privateKey)); + } while (!isValidPrivateKey(privateKey)); return privateKey; }; if (process.env.TEST_LIVE) { describe('secp256k1', () => { const privateKey = randomPrivateKey(); - const publicKey = publicKeyCreate(privateKey, false).slice(1); + const publicKey = publicKeyCreate( + privateKey, + false /* isCompressed */, + ).slice(1); const ethAddress = Secp256k1Program.publicKeyToEthAddress(publicKey); const from = Keypair.generate(); const connection = new Connection(url, 'confirmed'); @@ -37,7 +44,7 @@ if (process.env.TEST_LIVE) { it('create secp256k1 instruction with string address', async () => { const message = Buffer.from('string address'); const messageHash = Buffer.from(keccak_256.update(message).digest()); - const {signature, recid: recoveryId} = ecdsaSign(messageHash, privateKey); + const [signature, recoveryId] = ecdsaSign(messageHash, privateKey); const transaction = new Transaction().add( Secp256k1Program.createInstructionWithEthAddress({ ethAddress: ethAddress.toString('hex'), @@ -53,7 +60,7 @@ if (process.env.TEST_LIVE) { it('create secp256k1 instruction with 0x prefix string address', async () => { const message = Buffer.from('0x string address'); const messageHash = Buffer.from(keccak_256.update(message).digest()); - const {signature, recid: recoveryId} = ecdsaSign(messageHash, privateKey); + const [signature, recoveryId] = ecdsaSign(messageHash, privateKey); const transaction = new Transaction().add( Secp256k1Program.createInstructionWithEthAddress({ ethAddress: '0x' + ethAddress.toString('hex'), @@ -69,7 +76,7 @@ if (process.env.TEST_LIVE) { it('create secp256k1 instruction with buffer address', async () => { const message = Buffer.from('buffer address'); const messageHash = Buffer.from(keccak_256.update(message).digest()); - const {signature, recid: recoveryId} = ecdsaSign(messageHash, privateKey); + const [signature, recoveryId] = ecdsaSign(messageHash, privateKey); const transaction = new Transaction().add( Secp256k1Program.createInstructionWithEthAddress({ ethAddress, @@ -85,7 +92,7 @@ if (process.env.TEST_LIVE) { it('create secp256k1 instruction with public key', async () => { const message = Buffer.from('public key'); const messageHash = Buffer.from(keccak_256.update(message).digest()); - const {signature, recid: recoveryId} = ecdsaSign(messageHash, privateKey); + const [signature, recoveryId] = ecdsaSign(messageHash, privateKey); const transaction = new Transaction().add( Secp256k1Program.createInstructionWithPublicKey({ publicKey, diff --git a/web3.js/yarn.lock b/web3.js/yarn.lock index a50c77d0ec84cd..a83b4fe45cecdd 100644 --- a/web3.js/yarn.lock +++ b/web3.js/yarn.lock @@ -1350,6 +1350,11 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183" integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA== +"@noble/secp256k1@^1.6.3": + version "1.6.3" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.6.3.tgz#7eed12d9f4404b416999d0c87686836c4c5c9b94" + integrity sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz"