diff options
author | Kevin Serrano <kevgagser@gmail.com> | 2016-11-19 01:23:20 +0800 |
---|---|---|
committer | Kevin Serrano <kevgagser@gmail.com> | 2016-11-19 01:23:20 +0800 |
commit | bef023fb4a699c16fe0fff30ce18840678ed7ad0 (patch) | |
tree | d11be9af938712aeb554f070822be01b2d7fbceb /app/scripts/lib | |
parent | 37d836fa72c75945d436bd94feadb65aa95d7e5f (diff) | |
parent | 39e0a33bba2709971edec0304027f646cb2db3cc (diff) | |
download | tangerine-wallet-browser-bef023fb4a699c16fe0fff30ce18840678ed7ad0.tar tangerine-wallet-browser-bef023fb4a699c16fe0fff30ce18840678ed7ad0.tar.gz tangerine-wallet-browser-bef023fb4a699c16fe0fff30ce18840678ed7ad0.tar.bz2 tangerine-wallet-browser-bef023fb4a699c16fe0fff30ce18840678ed7ad0.tar.lz tangerine-wallet-browser-bef023fb4a699c16fe0fff30ce18840678ed7ad0.tar.xz tangerine-wallet-browser-bef023fb4a699c16fe0fff30ce18840678ed7ad0.tar.zst tangerine-wallet-browser-bef023fb4a699c16fe0fff30ce18840678ed7ad0.zip |
Resolve merge conflict.
Diffstat (limited to 'app/scripts/lib')
-rw-r--r-- | app/scripts/lib/auto-faucet.js | 5 | ||||
-rw-r--r-- | app/scripts/lib/auto-reload.js | 5 | ||||
-rw-r--r-- | app/scripts/lib/config-manager.js | 77 | ||||
-rw-r--r-- | app/scripts/lib/encryptor.js | 149 | ||||
-rw-r--r-- | app/scripts/lib/idStore-migrator.js | 52 | ||||
-rw-r--r-- | app/scripts/lib/idStore.js | 16 | ||||
-rw-r--r-- | app/scripts/lib/inpage-provider.js | 4 | ||||
-rw-r--r-- | app/scripts/lib/is-popup-or-notification.js | 2 | ||||
-rw-r--r-- | app/scripts/lib/notifications.js | 12 | ||||
-rw-r--r-- | app/scripts/lib/random-id.js | 2 | ||||
-rw-r--r-- | app/scripts/lib/sig-util.js | 28 |
11 files changed, 305 insertions, 47 deletions
diff --git a/app/scripts/lib/auto-faucet.js b/app/scripts/lib/auto-faucet.js index 59cf0ec20..1e86f735e 100644 --- a/app/scripts/lib/auto-faucet.js +++ b/app/scripts/lib/auto-faucet.js @@ -1,6 +1,9 @@ -var uri = 'https://faucet.metamask.io/' +const uri = 'https://faucet.metamask.io/' +const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG' +const env = process.env.METAMASK_ENV module.exports = function (address) { + if (METAMASK_DEBUG || env === 'test') return // Don't faucet in development or test var http = new XMLHttpRequest() var data = address http.open('POST', uri, true) diff --git a/app/scripts/lib/auto-reload.js b/app/scripts/lib/auto-reload.js index 3c90905db..1302df35f 100644 --- a/app/scripts/lib/auto-reload.js +++ b/app/scripts/lib/auto-reload.js @@ -18,17 +18,16 @@ function setupDappAutoReload (web3) { return handleResetRequest - function handleResetRequest() { + function handleResetRequest () { resetWasRequested = true // ignore if web3 was not used if (!pageIsUsingWeb3) return // reload after short timeout setTimeout(triggerReset, 500) } - } // reload the page function triggerReset () { global.location.reload() -}
\ No newline at end of file +} diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index cced32670..faf64bfdc 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -2,6 +2,7 @@ const Migrator = require('pojo-migrator') const MetamaskConfig = require('../config.js') const migrations = require('./migrations') const rp = require('request-promise') +const ethUtil = require('ethereumjs-util') const TESTNET_RPC = MetamaskConfig.network.testnet const MAINNET_RPC = MetamaskConfig.network.mainnet @@ -110,6 +111,27 @@ ConfigManager.prototype.setWallet = function (wallet) { this.setData(data) } +ConfigManager.prototype.setVault = function (encryptedString) { + var data = this.getData() + data.vault = encryptedString + this.setData(data) +} + +ConfigManager.prototype.getVault = function () { + var data = this.getData() + return ('vault' in data) && data.vault +} + +ConfigManager.prototype.getKeychains = function () { + return this.migrator.getData().keychains || [] +} + +ConfigManager.prototype.setKeychains = function (keychains) { + var data = this.migrator.getData() + data.keychains = keychains + this.setData(data) +} + ConfigManager.prototype.getSelectedAccount = function () { var config = this.getConfig() return config.selectedAccount @@ -117,7 +139,7 @@ ConfigManager.prototype.getSelectedAccount = function () { ConfigManager.prototype.setSelectedAccount = function (address) { var config = this.getConfig() - config.selectedAccount = address + config.selectedAccount = ethUtil.addHexPrefix(address) this.setConfig(config) } @@ -132,11 +154,23 @@ ConfigManager.prototype.setShowSeedWords = function (should) { this.setData(data) } + ConfigManager.prototype.getShouldShowSeedWords = function () { var data = this.migrator.getData() return data.showSeedWords } +ConfigManager.prototype.setSeedWords = function (words) { + var data = this.getData() + data.seedWords = words + this.setData(data) +} + +ConfigManager.prototype.getSeedWords = function () { + var data = this.getData() + return ('seedWords' in data) && data.seedWords +} + ConfigManager.prototype.getCurrentRpcAddress = function () { var provider = this.getProvider() if (!provider) return null @@ -235,13 +269,15 @@ ConfigManager.prototype.getWalletNicknames = function () { } ConfigManager.prototype.nicknameForWallet = function (account) { + const address = ethUtil.addHexPrefix(account.toLowerCase()) const nicknames = this.getWalletNicknames() - return nicknames[account] + return nicknames[address] } ConfigManager.prototype.setNicknameForWallet = function (account, nickname) { + const address = ethUtil.addHexPrefix(account.toLowerCase()) const nicknames = this.getWalletNicknames() - nicknames[account] = nickname + nicknames[address] = nickname var data = this.getData() data.walletNicknames = nicknames this.setData(data) @@ -249,6 +285,17 @@ ConfigManager.prototype.setNicknameForWallet = function (account, nickname) { // observable +ConfigManager.prototype.getSalt = function () { + var data = this.getData() + return ('salt' in data) && data.salt +} + +ConfigManager.prototype.setSalt = function (salt) { + var data = this.getData() + data.salt = salt + this.setData(data) +} + ConfigManager.prototype.subscribe = function (fn) { this._subs.push(fn) var unsubscribe = this.unsubscribe.bind(this, fn) @@ -266,15 +313,15 @@ ConfigManager.prototype._emitUpdates = function (state) { }) } -ConfigManager.prototype.setConfirmed = function (confirmed) { +ConfigManager.prototype.setConfirmedDisclaimer = function (confirmed) { var data = this.getData() - data.isConfirmed = confirmed + data.isDisclaimerConfirmed = confirmed this.setData(data) } -ConfigManager.prototype.getConfirmed = function () { +ConfigManager.prototype.getConfirmedDisclaimer = function () { var data = this.getData() - return ('isConfirmed' in data) && data.isConfirmed + return ('isDisclaimerConfirmed' in data) && data.isDisclaimerConfirmed } ConfigManager.prototype.setTOSHash = function (hash) { @@ -311,7 +358,6 @@ ConfigManager.prototype.updateConversionRate = function () { this.setConversionPrice(0) this.setConversionDate('N/A') }) - } ConfigManager.prototype.setConversionPrice = function (price) { @@ -336,21 +382,6 @@ ConfigManager.prototype.getConversionDate = function () { return (('conversionDate' in data) && data.conversionDate) || 'N/A' } -ConfigManager.prototype.setShouldntShowWarning = function () { - var data = this.getData() - if (data.isEthConfirmed) { - data.isEthConfirmed = !data.isEthConfirmed - } else { - data.isEthConfirmed = true - } - this.setData(data) -} - -ConfigManager.prototype.getShouldntShowWarning = function () { - var data = this.getData() - return ('isEthConfirmed' in data) && data.isEthConfirmed -} - ConfigManager.prototype.getShapeShiftTxList = function () { var data = this.getData() var shapeShiftTxList = data.shapeShiftTxList ? data.shapeShiftTxList : [] diff --git a/app/scripts/lib/encryptor.js b/app/scripts/lib/encryptor.js new file mode 100644 index 000000000..2af2a1d2b --- /dev/null +++ b/app/scripts/lib/encryptor.js @@ -0,0 +1,149 @@ +var ethUtil = require('ethereumjs-util') + +module.exports = { + + // Simple encryption methods: + encrypt, + decrypt, + + // More advanced encryption methods: + keyFromPassword, + encryptWithKey, + decryptWithKey, + + // Buffer <-> String methods + convertArrayBufferViewtoString, + convertStringToArrayBufferView, + + // Buffer <-> Hex string methods + serializeBufferForStorage, + serializeBufferFromStorage, + + // Buffer <-> base64 string methods + encodeBufferToBase64, + decodeBase64ToBuffer, + + generateSalt, +} + +// Takes a Pojo, returns cypher text. +function encrypt (password, dataObj) { + return keyFromPassword(password) + .then(function (passwordDerivedKey) { + return encryptWithKey(passwordDerivedKey, dataObj) + }) +} + +function encryptWithKey (key, dataObj) { + var data = JSON.stringify(dataObj) + var dataBuffer = convertStringToArrayBufferView(data) + var vector = global.crypto.getRandomValues(new Uint8Array(16)) + + return global.crypto.subtle.encrypt({ + name: 'AES-GCM', + iv: vector, + }, key, dataBuffer).then(function (buf) { + var buffer = new Uint8Array(buf) + var vectorStr = encodeBufferToBase64(vector) + var vaultStr = encodeBufferToBase64(buffer) + return `${vaultStr}\\${vectorStr}` + }) +} + +// Takes encrypted text, returns the restored Pojo. +function decrypt (password, text) { + return keyFromPassword(password) + .then(function (key) { + return decryptWithKey(key, text) + }) +} + +function decryptWithKey (key, text) { + const parts = text.split('\\') + const encryptedData = decodeBase64ToBuffer(parts[0]) + const vector = decodeBase64ToBuffer(parts[1]) + return crypto.subtle.decrypt({name: 'AES-GCM', iv: vector}, key, encryptedData) + .then(function (result) { + const decryptedData = new Uint8Array(result) + const decryptedStr = convertArrayBufferViewtoString(decryptedData) + const decryptedObj = JSON.parse(decryptedStr) + return decryptedObj + }) + .catch(function (reason) { + throw new Error('Incorrect password') + }) +} + +function convertStringToArrayBufferView (str) { + var bytes = new Uint8Array(str.length) + for (var i = 0; i < str.length; i++) { + bytes[i] = str.charCodeAt(i) + } + + return bytes +} + +function convertArrayBufferViewtoString (buffer) { + var str = '' + for (var i = 0; i < buffer.byteLength; i++) { + str += String.fromCharCode(buffer[i]) + } + + return str +} + +function keyFromPassword (password) { + var passBuffer = convertStringToArrayBufferView(password) + return global.crypto.subtle.digest('SHA-256', passBuffer) + .then(function (passHash) { + return global.crypto.subtle.importKey('raw', passHash, {name: 'AES-GCM'}, false, ['encrypt', 'decrypt']) + }) +} + +function serializeBufferFromStorage (str) { + str = ethUtil.stripHexPrefix(str) + var buf = new Uint8Array(str.length / 2) + for (var i = 0; i < str.length; i += 2) { + var seg = str.substr(i, 2) + buf[i / 2] = parseInt(seg, 16) + } + return buf +} + +// Should return a string, ready for storage, in hex format. +function serializeBufferForStorage (buffer) { + var result = '0x' + var len = buffer.length || buffer.byteLength + for (var i = 0; i < len; i++) { + result += unprefixedHex(buffer[i]) + } + return result +} + +function unprefixedHex (num) { + var hex = num.toString(16) + while (hex.length < 2) { + hex = '0' + hex + } + return hex +} + +function encodeBufferToBase64 (buf) { + var b64encoded = btoa(String.fromCharCode.apply(null, buf)) + return b64encoded +} + +function decodeBase64ToBuffer (base64) { + var buf = new Uint8Array(atob(base64).split('') + .map(function (c) { + return c.charCodeAt(0) + })) + return buf +} + +function generateSalt (byteCount = 32) { + var view = new Uint8Array(byteCount) + global.crypto.getRandomValues(view) + var b64encoded = btoa(String.fromCharCode.apply(null, view)) + return b64encoded +} diff --git a/app/scripts/lib/idStore-migrator.js b/app/scripts/lib/idStore-migrator.js new file mode 100644 index 000000000..818364720 --- /dev/null +++ b/app/scripts/lib/idStore-migrator.js @@ -0,0 +1,52 @@ +const IdentityStore = require('./idStore') + + +module.exports = class IdentityStoreMigrator { + + constructor ({ configManager }) { + this.configManager = configManager + const hasOldVault = this.hasOldVault() + if (!hasOldVault) { + this.idStore = new IdentityStore({ configManager }) + } + } + + oldSeedForPassword (password) { + const hasOldVault = this.hasOldVault() + const configManager = this.configManager + + if (!this.idStore) { + this.idStore = new IdentityStore({ configManager }) + } + + if (!hasOldVault) { + return Promise.resolve(null) + } + + return new Promise((resolve, reject) => { + this.idStore.submitPassword(password, (err) => { + if (err) return reject(err) + try { + resolve(this.serializeVault()) + } catch (e) { + reject(e) + } + }) + }) + } + + serializeVault () { + const mnemonic = this.idStore._idmgmt.getSeed() + const n = this.idStore._getAddresses().length + + return { + type: 'HD Key Tree', + data: { mnemonic, n }, + } + } + + hasOldVault () { + const wallet = this.configManager.getWallet() + return wallet + } +} diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js index ccd5efe69..b73652af5 100644 --- a/app/scripts/lib/idStore.js +++ b/app/scripts/lib/idStore.js @@ -102,8 +102,7 @@ IdentityStore.prototype.getState = function () { isInitialized: !!configManager.getWallet() && !seedWords, isUnlocked: this._isUnlocked(), seedWords: seedWords, - isConfirmed: configManager.getConfirmed(), - isEthConfirmed: configManager.getShouldntShowWarning(), + isDisclaimerConfirmed: configManager.getConfirmedDisclaimer(), unconfTxs: configManager.unconfirmedTxs(), transactions: configManager.getTxList(), unconfMsgs: messageManager.unconfirmedMsgs(), @@ -114,7 +113,6 @@ IdentityStore.prototype.getState = function () { conversionRate: configManager.getConversionRate(), conversionDate: configManager.getConversionDate(), gasMultiplier: configManager.getGasMultiplier(), - })) } @@ -245,7 +243,7 @@ IdentityStore.prototype.addUnconfirmedTransaction = function (txParams, onTxDone ], didComplete) // perform static analyis on the target contract code - function analyzeForDelegateCall(cb){ + function analyzeForDelegateCall (cb) { if (txParams.to) { query.getCode(txParams.to, (err, result) => { if (err) return cb(err.message || err) @@ -258,16 +256,16 @@ IdentityStore.prototype.addUnconfirmedTransaction = function (txParams, onTxDone } } - function estimateGas(cb){ + function estimateGas (cb) { var estimationParams = extend(txParams) // 1 billion gas for estimation var gasLimit = '0x3b9aca00' estimationParams.gas = gasLimit - query.estimateGas(estimationParams, function(err, result){ + query.estimateGas(estimationParams, function (err, result) { if (err) return cb(err.message || err) if (result === estimationParams.gas) { txData.simulationFails = true - query.getBlockByNumber('latest', true, function(err, block){ + query.getBlockByNumber('latest', true, function (err, block) { if (err) return cb(err) txData.estimatedGas = block.gasLimit txData.txParams.gas = block.gasLimit @@ -440,7 +438,9 @@ IdentityStore.prototype._loadIdentities = function () { var addresses = this._getAddresses() addresses.forEach((address, i) => { // // add to ethStore - this._ethStore.addAccount(ethUtil.addHexPrefix(address)) + if (this._ethStore) { + this._ethStore.addAccount(ethUtil.addHexPrefix(address)) + } // add to identities const defaultLabel = 'Account ' + (i + 1) const nickname = configManager.nicknameForWallet(address) diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js index f1ba2e0ed..c9d617996 100644 --- a/app/scripts/lib/inpage-provider.js +++ b/app/scripts/lib/inpage-provider.js @@ -40,7 +40,7 @@ function MetamaskInpageProvider (connectionStream) { self.idMap = {} // handle sendAsync requests via asyncProvider - self.sendAsync = function(payload, cb){ + self.sendAsync = function (payload, cb) { // rewrite request ids var request = eachJsonMessage(payload, (message) => { var newId = createRandomId() @@ -49,7 +49,7 @@ function MetamaskInpageProvider (connectionStream) { return message }) // forward to asyncProvider - asyncProvider.sendAsync(request, function(err, res){ + asyncProvider.sendAsync(request, function (err, res) { if (err) return cb(err) // transform messages to original ids eachJsonMessage(res, (message) => { diff --git a/app/scripts/lib/is-popup-or-notification.js b/app/scripts/lib/is-popup-or-notification.js index 5c38ac823..693fa8751 100644 --- a/app/scripts/lib/is-popup-or-notification.js +++ b/app/scripts/lib/is-popup-or-notification.js @@ -1,4 +1,4 @@ -module.exports = function isPopupOrNotification() { +module.exports = function isPopupOrNotification () { const url = window.location.href if (url.match(/popup.html$/)) { return 'popup' diff --git a/app/scripts/lib/notifications.js b/app/scripts/lib/notifications.js index cd7535232..3db1ac6b5 100644 --- a/app/scripts/lib/notifications.js +++ b/app/scripts/lib/notifications.js @@ -15,12 +15,9 @@ function show () { if (err) throw err if (popup) { - // bring focus to existing popup extension.windows.update(popup.id, { focused: true }) - } else { - // create new popup extension.windows.create({ url: 'notification.html', @@ -29,12 +26,11 @@ function show () { width, height, }) - } }) } -function getWindows(cb) { +function getWindows (cb) { // Ignore in test environment if (!extension.windows) { return cb() @@ -45,14 +41,14 @@ function getWindows(cb) { }) } -function getPopup(cb) { +function getPopup (cb) { getWindows((err, windows) => { if (err) throw err cb(null, getPopupIn(windows)) }) } -function getPopupIn(windows) { +function getPopupIn (windows) { return windows ? windows.find((win) => { return (win && win.type === 'popup' && win.height === height && @@ -60,7 +56,7 @@ function getPopupIn(windows) { }) : null } -function closePopup() { +function closePopup () { getPopup((err, popup) => { if (err) throw err if (!popup) return diff --git a/app/scripts/lib/random-id.js b/app/scripts/lib/random-id.js index 3c5ae5600..78d9e34a5 100644 --- a/app/scripts/lib/random-id.js +++ b/app/scripts/lib/random-id.js @@ -1,4 +1,4 @@ -const MAX = 1000000000 +const MAX = Number.MAX_SAFE_INTEGER let idCounter = Math.round( Math.random() * MAX ) function createRandomId() { diff --git a/app/scripts/lib/sig-util.js b/app/scripts/lib/sig-util.js new file mode 100644 index 000000000..193dda381 --- /dev/null +++ b/app/scripts/lib/sig-util.js @@ -0,0 +1,28 @@ +const ethUtil = require('ethereumjs-util') + +module.exports = { + + concatSig: function (v, r, s) { + const rSig = ethUtil.fromSigned(r) + const sSig = ethUtil.fromSigned(s) + const vSig = ethUtil.bufferToInt(v) + const rStr = padWithZeroes(ethUtil.toUnsigned(rSig).toString('hex'), 64) + const sStr = padWithZeroes(ethUtil.toUnsigned(sSig).toString('hex'), 64) + const vStr = ethUtil.stripHexPrefix(ethUtil.intToHex(vSig)) + return ethUtil.addHexPrefix(rStr.concat(sStr, vStr)).toString('hex') + }, + + normalize: function (address) { + if (!address) return + return ethUtil.addHexPrefix(address.toLowerCase()) + }, + +} + +function padWithZeroes (number, length) { + var myString = '' + number + while (myString.length < length) { + myString = '0' + myString + } + return myString +} |