From f6349187ef1b6b4128d9a0af124490879541199f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 6 Jan 2017 15:36:24 +0100 Subject: [PATCH] Bringing back `js-sha3` to fix in-browser signing (#4063) * Bring back Uint8Array sha3 support * Added SHA3 test with HEX encoding * Rename hex2Ascii => hexToAscii Add tests or the api/util/format functions Use js-sha3 for sha3 with hex encoding support * Adding Uint8Array test * Fixing Transaction import --- js/package.json | 1 - js/src/api/util/format.js | 18 ++++++--- js/src/api/util/format.spec.js | 44 +++++++++++++++++++++- js/src/api/util/index.js | 4 +- js/src/api/util/sha3.js | 16 +++----- js/src/api/util/sha3.spec.js | 11 ++++++ js/src/contracts/badgereg.js | 6 +-- js/src/ui/MethodDecoding/methodDecoding.js | 2 +- js/src/util/wallet.js | 2 +- 9 files changed, 79 insertions(+), 25 deletions(-) diff --git a/js/package.json b/js/package.json index abff29f4b..218452bf6 100644 --- a/js/package.json +++ b/js/package.json @@ -139,7 +139,6 @@ "blockies": "0.0.2", "brace": "0.9.0", "bytes": "2.4.0", - "crypto-js": "3.1.9-1", "debounce": "1.0.0", "es6-error": "4.0.0", "es6-promise": "4.0.5", diff --git a/js/src/api/util/format.js b/js/src/api/util/format.js index 531dfc549..f6976c13b 100644 --- a/js/src/api/util/format.js +++ b/js/src/api/util/format.js @@ -20,15 +20,21 @@ export function bytesToHex (bytes) { return '0x' + bytes.map((b) => ('0' + b.toString(16)).slice(-2)).join(''); } -export function hex2Ascii (_hex) { - const hex = /^(?:0x)?(.*)$/.exec(_hex.toString())[1]; +export function hexToBytes (hex) { + const raw = toHex(hex).slice(2); + const bytes = []; - let str = ''; - - for (let i = 0; i < hex.length; i += 2) { - str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); + for (let i = 0; i < raw.length; i += 2) { + bytes.push(parseInt(raw.substr(i, 2), 16)); } + return bytes; +} + +export function hexToAscii (hex) { + const bytes = hexToBytes(hex); + const str = bytes.map((byte) => String.fromCharCode(byte)).join(''); + return str; } diff --git a/js/src/api/util/format.spec.js b/js/src/api/util/format.spec.js index cfb07dee7..384c26d64 100644 --- a/js/src/api/util/format.spec.js +++ b/js/src/api/util/format.spec.js @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import { bytesToHex } from './format'; +import { bytesToHex, hexToBytes, hexToAscii, bytesToAscii, asciiToHex } from './format'; describe('api/util/format', () => { describe('bytesToHex', () => { @@ -26,4 +26,46 @@ describe('api/util/format', () => { expect(bytesToHex([0, 15, 16])).to.equal('0x000f10'); }); }); + + describe('hexToBytes', () => { + it('correctly converts an empty string', () => { + expect(hexToBytes('')).to.deep.equal([]); + expect(hexToBytes('0x')).to.deep.equal([]); + }); + + it('correctly converts a non-empty string', () => { + expect(hexToBytes('0x000f10')).to.deep.equal([0, 15, 16]); + }); + }); + + describe('asciiToHex', () => { + it('correctly converts an empty string', () => { + expect(asciiToHex('')).to.equal('0x'); + }); + + it('correctly converts a non-empty string', () => { + expect(asciiToHex('abc')).to.equal('0x616263'); + }); + }); + + describe('hexToAscii', () => { + it('correctly converts an empty string', () => { + expect(hexToAscii('')).to.equal(''); + expect(hexToAscii('0x')).to.equal(''); + }); + + it('correctly converts a non-empty string', () => { + expect(hexToAscii('0x616263')).to.equal('abc'); + }); + }); + + describe('bytesToAscii', () => { + it('correctly converts an empty string', () => { + expect(bytesToAscii([])).to.equal(''); + }); + + it('correctly converts a non-empty string', () => { + expect(bytesToAscii([97, 98, 99])).to.equal('abc'); + }); + }); }); diff --git a/js/src/api/util/index.js b/js/src/api/util/index.js index e33bb9273..414816cdd 100644 --- a/js/src/api/util/index.js +++ b/js/src/api/util/index.js @@ -16,7 +16,7 @@ import { isAddress as isAddressValid, toChecksumAddress } from '../../abi/util/address'; import { decodeCallData, decodeMethodInput, methodToAbi } from './decode'; -import { bytesToHex, hex2Ascii, asciiToHex } from './format'; +import { bytesToHex, hexToAscii, asciiToHex } from './format'; import { fromWei, toWei } from './wei'; import { sha3 } from './sha3'; import { isArray, isFunction, isHex, isInstanceOf, isString } from './types'; @@ -30,7 +30,7 @@ export default { isInstanceOf, isString, bytesToHex, - hex2Ascii, + hexToAscii, asciiToHex, createIdentityImg, decodeCallData, diff --git a/js/src/api/util/sha3.js b/js/src/api/util/sha3.js index 5a2c7c273..11ca6189a 100644 --- a/js/src/api/util/sha3.js +++ b/js/src/api/util/sha3.js @@ -14,21 +14,17 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import CryptoJS from 'crypto-js'; -import CryptoSha3 from 'crypto-js/sha3'; +import { keccak_256 } from 'js-sha3'; // eslint-disable-line + +import { hexToBytes } from './format'; export function sha3 (value, options) { if (options && options.encoding === 'hex') { - if (value.length > 2 && value.substr(0, 2) === '0x') { - value = value.substr(2); - } - - value = CryptoJS.enc.Hex.parse(value); + const bytes = hexToBytes(value); + return sha3(bytes); } - const hash = CryptoSha3(value, { - outputLength: 256 - }).toString(); + const hash = keccak_256(value); return `0x${hash}`; } diff --git a/js/src/api/util/sha3.spec.js b/js/src/api/util/sha3.spec.js index 20cd0e7d5..3446f7def 100644 --- a/js/src/api/util/sha3.spec.js +++ b/js/src/api/util/sha3.spec.js @@ -21,5 +21,16 @@ describe('api/util/sha3', () => { it('constructs a correct sha3 value', () => { expect(sha3('jacogr')).to.equal('0x2f4ff4b5a87abbd2edfed699db48a97744e028c7f7ce36444d40d29d792aa4dc'); }); + + it('constructs a correct sha3 encoded as hex', () => { + const key = '000000000000000000000000391694e7e0b0cce554cb130d723a9d27458f9298' + '0000000000000000000000000000000000000000000000000000000000000001'; + expect(sha3(key, { encoding: 'hex' })).to.equal('0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9'); + expect(sha3(`0x${key}`, { encoding: 'hex' })).to.equal('0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9'); + }); + + it('constructs a correct sha3 from Uint8Array', () => { + expect(sha3('01020304', { encoding: 'hex' })).to.equal('0xa6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b'); + expect(sha3(Uint8Array.from([1, 2, 3, 4]))).to.equal('0xa6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b'); + }); }); }); diff --git a/js/src/contracts/badgereg.js b/js/src/contracts/badgereg.js index 370236a26..0813caa47 100644 --- a/js/src/contracts/badgereg.js +++ b/js/src/contracts/badgereg.js @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import { bytesToHex, hex2Ascii } from '~/api/util/format'; +import { bytesToHex, hexToAscii } from '~/api/util/format'; import ABI from './abi/certifier.json'; @@ -62,7 +62,7 @@ export default class BadgeReg { name = bytesToHex(name); name = name === ZERO32 ? null - : hex2Ascii(name); + : hexToAscii(name); return this.fetchMeta(id) .then(({ title, icon }) => { @@ -84,7 +84,7 @@ export default class BadgeReg { }) .then(([ title, icon ]) => { title = bytesToHex(title); - title = title === ZERO32 ? null : hex2Ascii(title); + title = title === ZERO32 ? null : hexToAscii(title); if (bytesToHex(icon) === ZERO32) { icon = null; diff --git a/js/src/ui/MethodDecoding/methodDecoding.js b/js/src/ui/MethodDecoding/methodDecoding.js index a929d2681..59704a731 100644 --- a/js/src/ui/MethodDecoding/methodDecoding.js +++ b/js/src/ui/MethodDecoding/methodDecoding.js @@ -167,7 +167,7 @@ class MethodDecoding extends Component { getAscii () { const { api } = this.context; const { transaction } = this.props; - const ascii = api.util.hex2Ascii(transaction.input || transaction.data); + const ascii = api.util.hexToAscii(transaction.input || transaction.data); return { value: ascii, valid: ASCII_INPUT.test(ascii) }; } diff --git a/js/src/util/wallet.js b/js/src/util/wallet.js index b6240ff01..81fe2859e 100644 --- a/js/src/util/wallet.js +++ b/js/src/util/wallet.js @@ -15,7 +15,7 @@ // along with Parity. If not, see . import scrypt from 'scryptsy'; -import Transaction from 'ethereumjs-tx'; +import * as Transaction from 'ethereumjs-tx'; import { pbkdf2Sync } from 'crypto'; import { createDecipheriv } from 'browserify-aes';