1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Fix handling of migrating old vault style

Now old vaults are recognized as an "Initialized" MetaMask instance.

Upon logging in, when fetching the initial password-derived key, if there is no new-style vault, but there is an old style vault, it is migrated to the new format before proceeding through the usual unlocking steps.
This commit is contained in:
Dan Finlay 2016-11-02 15:04:50 -07:00
parent 8f3db0dbc0
commit 4cf1b606e4
5 changed files with 115 additions and 57 deletions

View File

@ -47,11 +47,14 @@ module.exports = class KeyringController extends EventEmitter {
} }
getState() { getState() {
let address = this.configManager.getSelectedAccount() const configManager = this.configManager
const address = configManager.getSelectedAccount()
const wallet = configManager.getWallet() // old style vault
const vault = configManager.getVault() // new style vault
return { return {
seedWords: this.configManager.getSeedWords(), seedWords: this.configManager.getSeedWords(),
isInitialized: !!this.configManager.getVault(), isInitialized: (!!wallet || !!vault),
isUnlocked: !!this.key, isUnlocked: !!this.key,
isConfirmed: true, // AUDIT this.configManager.getConfirmed(), isConfirmed: true, // AUDIT this.configManager.getConfirmed(),
unconfTxs: this.configManager.unconfirmedTxs(), unconfTxs: this.configManager.unconfirmedTxs(),
@ -77,7 +80,7 @@ module.exports = class KeyringController extends EventEmitter {
createNewVaultAndKeychain(password, entropy, cb) { createNewVaultAndKeychain(password, entropy, cb) {
this.createNewVault(password, entropy, (err, serialized) => { this.createNewVault(password, entropy, (err, serialized) => {
if (err) return cb(err) if (err) return cb(err)
this.createFirstKeyTree(serialized, password, cb) this.createFirstKeyTree(password, cb)
}) })
} }
@ -112,25 +115,43 @@ module.exports = class KeyringController extends EventEmitter {
}) })
} }
createNewVault(password, entropy, cb) { migrateAndGetKey(password) {
const salt = this.encryptor.generateSalt() let key
this.configManager.setSalt(salt) const shouldMigrate = !!this.configManager.getWallet() && !this.configManager.getVault()
let serialized
this.idStoreMigrator.oldSeedForPassword(password)
.then((oldSerialized) => {
if (oldSerialized) {
serialized = oldSerialized
}
return this.loadKey(password) return this.loadKey(password)
.then((derivedKey) => {
key = derivedKey
return this.idStoreMigrator.oldSeedForPassword(password)
})
.then((serialized) => {
if (serialized && shouldMigrate) {
const accountLength = this.getAccounts().length
const keyring = this.restoreKeyring(accountLength, serialized)
this.keyrings.push(keyring)
this.configManager.setSelectedAccount(keyring.getAccounts()[0])
return this.persistAllKeyrings().then(() => {
return key
})
} else {
return Promise.resolve(key)
}
})
}
createNewVault(password, entropy, cb) {
const configManager = this.configManager
const salt = this.encryptor.generateSalt()
configManager.setSalt(salt)
return new Promise((res, rej) => {
this.createFirstKeyTree(password, (err, state) => {
if (err) return rej(err)
res(configManager.getVault())
}) })
.then((key) => {
const first = serialized ? [serialized] : []
return this.encryptor.encryptWithKey(key, first)
}) })
.then((encryptedString) => { .then((encryptedString) => {
this.configManager.setVault(encryptedString) const serialized = this.keyrings[0].serialize()
cb(null, serialized) cb(null, serialized)
}) })
.catch((err) => { .catch((err) => {
@ -138,9 +159,9 @@ module.exports = class KeyringController extends EventEmitter {
}) })
} }
createFirstKeyTree(serialized, password, cb) { createFirstKeyTree(password, cb) {
if (!serialized) { this.clearKeyrings()
this.addNewKeyring('HD Key Tree', {n: 1}, (err, newState) => { this.addNewKeyring('HD Key Tree', {n: 1}, (err) => {
const firstKeyring = this.keyrings[0] const firstKeyring = this.keyrings[0]
const firstAccount = firstKeyring.getAccounts()[0] const firstAccount = firstKeyring.getAccounts()[0]
const hexAccount = ethUtil.addHexPrefix(firstAccount) const hexAccount = ethUtil.addHexPrefix(firstAccount)
@ -148,11 +169,9 @@ module.exports = class KeyringController extends EventEmitter {
this.configManager.setSelectedAccount(hexAccount) this.configManager.setSelectedAccount(hexAccount)
this.configManager.setSeedWords(seedWords) this.configManager.setSeedWords(seedWords)
autoFaucet(hexAccount) autoFaucet(hexAccount)
this.persistAllKeyrings()
cb(err, this.getState()) cb(err, this.getState())
}) })
} else {
return this.submitPassword(password, cb)
}
} }
placeSeedWords () { placeSeedWords () {
@ -161,10 +180,8 @@ module.exports = class KeyringController extends EventEmitter {
this.configManager.setSeedWords(seedWords) this.configManager.setSeedWords(seedWords)
} }
submitPassword(password, cb) { submitPassword(password, cb) {
this.loadKey(password) this.migrateAndGetKey(password)
.then((key) => { .then((key) => {
return this.unlockKeyrings(key) return this.unlockKeyrings(key)
}) })
@ -173,6 +190,7 @@ module.exports = class KeyringController extends EventEmitter {
cb(null, this.getState()) cb(null, this.getState())
}) })
.catch((err) => { .catch((err) => {
console.error(err)
cb(err) cb(err)
}) })
} }
@ -208,7 +226,12 @@ module.exports = class KeyringController extends EventEmitter {
const accounts = ring.addAccounts(1) const accounts = ring.addAccounts(1)
this.setupAccounts(accounts) this.setupAccounts(accounts)
this.persistAllKeyrings() this.persistAllKeyrings()
.then(() => {
cb(null, this.getState()) cb(null, this.getState())
})
.catch((reason) => {
cb(reason)
})
} }
setupAccounts(accounts) { setupAccounts(accounts) {
@ -258,9 +281,6 @@ module.exports = class KeyringController extends EventEmitter {
this.configManager.setVault(encryptedString) this.configManager.setVault(encryptedString)
return true return true
}) })
.catch((reason) => {
console.error('Failed to persist keyrings.', reason)
})
} }
unlockKeyrings(key) { unlockKeyrings(key) {
@ -268,6 +288,9 @@ module.exports = class KeyringController extends EventEmitter {
return this.encryptor.decryptWithKey(key, encryptedVault) return this.encryptor.decryptWithKey(key, encryptedVault)
.then((vault) => { .then((vault) => {
this.keyrings = vault.map(this.restoreKeyring.bind(this, 0)) this.keyrings = vault.map(this.restoreKeyring.bind(this, 0))
return this.persistAllKeyrings()
})
.then(() => {
return this.keyrings return this.keyrings
}) })
} }
@ -282,6 +305,7 @@ module.exports = class KeyringController extends EventEmitter {
this.loadBalanceAndNickname(account, i) this.loadBalanceAndNickname(account, i)
}) })
this.keyrings.push(keyring)
return keyring return keyring
} }

