diff --git a/package-lock.json b/package-lock.json index ad1d694..39420b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "web-ext", - "version": "2.3.2", + "version": "2.3.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index f0f1db8..e733378 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "web-ext", - "version": "2.3.2", + "version": "2.3.3", "description": "Adds Blockstack support to a web broswer.", "main": "blockstack-background.js", "private": true, diff --git a/src/common/components/bs-login/bs-login.ts b/src/common/components/bs-login/bs-login.ts index 6993af3..f66cab0 100644 --- a/src/common/components/bs-login/bs-login.ts +++ b/src/common/components/bs-login/bs-login.ts @@ -7,7 +7,7 @@ import * as bip32 from 'bip32'; import { BIP32Interface } from 'bip32'; import { randomBytes } from 'crypto'; import { dispatch, commit } from '../../vuex/remote-interface'; -import { encrypt } from '../../util'; +import { encrypt, createIv } from '../../util'; import { FieldFlags } from 'vee-validate'; import { StateType } from '../../vuex/stores/types/state'; @@ -85,10 +85,11 @@ export default (Vue as VVue).component('bs-login', { } else { throw new Error('Tried to initialize a wallet with a bad phrase'); } - return encrypt(this.phrase, this.pass).then(encryptedBackupPhrase => { + const iv = await createIv(); + return encrypt(this.phrase, this.pass, iv).then(encryptedBackupPhrase => { console.log('Creating account w/ enc phrase: "' + encryptedBackupPhrase + '"!'); return dispatch('account/createAccount', - { email: this.email, encryptedBackupPhrase, masterKeychain: masterKeychain.toBase58() }); + { email: this.email, encryptedBackupPhrase, iv, masterKeychain: masterKeychain.toBase58() }); }); }, async initializeIdentity(autoGenProfile?: boolean) { diff --git a/src/common/util.ts b/src/common/util.ts index 741d320..c797197 100644 --- a/src/common/util.ts +++ b/src/common/util.ts @@ -1,6 +1,6 @@ import { payments, ECPair } from 'bitcoinjs-lib'; import { BIP32Interface } from 'bip32'; -import { createCipher, createDecipher, randomBytes } from 'crypto'; +import { randomBytes, createDecipheriv, createCipheriv, createHash } from 'crypto'; import { StateType } from './vuex/stores/types/state'; import { TokenSigner } from 'jsontokens'; import { connectToGaiaHub, uploadToGaiaHub } from 'blockstack'; @@ -11,15 +11,21 @@ export function getAddress(node: BIP32Interface, network?: any) { return payments.p2pkh({ pubkey: node.publicKey }).address; } -export async function encrypt(data: string, key: string): Promise { - const cipher = createCipher('aes192', key); +export async function createIv() { + return createHash('sha256').update(randomBytes(16)).digest('hex').slice(0, 16); +} + +export async function encrypt(data: string, key: string, iv: string): Promise { + const k = createHash('sha256').update(key).digest(); + const cipher = createCipheriv('aes256', k, iv); let enc = cipher.update(data, 'utf8', 'hex'); enc += cipher.final('hex'); return enc; } -export async function decrypt(data: string, key: string): Promise { - const decipher = createDecipher('aes192', key); +export async function decrypt(data: string, key: string, iv: string): Promise { + const k = createHash('sha256').update(key).digest(); + const decipher = createDecipheriv('aes256', k, iv); let dec = decipher.update(data, 'hex', 'utf8'); dec += decipher.final('utf8'); return dec; diff --git a/src/common/vuex/stores/account.store.ts b/src/common/vuex/stores/account.store.ts index be18767..54e7f82 100644 --- a/src/common/vuex/stores/account.store.ts +++ b/src/common/vuex/stores/account.store.ts @@ -2,7 +2,7 @@ import { Module } from 'vuex'; import { WrappedKeychain } from '../../data/wrapped-keychain'; import { AccountStateType, SATOSHIS_IN_BTC } from './types/account.state'; import { StateType } from './types/state'; -import { decrypt, encrypt } from '../../util'; +import { decrypt, encrypt, createIv } from '../../util'; import { validateMnemonic, mnemonicToSeed } from 'bip39'; import Axios, { AxiosPromise, AxiosResponse } from 'axios'; import { config, transactions, network } from 'blockstack'; @@ -13,6 +13,7 @@ function makeState(): AccountStateType { accountCreated: false, email: '', encryptedBackupPhrase: '', + iv: null, bitcoinAccount: { publicKeychain: '', addresses: [] as string[], @@ -51,9 +52,10 @@ export const accountModule: Module = { async reset({ commit }) { commit('reset'); }, - async createAccount({ commit, state }, { email, encryptedBackupPhrase, masterKeychain, identitiesToGenerate }: { + async createAccount({ commit, state }, { email, encryptedBackupPhrase, iv, masterKeychain, identitiesToGenerate }: { email?: string, encryptedBackupPhrase: string, + iv: string, masterKeychain: string, identitiesToGenerate?: number }) { @@ -72,6 +74,7 @@ export const accountModule: Module = { email: email || state.email, accountCreated: true, encryptedBackupPhrase, + iv, bitcoinAccount: { publicKeychain: wrapped.bitcoinPublicKeychain.toBase58(), addresses: [firstBitcoinAddress], @@ -81,10 +84,11 @@ export const accountModule: Module = { } as Partial); }, async changePassword({ state, commit }, { newpass, oldpass }: { newpass: string, oldpass: string }) { - const phrase = await decrypt(state.encryptedBackupPhrase, oldpass); + const phrase = await decrypt(state.encryptedBackupPhrase, oldpass, state.iv); if(!validateMnemonic(phrase)) throw new Error('Wrong password!'); - const encryptedBackupPhrase = await encrypt(phrase, newpass); - commit('update', { encryptedBackupPhrase }); + const iv = await createIv(); + const encryptedBackupPhrase = await encrypt(phrase, newpass, iv); + commit('update', { iv, encryptedBackupPhrase }); }, async refreshBalances({ state, commit, rootState }) { const balances: { [key: string]: number } = { }; @@ -105,7 +109,7 @@ export const accountModule: Module = { }, async withdraw({ state, dispatch }, { to, amount, password }: { to: string, amount: number, password: string }) { if(amount > state.bitcoinAccount.balances[0]) throw new Error('Will not overwithdraw from the account.') - const phrase = await decrypt(state.encryptedBackupPhrase, password); + const phrase = await decrypt(state.encryptedBackupPhrase, password, state.iv); if(!validateMnemonic(phrase)) throw new Error('Wrong password!'); const seedBuffer = mnemonicToSeed(phrase); const masterKeychain = WrappedNode.fromSeed(seedBuffer); diff --git a/src/common/vuex/stores/identity.store.ts b/src/common/vuex/stores/identity.store.ts index 197d8f6..4130991 100644 --- a/src/common/vuex/stores/identity.store.ts +++ b/src/common/vuex/stores/identity.store.ts @@ -183,7 +183,7 @@ export const identityModule: Module = { commit('reset'); }, async create({ commit, state, rootState }, { password, index }: { password: string, index?: number }) { - const phrase = await decrypt(rootState.account.encryptedBackupPhrase, password); + const phrase = await decrypt(rootState.account.encryptedBackupPhrase, password, rootState.account.iv); if(!validateMnemonic(phrase)) throw new Error('Wrong password!'); if(!index) { if(!state.identities.length) index = 0; diff --git a/src/common/vuex/stores/types/account.state.ts b/src/common/vuex/stores/types/account.state.ts index fb4117c..63b0305 100644 --- a/src/common/vuex/stores/types/account.state.ts +++ b/src/common/vuex/stores/types/account.state.ts @@ -4,6 +4,7 @@ export interface AccountStateType { accountCreated: boolean; email: string; // dumb =T encryptedBackupPhrase: string; + iv: string; bitcoinAccount: { publicKeychain: string; addresses: string[]; diff --git a/src/main/app/app.ts b/src/main/app/app.ts index 42d53c5..64cb6bc 100644 --- a/src/main/app/app.ts +++ b/src/main/app/app.ts @@ -100,7 +100,7 @@ export default (Vue as VVue).extend({ type: 'password' }, onConfirm: (value) => { - decrypt(this.$store.state.account.encryptedBackupPhrase, value).then(phrase => { + decrypt(this.$store.state.account.encryptedBackupPhrase, value, this.$store.state.account.iv).then(phrase => { this.$dialog.alert({ title: 'Backup Phrase', message: `
diff --git a/src/manifest.json b/src/manifest.json index 87f7bbc..4456858 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "(Unofficial) Blockstack Extension", "short_name": "uBlockstack", - "version": "2.3.2", + "version": "2.3.3", "author": "Michael Fedora", "homepage_url": "https://github.com/michaelfedora/unofficial-blockstack-extension", diff --git a/src/popup/app/components/settings.ts b/src/popup/app/components/settings.ts index 2c329fb..355f52d 100644 --- a/src/popup/app/components/settings.ts +++ b/src/popup/app/components/settings.ts @@ -161,7 +161,7 @@ export default (Vue as VVue).component('bs-popup-settings', { type: 'password' }, onConfirm: (value) => { - decrypt(this.$store.state.account.encryptedBackupPhrase, value).then(phrase => { + decrypt(this.$store.state.account.encryptedBackupPhrase, value, this.$store.state.account.iv).then(phrase => { this.$dialog.alert({ title: 'Backup Phrase', message: `