Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
Merge pull request #579 from EOSIO/elliptic2
Browse files Browse the repository at this point in the history
Replace eosjs-ecc with elliptic
  • Loading branch information
tbfleming authored Aug 9, 2019
2 parents d85dbee + bf7cf57 commit 3d6c39c
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 87 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
},
"dependencies": {
"babel-runtime": "6.26.0",
"eosjs-ecc": "4.0.4",
"text-encoding": "0.7.0"
},
"devDependencies": {
Expand Down
75 changes: 47 additions & 28 deletions src/eosjs-jssig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,12 @@
*/
// copyright defined in eosjs/LICENSE.txt

import * as ecc from 'eosjs-ecc';
import { SignatureProvider, SignatureProviderArgs } from './eosjs-api-interfaces';
import { convertLegacyPublicKey } from './eosjs-numeric';

function hexToUint8Array(hex: string) {
if (typeof hex !== 'string') {
throw new Error('Expected string containing hex digits');
}
if (hex.length % 2) {
throw new Error('Odd number of hex digits');
}
const l = hex.length / 2;
const result = new Uint8Array(l);
for (let i = 0; i < l; ++i) {
const x = parseInt(hex.substr(i * 2, 2), 16);
if (Number.isNaN(x)) {
throw new Error('Expected hex string');
}
result[i] = x;
}
return result;
}
import {
signatureToString, stringToPrivateKey, KeyType, convertLegacyPublicKey,
publicKeyToString
} from './eosjs-numeric';
import { ec } from 'elliptic';

/** Signs transactions using in-process private keys */
export class JsSignatureProvider implements SignatureProvider {
Expand All @@ -36,10 +20,22 @@ export class JsSignatureProvider implements SignatureProvider {

/** @param privateKeys private keys to sign with */
constructor(privateKeys: string[]) {
const e = new ec('secp256k1') as any;
for (const k of privateKeys) {
const pub = convertLegacyPublicKey(ecc.PrivateKey.fromString(k).toPublic().toString());
this.keys.set(pub, k);
this.availableKeys.push(pub);
const bin = stringToPrivateKey(k);
if (bin.type !== KeyType.k1) {
throw new Error('Key type isn\'t k1');
}
const priv = e.keyFromPrivate(bin.data);
const pub = priv.getPublic();
const x = pub.getX().toArray();
const y = pub.getY().toArray();
const pubStr = publicKeyToString({
type: KeyType.k1,
data: new Uint8Array([(y[31] & 1) ? 3 : 2].concat(x)),
});
this.keys.set(pubStr, priv);
this.availableKeys.push(pubStr);
}
}

Expand All @@ -52,18 +48,41 @@ export class JsSignatureProvider implements SignatureProvider {
public async sign(
{ chainId, requiredKeys, serializedTransaction, serializedContextFreeData }: SignatureProviderArgs
) {
const e = new ec('secp256k1') as any;
const signBuf = Buffer.concat([
new Buffer(chainId, 'hex'),
new Buffer(serializedTransaction),
new Buffer(
serializedContextFreeData ?
hexToUint8Array(ecc.sha256(serializedContextFreeData)) :
new Uint8Array(e.hash(serializedContextFreeData).update(serializedContextFreeData).digest()) :
new Uint8Array(32)
),
]);
const signatures = requiredKeys.map(
(pub) => ecc.Signature.sign(signBuf, this.keys.get(convertLegacyPublicKey(pub))).toString(),
);
const digest = e.hash().update(signBuf).digest();

const signatures = [] as string[];
for (const key of requiredKeys) {
const privKey = this.keys.get(convertLegacyPublicKey(key)) as any;
let tries = 0;
let sigData: Uint8Array;
const isCanonical = () =>
!(sigData[1] & 0x80) && !(sigData[1] === 0 && !(sigData[2] & 0x80))
&& !(sigData[33] & 0x80) && !(sigData[33] === 0 && !(sigData[34] & 0x80));

do {
const sig = privKey.sign(digest, { canonical: true, pers: [++tries] });
const r = sig.r.toArray();
const s = sig.s.toArray();
sigData = new Uint8Array([sig.recoveryParam + 27 + 4].concat(r, s));
} while (!isCanonical());

const sigStr = signatureToString({
type: KeyType.k1,
data: sigData,
});
signatures.push(sigStr);
}

return { signatures, serializedTransaction, serializedContextFreeData };
}
}
13 changes: 12 additions & 1 deletion src/eosjs-numeric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,18 @@ export function stringToPrivateKey(s: string): Key {
if (s.substr(0, 7) === 'PVT_R1_') {
return stringToKey(s.substr(7), KeyType.r1, privateKeyDataSize, 'R1');
} else {
throw new Error('unrecognized private key format');
// todo: Verify checksum: sha256(sha256(key.data)).
// Not critical since a bad key will fail to produce a
// valid signature anyway.
const whole = base58ToBinary(privateKeyDataSize + 5, s);
const key = { type: KeyType.k1, data: new Uint8Array(privateKeyDataSize) };
if (whole[0] !== 0x80) {
throw new Error('unrecognized private key type');
}
for (let i = 0; i < privateKeyDataSize; ++i) {
key.data[i] = whole[i + 1];
}
return key;
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/tests/eosjs-jssig.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as ecc from 'eosjs-ecc';
import { JsSignatureProvider } from '../eosjs-jssig';

describe('JsSignatureProvider', () => {
Expand All @@ -9,17 +8,22 @@ describe('JsSignatureProvider', () => {
'PUB_K1_5imfbmmHC83VRxLRTcvovviAc6LPpyszcDuKtkwka9e9Jg37Hp',
];

// These didn't test the correctness of signing. They also depend on the now-removed eosjs-ecc.

it('builds public keys from private when constructed', async () => {
/*
const eccPkFromString = jest.spyOn(ecc.PrivateKey, 'fromString');
eccPkFromString.mockImplementation((k) => ecc.PrivateKey.fromHex(ecc.sha256(k)));
const provider = new JsSignatureProvider(privateKeys);
const actualPublicKeys = await provider.getAvailableKeys();
expect(eccPkFromString).toHaveBeenCalledTimes(privateKeys.length);
expect(actualPublicKeys).toEqual(publicKeys);
*/
});

it('signs a transaction', async () => {
/*
const eccSignatureSign = jest.spyOn(ecc.Signature, 'sign');
eccSignatureSign.mockImplementation((buffer, signKey) => signKey);
Expand All @@ -38,5 +42,6 @@ describe('JsSignatureProvider', () => {
expect(eccSignatureSign).toHaveBeenCalledTimes(2);
expect(signOutput).toEqual({ signatures: [privateKeys[0], privateKeys[2]], serializedTransaction });
*/
});
});
59 changes: 3 additions & 56 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1310,13 +1310,6 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=

base-x@^3.0.2:
version "3.0.5"
resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.5.tgz#d3ada59afed05b921ab581ec3112e6444ba0795a"
integrity sha512-C3picSgzPSLE+jW3tcBzJoGwitOtazb5B+5YmAxZm2ybmTi9LNgAtDO/jjVEBZwHoXmDBZ9m/IELj3elJVRBcA==
dependencies:
safe-buffer "^5.0.1"

base64-js@^1.0.2:
version "1.3.0"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3"
Expand Down Expand Up @@ -1347,11 +1340,6 @@ big.js@^5.2.2:
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==

bigi@^1.1.0, bigi@^1.4.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/bigi/-/bigi-1.4.2.tgz#9c665a95f88b8b08fc05cfd731f561859d725825"
integrity sha1-nGZalfiLiwj8Bc/XMfVhhZ1yWCU=

binary-extensions@^1.0.0:
version "1.13.1"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
Expand Down Expand Up @@ -1420,7 +1408,7 @@ browser-stdout@1.3.1:
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==

browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.0.6:
browserify-aes@^1.0.0, browserify-aes@^1.0.4:
version "1.2.0"
resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==
Expand Down Expand Up @@ -1494,13 +1482,6 @@ bs-logger@0.x:
dependencies:
fast-json-stable-stringify "2.x"

bs58@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo=
dependencies:
base-x "^3.0.2"

bser@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719"
Expand Down Expand Up @@ -1542,13 +1523,6 @@ builtin-status-codes@^3.0.0:
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=

bytebuffer@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/bytebuffer/-/bytebuffer-5.0.1.tgz#582eea4b1a873b6d020a48d58df85f0bba6cfddd"
integrity sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=
dependencies:
long "~3"

cacache@^11.0.2:
version "11.3.2"
resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.2.tgz#2d81e308e3d258ca38125b676b98b2ac9ce69bfa"
Expand Down Expand Up @@ -1908,7 +1882,7 @@ create-ecdh@^4.0.0:
bn.js "^4.1.0"
elliptic "^6.0.0"

create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.1.3:
create-hash@^1.1.0, create-hash@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196"
integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==
Expand All @@ -1919,7 +1893,7 @@ create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.1.3:
ripemd160 "^2.0.1"
sha.js "^2.4.0"

create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4, create-hmac@^1.1.6:
create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
version "1.1.7"
resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff"
integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==
Expand Down Expand Up @@ -2239,14 +2213,6 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0"
safer-buffer "^2.1.0"

ecurve@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/ecurve/-/ecurve-1.0.6.tgz#dfdabbb7149f8d8b78816be5a7d5b83fcf6de797"
integrity sha512-/BzEjNfiSuB7jIWKcS/z8FK9jNjmEWvUV2YZ4RLSmcDtP7Lq0m6FvDuSnJpBlDpGRpfRQeTLGLBI8H+kEv0r+w==
dependencies:
bigi "^1.1.0"
safe-buffer "^5.0.1"

electron-to-chromium@^1.3.47:
version "1.3.122"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.122.tgz#b32a0805f48557bd3c3b8104eadc7fa511b14a9a"
Expand Down Expand Up @@ -2304,20 +2270,6 @@ enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0:
memory-fs "^0.4.0"
tapable "^1.0.0"

eosjs-ecc@4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/eosjs-ecc/-/eosjs-ecc-4.0.4.tgz#431450f30a6f73088ff5d7ba1ebdfe967a5ca4ab"
integrity sha512-9wAYefts4TidHOu+eN9nAisZdWpUzlUimZrB63oP7+/s4xRNJEn2Vvep2ICRODpxpidbshM1L7WaSYW9oiV5gA==
dependencies:
bigi "^1.4.2"
browserify-aes "^1.0.6"
bs58 "^4.0.1"
bytebuffer "^5.0.1"
create-hash "^1.1.3"
create-hmac "^1.1.6"
ecurve "^1.0.5"
randombytes "^2.0.5"

errno@^0.1.3, errno@~0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
Expand Down Expand Up @@ -4247,11 +4199,6 @@ log-update@^1.0.2:
ansi-escapes "^1.0.0"
cli-cursor "^1.0.2"

long@~3:
version "3.2.0"
resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b"
integrity sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=

loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
Expand Down

0 comments on commit 3d6c39c

Please sign in to comment.