mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge pull request #939 from MetaMask/BrowserPassworder
Remove encryptor in favor of external browser-passworder
This commit is contained in:
commit
4fbea1c7b4
@ -5,8 +5,9 @@ const bip39 = require('bip39')
|
|||||||
const Transaction = require('ethereumjs-tx')
|
const Transaction = require('ethereumjs-tx')
|
||||||
const EventEmitter = require('events').EventEmitter
|
const EventEmitter = require('events').EventEmitter
|
||||||
const filter = require('promise-filter')
|
const filter = require('promise-filter')
|
||||||
|
const encryptor = require('browser-passworder')
|
||||||
|
|
||||||
const normalize = require('./lib/sig-util').normalize
|
const normalize = require('./lib/sig-util').normalize
|
||||||
const encryptor = require('./lib/encryptor')
|
|
||||||
const messageManager = require('./lib/message-manager')
|
const messageManager = require('./lib/message-manager')
|
||||||
const IdStoreMigrator = require('./lib/idStore-migrator')
|
const IdStoreMigrator = require('./lib/idStore-migrator')
|
||||||
const BN = ethUtil.BN
|
const BN = ethUtil.BN
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -37,6 +37,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async": "^1.5.2",
|
"async": "^1.5.2",
|
||||||
"bip39": "^2.2.0",
|
"bip39": "^2.2.0",
|
||||||
|
"browser-passworder": "^2.0.3",
|
||||||
"browserify-derequire": "^0.9.4",
|
"browserify-derequire": "^0.9.4",
|
||||||
"clone": "^1.0.2",
|
"clone": "^1.0.2",
|
||||||
"copy-to-clipboard": "^2.0.0",
|
"copy-to-clipboard": "^2.0.0",
|
||||||
|
@ -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()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user