diff --git a/README.md b/README.md index 7789cbd7c3..9f67d7bb50 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Check gh-pages files against live website at https://www.myetherwallet.com | File Name| Status| |---|---| | index.html |![embedded.html](https://filechecker.myetherwallet.com/check?localFile=https://raw.githubusercontent.com/kvhnuke/etherwallet/gh-pages/index.html&remoteFile=https://www.myetherwallet.com/index.html)| -| embedded.html |![embedded.html](https://filechecker.myetherwallet.com/check?localFile=https://raw.githubusercontent.com/kvhnuke/etherwallet/gh-pages/embedded.html&remoteFile=https://www.myetherwallet.com/embedded.html)| +| embedded.html |![embedded.html](https://filechecker.myetherwallet.com/check?localFile=https://raw.githubusercontent.com/kvhnuke/etherwallet/gh-pages/embedded.html&remoteFile=https://www.myetherwallet.com/embedded.html)| | helpers.html |![embedded.html](https://filechecker.myetherwallet.com/check?localFile=https://raw.githubusercontent.com/kvhnuke/etherwallet/gh-pages/helpers.html&remoteFile=https://www.myetherwallet.com/helpers.html)| | signmsg.html |![signmsg.html](https://filechecker.myetherwallet.com/check?localFile=https://raw.githubusercontent.com/kvhnuke/etherwallet/gh-pages/signmsg.html&remoteFile=https://www.myetherwallet.com/signmsg.html)| | bin/startMEW.js |![startMEW.js](https://filechecker.myetherwallet.com/check?localFile=https://raw.githubusercontent.com/kvhnuke/etherwallet/gh-pages/bin/startMEW.js&remoteFile=https://www.myetherwallet.com/bin/startMEW.js)| @@ -64,10 +64,10 @@ Check gh-pages files against live website at https://www.myetherwallet.com #### Features - Create new wallets completely client side. -- Access your wallet via unencrypted private key, encrypted private key, keystore files, mnemonics, or Digital Bitbox, Ledger Nano S or TREZOR hardware wallet. +- Access your wallet via unencrypted private key, encrypted private key, keystore files, mnemonics, or BitBox, Ledger Nano S or TREZOR hardware wallet. - Easily send ETH and *any* ERC-20 Standard Token. [Many tokens included as default.](https://myetherwallet.groovehq.com/knowledge_base/topics/can-i-send-my-steem-slash-btc-slash-ltc-slash-nem-slash-to-myetherwallet) - Generate, sign & send transactions offline, ensuring your private keys never touch an internet-connected device. -- Securely access your ETH & Tokens on your [Digital Bitbox, Ledger or TREZOR Hardware Wallet](https://myetherwallet.groovehq.com/knowledge_base/topics/hardware-wallet-recommends) via the MyEtherWallet interface (Chrome & Opera natively, Firefox w/ [add-on](https://addons.mozilla.org/en-US/firefox/addon/u2f-support-add-on/)) +- Securely access your ETH & Tokens on your [BitBox, Ledger or TREZOR Hardware Wallet](https://myetherwallet.groovehq.com/knowledge_base/topics/hardware-wallet-recommends) via the MyEtherWallet interface (Chrome & Opera natively, Firefox w/ [add-on](https://addons.mozilla.org/en-US/firefox/addon/u2f-support-add-on/)) - Now in 18 languages thanks 100% to the amazing Ethereum community. - Supports URI Strings on Send Transaction Page. - to=[address] diff --git a/app/includes/footer.tpl b/app/includes/footer.tpl index eb972ea717..f7b31e6937 100644 --- a/app/includes/footer.tpl +++ b/app/includes/footer.tpl @@ -61,7 +61,7 @@
@@ -873,7 +873,7 @@- Jaxx, Metamask, Exodus, imToken, TREZOR (ETH) & Digital Bitbox + Jaxx, Metamask, Exodus, imToken, TREZOR (ETH) & BitBox
diff --git a/app/scripts/staticJS/digitalBitboxEth.js b/app/scripts/staticJS/digitalBitboxEth.js index e123d636d0..e3fac33628 100644 --- a/app/scripts/staticJS/digitalBitboxEth.js +++ b/app/scripts/staticJS/digitalBitboxEth.js @@ -22,31 +22,46 @@ var DigitalBitboxEth = function(comm, sec) { DigitalBitboxEth.to = setTimeout(function(){ DigitalBitboxEth.sec = ''; }, 60000); } +var BitBoxSupportedMajorVersion = 5; + DigitalBitboxEth.sec = ''; DigitalBitboxEth.to = null; DigitalBitboxEth.aes_cbc_b64_decrypt = function(ciphertext, key) { - var res; try { + var h = Crypto.createHash('sha512').update(key).digest(); + var encryptionKey = h.slice(0, 32); + var authenticationKey = h.slice(32, 64); + var ub64 = new Buffer(ciphertext, "base64").toString("binary"); - var iv = new Buffer(ub64.slice(0, 16), "binary"); - var enc = new Buffer(ub64.slice(16), "binary"); - var decipher = Crypto.createDecipheriv("aes-256-cbc", key, iv); + var cipher = new Buffer(ub64.slice(0, ub64.length - 32), "binary"); + var hmac = new Buffer(ub64.slice(ub64.length - 32), "binary"); + var expectedHmac = Crypto.createHmac('sha256', authenticationKey).update(cipher).digest(); + if (!hmac.equals(expectedHmac)) { + throw "hmac check failed"; + } + var iv = new Buffer(cipher.slice(0, 16), "binary"); + var enc = new Buffer(cipher.slice(16), "binary"); + var decipher = Crypto.createDecipheriv("aes-256-cbc", encryptionKey, iv); var dec = decipher.update(enc) + decipher.final(); - res = dec.toString("utf8"); + return dec.toString("utf8"); } catch(err) { - res = ciphertext; + return ciphertext; } - return res; } DigitalBitboxEth.aes_cbc_b64_encrypt = function(plaintext, key) { try { + var h = Crypto.createHash('sha512').update(key).digest(); + var encryptionKey = h.slice(0, 32); + var authenticationKey = h.slice(32, 64); + var iv = Crypto.pseudoRandomBytes(16); - var cipher = Crypto.createCipheriv("aes-256-cbc", key, iv); + var cipher = Crypto.createCipheriv("aes-256-cbc", encryptionKey, iv); var ciphertext = Buffer.concat([iv, cipher.update(plaintext), cipher.final()]); - return ciphertext.toString("base64"); + var hmac = Crypto.createHmac('sha256', authenticationKey).update(ciphertext).digest(); + return Buffer.concat([ciphertext, hmac]).toString("base64"); } catch(err) { return ''; @@ -55,25 +70,48 @@ DigitalBitboxEth.aes_cbc_b64_encrypt = function(plaintext, key) { DigitalBitboxEth.parseError = function(errObject) { var errMsg = { - err101: 'The Digital Bitbox is not initialized. First use the Digital Bitbox desktop app to set up a wallet.',// No password set - err250: 'The Digital Bitbox is not initialized. First use the Digital Bitbox desktop app to set up a wallet.',// Wallet not seeded - err251: 'The Digital Bitbox is not initialized. First use the Digital Bitbox desktop app to set up a wallet.',// Wallet not seeded - err109: 'The Digital Bitbox received unexpected data. Was the correct password used? ' + errObject.message, + err101: 'The BitBox is not initialized. First use the Digital Bitbox desktop app to set up a wallet.',// No password set + err250: 'The BitBox is not initialized. First use the BitBox desktop app to set up a wallet.',// Wallet not seeded + err251: 'The BitBox is not initialized. First use the BitBox desktop app to set up a wallet.',// Wallet not seeded + err109: 'The BitBox received unexpected data. Was the correct password used? ' + errObject.message, }; var code = 'err' + errObject.code.toString(); var msg = errMsg[code] || errObject.message; return msg; } -DigitalBitboxEth.prototype.getAddress = function(path, callback) { +DigitalBitboxEth.prototype.send = function(cmd, callback) { var self = this; - var cmd = '{"xpub":"' + path + '"}'; - cmd = DigitalBitboxEth.aes_cbc_b64_encrypt(cmd, this.key); - var localCallback = function(response, error) { - if (typeof error != "undefined") { - callback(undefined, error); - } - else { + + this.comm.exchange('{"ping":""}', function(pingResponse, pingError) { + if (typeof pingError !== 'undefined') { + callback(undefined, pingError); + return; + } + pingResponse = JSON.parse(pingResponse.toString('utf8')); + if (!('device' in pingResponse)) { + callback(undefined, 'Please upgrade to the newest firmware using the BitBox Desktop app.'); + return; + } + var match = (/^v(\d+)\.\d+\.\d+/).exec(pingResponse.device.version); + if (match === null || match.length != 2) { + throw 'unexpected reply'; + } + var majorVersion = parseInt(match[1]); + if (majorVersion < BitBoxSupportedMajorVersion) { + callback(undefined, 'Please upgrade to the newest firmware using the BitBox Desktop app.'); + return; + } + if (majorVersion > BitBoxSupportedMajorVersion) { + callback(undefined, 'MyEtherWallet does not yet support this version of the firmware'); + return; + } + var cipher = DigitalBitboxEth.aes_cbc_b64_encrypt(cmd, self.key) + self.comm.exchange(cipher, function(response, error) { + if (typeof pingError !== 'undefined') { + callback(undefined, pingError); + return; + } try { response = JSON.parse(response.toString('utf8')); if ('error' in response) { @@ -86,71 +124,62 @@ DigitalBitboxEth.prototype.getAddress = function(path, callback) { callback(undefined, DigitalBitboxEth.parseError(response.error)); return; } - var hdkey = HDKey.fromExtendedKey(response.xpub); - var result = { - publicKey: hdkey.publicKey.toString('hex'), - chainCode: hdkey.chainCode.toString('hex'), - }; - callback(result); - return; + callback(response, undefined); } } catch(err) { callback(undefined, 'Unexpected error: ' + err.message); } + }); + }); +} + +DigitalBitboxEth.prototype.getAddress = function(path, callback) { + var self = this; + var cmd = '{"xpub":"' + path + '"}'; + var localCallback = function(response, error) { + if (typeof error != "undefined") { + callback(undefined, error); + return; } + var hdkey = HDKey.fromExtendedKey(response.xpub); + var result = { + publicKey: hdkey.publicKey.toString('hex'), + chainCode: hdkey.chainCode.toString('hex'), + }; + callback(result); + return; }; - self.comm.exchange(cmd, localCallback); + self.send(cmd, localCallback); } DigitalBitboxEth.signGeneric = function(self, path, chainId, hashToSign, callback) { var cmd = '{"sign":{"data":[{"hash":"' + hashToSign + '","keypath":"' + path + '"}]}}'; - cmd = DigitalBitboxEth.aes_cbc_b64_encrypt(cmd, self.key); - var localCallback = function(response, error) { if (typeof error != "undefined") { callback(undefined, error); + return; } - else { - try { - response = JSON.parse(response.toString('utf8')); - if ('error' in response) { - callback(undefined, DigitalBitboxEth.parseError(response.error)); - return; - } - if ('ciphertext' in response) { - response = JSON.parse(DigitalBitboxEth.aes_cbc_b64_decrypt(response.ciphertext, self.key)); - if ('error' in response) { - callback(undefined, DigitalBitboxEth.parseError(response.error)); - return; - } - if ('echo' in response) { - // Echo from first sign command. (Smart verification not implemented.) - // Send second sign command. - var cmd = '{"sign":""}'; - cmd = DigitalBitboxEth.aes_cbc_b64_encrypt(cmd, self.key); - self.comm.exchange(cmd, localCallback); - return; - } - if ('sign' in response) { - var vOffset = chainId ? chainId * 2 + 8 : 0; - var v = new Buffer([parseInt(response.sign[0].recid, 16) + 27 + vOffset]); - var result = { - v: v.toString('hex'), - r: response.sign[0].sig.slice(0, 64), - s: response.sign[0].sig.slice(64, 128), - }; - callback(result); - return; - } - } - } - catch(err) { - callback(undefined, 'Unexpected error:' + err.message); - } - } + if ('echo' in response) { + // Echo from first sign command. (Smart verification not implemented.) + // Send second sign command. + var cmd = '{"sign":""}'; + self.send(cmd, localCallback); + return; + } + if ('sign' in response) { + var vOffset = chainId ? chainId * 2 + 8 : 0; + var v = new Buffer([parseInt(response.sign[0].recid, 16) + 27 + vOffset]); + var result = { + v: v.toString('hex'), + r: response.sign[0].sig.slice(0, 64), + s: response.sign[0].sig.slice(64, 128), + }; + callback(result); + return; + } }; - self.comm.exchange(cmd, localCallback); + self.send(cmd, localCallback); } DigitalBitboxEth.prototype.signTransaction = function(path, eTx, callback) { diff --git a/app/scripts/translations/en.js b/app/scripts/translations/en.js index f5de4b8afe..c52341486d 100644 --- a/app/scripts/translations/en.js +++ b/app/scripts/translations/en.js @@ -210,7 +210,7 @@ ONBOARD_resume : 'It looks like you didn\'t finish reading thr /* Old */ ADD_DigitalBitbox_0a : 'Re-open MyEtherWallet on a secure (SSL) connection', ADD_DigitalBitbox_0b : 'Re-open MyEtherWallet using [Chrome](https://www.google.com/chrome/browser/desktop/) or [Opera](https://www.opera.com/)', -ADD_DigitalBitbox_scan : 'Connect your Digital Bitbox', +ADD_DigitalBitbox_scan : 'Connect your BitBox', ADD_Secalot_0a : 'Re-open MyEtherWallet on a secure (SSL) connection ', ADD_Secalot_0b : 'Re-open MyEtherWallet using [Chrome](https://www.google.com/chrome/browser/desktop/) or [Opera](https://www.opera.com/) ', ADD_Secalot_scan : 'Connect your Secalot ', @@ -580,7 +580,7 @@ x_Cancel : 'Cancel', x_CancelReplaceTx : 'Cancel or Replace Transaction', x_CancelTx : 'Cancel Transaction', x_CSV : 'CSV file (unencrypted)', -x_DigitalBitbox : 'Digital Bitbox', +x_DigitalBitbox : 'BitBox', x_Secalot : 'Secalot ', x_Download : 'Download', x_Json : 'JSON File (unencrypted)',