diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js index ca4c306be..58366c26f 100644 --- a/app/scripts/keyring-controller.js +++ b/app/scripts/keyring-controller.js @@ -5,8 +5,9 @@ const bip39 = require('bip39') const Transaction = require('ethereumjs-tx') const EventEmitter = require('events').EventEmitter const filter = require('promise-filter') +const encryptor = require('browser-passworder') + const normalize = require('./lib/sig-util').normalize -const encryptor = require('./lib/encryptor') const messageManager = require('./lib/message-manager') const IdStoreMigrator = require('./lib/idStore-migrator') const BN = ethUtil.BN diff --git a/app/scripts/lib/encryptor.js b/app/scripts/lib/encryptor.js deleted file mode 100644 index 4770d2f54..000000000 --- a/app/scripts/lib/encryptor.js +++ /dev/null @@ -1,156 +0,0 @@ -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) { - const salt = this.generateSalt() - - return keyFromPassword(password + salt) - .then(function (passwordDerivedKey) { - return encryptWithKey(passwordDerivedKey, dataObj) - }) - .then(function (payload) { - payload.salt = salt - return JSON.stringify(payload) - }) -} - -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 { - data: vaultStr, - iv: vectorStr, - } - }) -} - -// Takes encrypted text, returns the restored Pojo. -function decrypt (password, text) { - const payload = JSON.parse(text) - const salt = payload.salt - return keyFromPassword(password + salt) - .then(function (key) { - return decryptWithKey(key, payload) - }) -} - -function decryptWithKey (key, payload) { - const encryptedData = decodeBase64ToBuffer(payload.data) - const vector = decodeBase64ToBuffer(payload.iv) - 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) { - var stripStr = (str.slice(0, 2) === '0x') ? str.slice(2) : str - var buf = new Uint8Array(stripStr.length / 2) - for (var i = 0; i < stripStr.length; i += 2) { - var seg = stripStr.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/package.json b/package.json index 46d17a94c..f3dd20d41 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "dependencies": { "async": "^1.5.2", "bip39": "^2.2.0", + "browser-passworder": "^2.0.3", "browserify-derequire": "^0.9.4", "clone": "^1.0.2", "copy-to-clipboard": "^2.0.0", diff --git a/test/integration/lib/encryptor-test.js b/test/integration/lib/encryptor-test.js deleted file mode 100644 index 897d22740..000000000 --- a/test/integration/lib/encryptor-test.js +++ /dev/null @@ -1,71 +0,0 @@ -var encryptor = require('../../../app/scripts/lib/encryptor') - -QUnit.module('encryptor') - -QUnit.test('encryptor:serializeBufferForStorage', function (assert) { - assert.expect(1) - var buf = new Buffer(2) - buf[0] = 16 - buf[1] = 1 - - var output = encryptor.serializeBufferForStorage(buf) - - var expect = '0x1001' - assert.equal(expect, output) -}) - -QUnit.test('encryptor:serializeBufferFromStorage', function (assert) { - assert.expect(2) - var input = '0x1001' - var output = encryptor.serializeBufferFromStorage(input) - - assert.equal(output[0], 16) - assert.equal(output[1], 1) -}) - -QUnit.test('encryptor:encrypt & decrypt', function(assert) { - var done = assert.async(); - var password, data, encrypted - - password = 'a sample passw0rd' - data = { foo: 'data to encrypt' } - - encryptor.encrypt(password, data) - .then(function(encryptedStr) { - assert.equal(typeof encryptedStr, 'string', 'returns a string') - return encryptor.decrypt(password, encryptedStr) - }) - .then(function (decryptedObj) { - assert.deepEqual(decryptedObj, data, 'decrypted what was encrypted') - done() - }) - .catch(function(reason) { - assert.ifError(reason, 'threw an error') - done(reason) - }) - -}) - -QUnit.test('encryptor:encrypt & decrypt with wrong password', function(assert) { - var done = assert.async(); - var password, data, encrypted, wrongPassword - - password = 'a sample passw0rd' - wrongPassword = 'a wrong password' - data = { foo: 'data to encrypt' } - - encryptor.encrypt(password, data) - .then(function(encryptedStr) { - assert.equal(typeof encryptedStr, 'string', 'returns a string') - return encryptor.decrypt(wrongPassword, encryptedStr) - }) - .then(function (decryptedObj) { - assert.equal(!decryptedObj, true, 'Wrong password should not decrypt') - done() - }) - .catch(function(reason) { - done() - }) -}) - -