View File

@ -16,11 +16,11 @@ module.exports = class HdKeyring extends EventEmitter {
constructor(opts = {}) { constructor(opts = {}) {
super() super()
this.type = type this.type = type
this.opts = opts || {}
this.deserialize(opts) this.deserialize(opts)
} }
deserialize(opts) { deserialize(opts = {}) {
this.opts = opts || {}
this.wallets = [] this.wallets = []
this.mnemonic = null this.mnemonic = null
this.root = null this.root = null
@ -32,12 +32,11 @@ module.exports = class HdKeyring extends EventEmitter {
if ('n' in opts) { if ('n' in opts) {
this.addAccounts(opts.n) this.addAccounts(opts.n)
} }
} }
initFromMnemonic(mnemonic) { initFromMnemonic(mnemonic) {
const seed = bip39.mnemonicToSeed(mnemonic)
this.mnemonic = mnemonic this.mnemonic = mnemonic
const seed = bip39.mnemonicToSeed(mnemonic)
this.hdWallet = hdkey.fromMasterSeed(seed) this.hdWallet = hdkey.fromMasterSeed(seed)
this.root = this.hdWallet.derivePath(hdPathString) this.root = this.hdWallet.derivePath(hdPathString)
} }

View File

@ -5,12 +5,21 @@ module.exports = class IdentityStoreMigrator {
constructor ({ configManager }) { constructor ({ configManager }) {
this.configManager = configManager this.configManager = configManager
const hasOldVault = this.hasOldVault()
if (!hasOldVault) {
this.idStore = new IdentityStore({ configManager }) this.idStore = new IdentityStore({ configManager })
} }
}
oldSeedForPassword( password ) { oldSeedForPassword( password ) {
const isOldVault = this.hasOldVault() const hasOldVault = this.hasOldVault()
if (!isOldVault) { const configManager = this.configManager
if (!this.idStore) {
this.idStore = new IdentityStore({ configManager })
}
if (!hasOldVault) {
return Promise.resolve(null) return Promise.resolve(null)
} }

View File

@ -40,7 +40,7 @@ describe('IdentityStore to KeyringController migration', function() {
window.localStorage = {} // Hacking localStorage support into JSDom window.localStorage = {} // Hacking localStorage support into JSDom
configManager = new ConfigManager({ configManager = new ConfigManager({
loadData, loadData,
setData: (d) => { global.localStorage = d } setData: (d) => { window.localStorage = d }
}) })
@ -52,11 +52,11 @@ describe('IdentityStore to KeyringController migration', function() {
}, },
}) })
idStore._createVault(password, mockVault.seed, null, function (err) { idStore._createVault(password, mockVault.seed, null, (err) => {
assert.ifError(err, 'createNewVault threw error') assert.ifError(err, 'createNewVault threw error')
originalKeystore = idStore._idmgmt.keyStore originalKeystore = idStore._idmgmt.keyStore
idStore.setLocked(function(err) { idStore.setLocked((err) => {
assert.ifError(err, 'createNewVault threw error') assert.ifError(err, 'createNewVault threw error')
keyringController = new KeyringController({ keyringController = new KeyringController({
configManager, configManager,
@ -74,22 +74,38 @@ describe('IdentityStore to KeyringController migration', function() {
}) })
}) })
describe('creating new vault type', function() { describe('entering a password', function() {
it('should identify an old wallet as an initialized keyring', function() {
keyringController.configManager.setWallet('something')
const state = keyringController.getState()
assert(state.isInitialized, 'old vault counted as initialized.')
})
/*
it('should use the password to migrate the old vault', function(done) { it('should use the password to migrate the old vault', function(done) {
this.timeout(5000) this.timeout(5000)
keyringController.createNewVault(password, null, function (err, state) { console.log('calling submitPassword')
assert.ifError(err, 'createNewVault threw error') console.dir(keyringController)
keyringController.submitPassword(password, function (err, state) {
assert.ifError(err, 'submitPassword threw error')
function log(str, dat) { console.log(str + ': ' + JSON.stringify(dat)) }
let newAccounts = keyringController.getAccounts() let newAccounts = keyringController.getAccounts()
log('new accounts: ', newAccounts)
let newAccount = ethUtil.addHexPrefix(newAccounts[0]) let newAccount = ethUtil.addHexPrefix(newAccounts[0])
assert.equal(ethUtil.addHexPrefix(newAccount), mockVault.account, 'restored the correct account') assert.equal(ethUtil.addHexPrefix(newAccount), mockVault.account, 'restored the correct account')
const newSeed = keyringController.keyrings[0].mnemonic const newSeed = keyringController.keyrings[0].mnemonic
log('keyringController keyrings', keyringController.keyrings)
assert.equal(newSeed, mockVault.seed, 'seed phrase transferred.') assert.equal(newSeed, mockVault.seed, 'seed phrase transferred.')
assert(configManager.getVault(), 'new type of vault is persisted') assert(configManager.getVault(), 'new type of vault is persisted')
done() done()
}) })
}) })
*/
}) })
}) })

View File

@ -31,7 +31,7 @@ describe('KeyringController', function() {
// Browser crypto is tested in the integration test suite. // Browser crypto is tested in the integration test suite.
keyringController.encryptor = mockEncryptor keyringController.encryptor = mockEncryptor
keyringController.createNewVault(password, null, function (err, state) { keyringController.createNewVaultAndKeychain(password, null, function (err, state) {
done() done()
}) })
}) })
@ -41,11 +41,11 @@ describe('KeyringController', function() {
this.sinon.restore() this.sinon.restore()
}) })
describe('#createNewVault', function () { describe('#createNewVaultAndKeychain', function () {
it('should set a vault on the configManager', function(done) { it('should set a vault on the configManager', function(done) {
keyringController.configManager.setVault(null) keyringController.configManager.setVault(null)
assert(!keyringController.configManager.getVault(), 'no previous vault') assert(!keyringController.configManager.getVault(), 'no previous vault')
keyringController.createNewVault(password, null, function (err, state) { keyringController.createNewVaultAndKeychain(password, null, (err, state) => {
assert.ifError(err) assert.ifError(err)
const vault = keyringController.configManager.getVault() const vault = keyringController.configManager.getVault()
assert(vault, 'vault created') assert(vault, 'vault created')
@ -54,7 +54,7 @@ describe('KeyringController', function() {
}) })
}) })
describe('#restoreKeyring', function(done) { describe('#restoreKeyring', function() {
it(`should pass a keyring's serialized data back to the correct type.`, function() { it(`should pass a keyring's serialized data back to the correct type.`, function() {
keyringController.keyringTypes = [ MockSimpleKeychain ] keyringController.keyringTypes = [ MockSimpleKeychain ]
@ -75,6 +75,16 @@ describe('KeyringController', function() {
}) })
describe('#migrateAndGetKey', function() {
it('should return the key for that password', function(done) {
keyringController.migrateAndGetKey(password)
.then((key) => {
assert(key, 'a key is returned')
done()
})
})
})
}) })