Skip to content

Commit

Permalink
fix: replace secp256k1 with @noble/secp256k1
Browse files Browse the repository at this point in the history
  • Loading branch information
steveluscher committed Aug 25, 2022
1 parent b8a47d0 commit 2df2146
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 27 deletions.
66 changes: 52 additions & 14 deletions web3.js/package-lock.json

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

2 changes: 1 addition & 1 deletion web3.js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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"
},
Expand Down
11 changes: 6 additions & 5 deletions web3.js/src/programs/secp256k1.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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,
Expand Down
18 changes: 18 additions & 0 deletions web3.js/src/utils/secp256k1.ts
Original file line number Diff line number Diff line change
@@ -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<typeof secp256k1.signSync>[0],
privKey: Parameters<typeof secp256k1.signSync>[1],
) => secp256k1.signSync(msgHash, privKey, {der: false, recovered: true});
export const isValidPrivateKey = secp256k1.utils.isValidPrivateKey;
export const publicKeyCreate = secp256k1.getPublicKey;
21 changes: 14 additions & 7 deletions web3.js/test/program-tests/secp256k1.test.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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');
Expand All @@ -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'),
Expand All @@ -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'),
Expand All @@ -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,
Expand All @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions web3.js/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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/[email protected]":
version "2.1.5"
resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz"
Expand Down

0 comments on commit 2df2146

Please sign in to comment.