Skip to content

Commit

Permalink
Improvement/migrate aes crypto lib (#1755)
Browse files Browse the repository at this point in the history
* Update aes crypto lib working

* Working on iOS

* init migration

* Recreate vault on login and fingerprint

* Fix selectedAddress

* Add passwordSet again

* Fix recreate vault

* Improve getting simple key pair accounts

* Update tests

* Make sure selectedAddress is available

* Fix for multiple imported accounts

* remove unecessary async await

* Rename and remove unecessary code

* Using logger now

* Check new error on android
  • Loading branch information
andrepimenta authored Aug 12, 2020
1 parent 91b4840 commit 84cc298
Show file tree
Hide file tree
Showing 13 changed files with 158 additions and 64 deletions.
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ buildscript {
ext {
buildToolsVersion = "28.0.3"
minSdkVersion = 19
compileSdkVersion = 28
compileSdkVersion = 29
targetSdkVersion = 28
kotlin_version = "1.3.50"
kotlinVersion = "$kotlin_version"
Expand Down
17 changes: 15 additions & 2 deletions app/components/Views/Entry/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import DeeplinkManager from '../../../core/DeeplinkManager';
import Logger from '../../../util/Logger';
import Device from '../../../util/Device';
import SplashScreen from 'react-native-splash-screen';
import { recreateVaultWithSamePassword } from '../../../core/Vault';

/**
* Entry Screen that decides which screen to show
Expand Down Expand Up @@ -76,7 +77,11 @@ class Entry extends PureComponent {
/**
* Action to set onboarding wizard step
*/
setOnboardingWizardStep: PropTypes.func
setOnboardingWizardStep: PropTypes.func,
/**
* A string representing the selected address => account
*/
selectedAddress: PropTypes.string
};

state = {
Expand Down Expand Up @@ -162,6 +167,11 @@ class Entry extends PureComponent {
// Restore vault with existing credentials

await KeyringController.submitPassword(credentials.password);
const encryptionLib = await AsyncStorage.getItem('@MetaMask:encryptionLib');
if (encryptionLib !== 'original') {
await recreateVaultWithSamePassword(credentials.password, this.props.selectedAddress);
await AsyncStorage.setItem('@MetaMask:encryptionLib', 'original');
}
// Get onboarding wizard state
const onboardingWizard = await AsyncStorage.getItem('@MetaMask:onboardingWizard');
// Check if user passed through metrics opt-in screen
Expand Down Expand Up @@ -233,7 +243,10 @@ const mapDispatchToProps = dispatch => ({
});

const mapStateToProps = state => ({
passwordSet: state.user.passwordSet
passwordSet: state.user.passwordSet,
selectedAddress:
state.engine.backgroundState.PreferencesController &&
state.engine.backgroundState.PreferencesController.selectedAddress
});

export default connect(
Expand Down
5 changes: 5 additions & 0 deletions app/components/Views/Entry/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ describe('Entry', () => {
const initialState = {
user: {
passwordSet: false
},
engine: {
backgroundState: {
PreferencesController: {}
}
}
};

Expand Down
21 changes: 18 additions & 3 deletions app/components/Views/Login/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ANALYTICS_EVENT_OPTS } from '../../../util/analytics';
import Device from '../../../util/Device';
import { OutlinedTextField } from 'react-native-material-textfield';
import BiometryButton from '../../UI/BiometryButton';
import { recreateVaultWithSamePassword } from '../../../core/Vault';

const styles = StyleSheet.create({
mainWrapper: {
Expand Down Expand Up @@ -92,6 +93,7 @@ const styles = StyleSheet.create({

const PASSCODE_NOT_SET_ERROR = 'Error: Passcode not set.';
const WRONG_PASSWORD_ERROR = 'Error: Decrypt failed';
const WRONG_PASSWORD_ERROR_ANDROID = 'Error: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT';

/**
* View where returning users can authenticate
Expand Down Expand Up @@ -121,7 +123,11 @@ class Login extends PureComponent {
/**
* Boolean flag that determines if password has been set
*/
passwordSet: PropTypes.bool
passwordSet: PropTypes.bool,
/**
* A string representing the selected address => account
*/
selectedAddress: PropTypes.string
};

state = {
Expand Down Expand Up @@ -188,6 +194,11 @@ class Login extends PureComponent {

// Restore vault with user entered password
await KeyringController.submitPassword(this.state.password);
const encryptionLib = await AsyncStorage.getItem('@MetaMask:encryptionLib');
if (encryptionLib !== 'original') {
await recreateVaultWithSamePassword(this.state.password, this.props.selectedAddress);
await AsyncStorage.setItem('@MetaMask:encryptionLib', 'original');
}
if (this.state.biometryChoice && this.state.biometryType) {
const authOptions = {
accessControl: this.state.biometryChoice
Expand Down Expand Up @@ -232,7 +243,10 @@ class Login extends PureComponent {
this.setState({ loading: false });
} catch (error) {
// Should we force people to enable passcode / biometrics?
if (error.toString().toLowerCase() === WRONG_PASSWORD_ERROR.toLowerCase()) {
if (
error.toString().toLowerCase() === WRONG_PASSWORD_ERROR.toLowerCase() ||
error.toString().toLowerCase() === WRONG_PASSWORD_ERROR_ANDROID.toLowerCase()
) {
this.setState({ loading: false, error: strings('login.invalid_password') });
} else if (error.toString() === PASSCODE_NOT_SET_ERROR) {
Alert.alert(
Expand Down Expand Up @@ -402,7 +416,8 @@ const mapStateToProps = state => ({
accountsLength: Object.keys(state.engine.backgroundState.AccountTrackerController.accounts).length,
tokensLength: state.engine.backgroundState.AssetsController.tokens.length,
networkType: state.engine.backgroundState.NetworkController.provider.type,
passwordSet: state.user.passwordSet
passwordSet: state.user.passwordSet,
selectedAddress: state.engine.backgroundState.PreferencesController.selectedAddress
});

const mapDispatchToProps = dispatch => ({
Expand Down
3 changes: 2 additions & 1 deletion app/components/Views/Login/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ describe('Login', () => {
},
AssetsController: {
tokens: []
}
},
PreferencesController: {}
}
},
user: {
Expand Down
25 changes: 15 additions & 10 deletions app/core/Encryptor.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { NativeModules } from 'react-native';
const Aes = NativeModules.Aes;
const AesForked = NativeModules.AesForked;

/**
* Class that exposes two public methods: Encrypt and Decrypt
Expand All @@ -17,16 +18,20 @@ export default class Encryptor {
return b64encoded;
}

_generateKey = (password, salt) => Aes.pbkdf2(password, salt);
_generateKey = (password, salt, lib) =>
lib === 'original' ? Aes.pbkdf2(password, salt, 5000, 256) : AesForked.pbkdf2(password, salt);

_keyFromPassword = (password, salt) => this._generateKey(password, salt);
_keyFromPassword = (password, salt, lib) => this._generateKey(password, salt, lib);

_encryptWithKey = (text, keyBase64) => {
const ivBase64 = this._generateSalt(32);
return Aes.encrypt(text, keyBase64, ivBase64).then(cipher => ({ cipher, iv: ivBase64 }));
_encryptWithKey = async (text, keyBase64) => {
const iv = await Aes.randomKey(16);
return Aes.encrypt(text, keyBase64, iv).then(cipher => ({ cipher, iv }));
};

_decryptWithKey = (encryptedData, key) => Aes.decrypt(encryptedData.cipher, key, encryptedData.iv);
_decryptWithKey = (encryptedData, key, lib) =>
lib === 'original'
? Aes.decrypt(encryptedData.cipher, key, encryptedData.iv)
: AesForked.decrypt(encryptedData.cipher, key, encryptedData.iv);

/**
* Encrypts a JS object using a password (and AES encryption with native libraries)
Expand All @@ -37,9 +42,10 @@ export default class Encryptor {
*/
encrypt = async (password, object) => {
const salt = this._generateSalt(16);
const key = await this._keyFromPassword(password, salt);
const key = await this._keyFromPassword(password, salt, 'original');
const result = await this._encryptWithKey(JSON.stringify(object), key);
result.salt = salt;
result.lib = 'original';
return JSON.stringify(result);
};

Expand All @@ -53,9 +59,8 @@ export default class Encryptor {
*/
decrypt = async (password, encryptedString) => {
const encryptedData = JSON.parse(encryptedString);
const key = await this._keyFromPassword(password, encryptedData.salt);
const data = await this._decryptWithKey(encryptedData, key);

const key = await this._keyFromPassword(password, encryptedData.salt, encryptedData.lib);
const data = await this._decryptWithKey(encryptedData, key, encryptedData.lib);
return JSON.parse(data);
};
}
72 changes: 72 additions & 0 deletions app/core/Vault.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import Engine from './Engine';
import Logger from '../util/Logger';

/**
* Returns current vault seed phrase
* It does it using an empty password or a password set by the user
* depending on the state the app is currently in
*/
export const getSeedPhrase = async (password = '') => {
const { KeyringController } = Engine.context;
const mnemonic = await KeyringController.exportSeedPhrase(password);
return JSON.stringify(mnemonic).replace(/"/g, '');
};

/**
* Recreates a vault with the same password for the purpose of using the newest encryption methods
*
* @param password - Password to recreate and set the vault with
*/
export const recreateVaultWithSamePassword = async (password = '', selectedAddress) => {
const { KeyringController, PreferencesController } = Engine.context;
const seedPhrase = await getSeedPhrase(password);

let importedAccounts = [];
try {
// Get imported accounts
const simpleKeyrings = KeyringController.state.keyrings.filter(keyring => keyring.type === 'Simple Key Pair');
for (let i = 0; i < simpleKeyrings.length; i++) {
const simpleKeyring = simpleKeyrings[i];
const simpleKeyringAccounts = await Promise.all(
simpleKeyring.accounts.map(account => KeyringController.exportAccount(password, account))
);
importedAccounts = [...importedAccounts, ...simpleKeyringAccounts];
}
} catch (e) {
Logger.error(e, 'error while trying to get imported accounts on recreate vault');
}

// Recreate keyring with password given to this method
await KeyringController.createNewVaultAndRestore(password, seedPhrase);

// Get props to restore vault
const hdKeyring = KeyringController.state.keyrings[0];
const existingAccountCount = hdKeyring.accounts.length;
let preferencesControllerState = PreferencesController.state;

// Create previous accounts again
for (let i = 0; i < existingAccountCount - 1; i++) {
await KeyringController.addNewAccount();
}

try {
// Import imported accounts again
for (let i = 0; i < importedAccounts.length; i++) {
await KeyringController.importAccountWithStrategy('privateKey', [importedAccounts[i]]);
}
} catch (e) {
Logger.error(e, 'error while trying to import accounts on recreate vault');
}

// Reset preferencesControllerState
preferencesControllerState = PreferencesController.state;

// Set preferencesControllerState again
await PreferencesController.update(preferencesControllerState);
// Reselect previous selected account if still available
if (hdKeyring.accounts.includes(selectedAddress)) {
PreferencesController.setSelectedAddress(selectedAddress);
} else {
PreferencesController.setSelectedAddress(hdKeyring[0]);
}
};
28 changes: 14 additions & 14 deletions ios/MetaMask.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
48AD4B0AABCB447B99B85DC4 /* Roboto-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 684F2C84313849199863B5FE /* Roboto-Black.ttf */; };
49D8E62C506F4A63889EEC7F /* branch.json in Resources */ = {isa = PBXBuildFile; fileRef = FE3C9A2458A1416290DEDAD4 /* branch.json */; };
4CEFC9E34A8D4288BFE2F85A /* Roboto-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = BB8BA2D3C0354D6090B56A8A /* Roboto-Light.ttf */; };
654377D9243E1E4E00571B9C /* libRCTAes.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 654377D1243E1E1F00571B9C /* libRCTAes.a */; };
650F2B9D24DC5FF200C3B9C4 /* libRCTAesForked.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 650F2B9C24DC5FEC00C3B9C4 /* libRCTAesForked.a */; };
654378B0243E2ADC00571B9C /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 654378AF243E2ADC00571B9C /* File.swift */; };
7C0226ABD9694AEDBAF3016F /* Roboto-ThinItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CF552F79C77A4184A690513A /* Roboto-ThinItalic.ttf */; };
7E08FB90F3754D47994208B4 /* Roboto-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D9A37B5BF2914CF1B49EEF80 /* Roboto-Thin.ttf */; };
Expand Down Expand Up @@ -138,12 +138,12 @@
remoteGlobalIDString = 7C170C291A4A02F500D9E0F2;
remoteInfo = Mixpanel;
};
654377D0243E1E1F00571B9C /* PBXContainerItemProxy */ = {
650F2B9B24DC5FEC00C3B9C4 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 654377CC243E1E1E00571B9C /* RCTAes.xcodeproj */;
containerPortal = 650F2B9724DC5FEB00C3B9C4 /* RCTAesForked.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 32D980DD1BE9F11C00FA27E5;
remoteInfo = RCTAes;
remoteInfo = RCTAesForked;
};
/* End PBXContainerItemProxy section */

Expand Down Expand Up @@ -204,7 +204,7 @@
58572D81B5D54ED79A16A16D /* EuclidCircularB-RegularItalic.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "EuclidCircularB-RegularItalic.otf"; path = "../app/fonts/EuclidCircularB-RegularItalic.otf"; sourceTree = "<group>"; };
5D7956F8525C4A45A2A555C3 /* Roboto-Italic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-Italic.ttf"; path = "../app/fonts/Roboto-Italic.ttf"; sourceTree = "<group>"; };
5E32A09A7BDC431FA403BA73 /* FontAwesome.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf"; sourceTree = "<group>"; };
654377CC243E1E1E00571B9C /* RCTAes.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAes.xcodeproj; path = "../node_modules/react-native-aes-crypto/ios/RCTAes.xcodeproj"; sourceTree = "<group>"; };
650F2B9724DC5FEB00C3B9C4 /* RCTAesForked.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAesForked.xcodeproj; path = "../node_modules/react-native-aes-crypto-forked/ios/RCTAesForked.xcodeproj"; sourceTree = "<group>"; };
654378AE243E2ADB00571B9C /* MetaMask-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MetaMask-Bridging-Header.h"; sourceTree = "<group>"; };
654378AF243E2ADC00571B9C /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = "<group>"; };
67FBD519E04742E0AF191782 /* EuclidCircularB-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "EuclidCircularB-Bold.otf"; path = "../app/fonts/EuclidCircularB-Bold.otf"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -245,7 +245,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
654377D9243E1E4E00571B9C /* libRCTAes.a in Frameworks */,
650F2B9D24DC5FF200C3B9C4 /* libRCTAesForked.a in Frameworks */,
153C1ABB2217BCDC0088EFE0 /* JavaScriptCore.framework in Frameworks */,
15ACC9FC22655C3A0063978B /* Lottie.framework in Frameworks */,
15F7795E22A1B7B500B1DF8C /* Mixpanel.framework in Frameworks */,
Expand Down Expand Up @@ -409,18 +409,18 @@
name = Resources;
sourceTree = "<group>";
};
654377CD243E1E1E00571B9C /* Products */ = {
650F2B9824DC5FEB00C3B9C4 /* Products */ = {
isa = PBXGroup;
children = (
654377D1243E1E1F00571B9C /* libRCTAes.a */,
650F2B9C24DC5FEC00C3B9C4 /* libRCTAesForked.a */,
);
name = Products;
sourceTree = "<group>";
};
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
isa = PBXGroup;
children = (
654377CC243E1E1E00571B9C /* RCTAes.xcodeproj */,
650F2B9724DC5FEB00C3B9C4 /* RCTAesForked.xcodeproj */,
153F84C42319B8DA00C19B63 /* BranchSDK.xcodeproj */,
15F7794F22A1B79400B1DF8C /* Mixpanel.xcodeproj */,
4379F36F969347758D1A9F96 /* Lottie.xcodeproj */,
Expand Down Expand Up @@ -545,8 +545,8 @@
ProjectRef = 15F7794F22A1B79400B1DF8C /* Mixpanel.xcodeproj */;
},
{
ProductGroup = 654377CD243E1E1E00571B9C /* Products */;
ProjectRef = 654377CC243E1E1E00571B9C /* RCTAes.xcodeproj */;
ProductGroup = 650F2B9824DC5FEB00C3B9C4 /* Products */;
ProjectRef = 650F2B9724DC5FEB00C3B9C4 /* RCTAesForked.xcodeproj */;
},
);
projectRoot = "";
Expand Down Expand Up @@ -620,11 +620,11 @@
remoteRef = 15F7795C22A1B79400B1DF8C /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
654377D1243E1E1F00571B9C /* libRCTAes.a */ = {
650F2B9C24DC5FEC00C3B9C4 /* libRCTAesForked.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTAes.a;
remoteRef = 654377D0243E1E1F00571B9C /* PBXContainerItemProxy */;
path = libRCTAesForked.a;
remoteRef = 650F2B9B24DC5FEC00C3B9C4 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
Expand Down
6 changes: 6 additions & 0 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ PODS:
- React-cxxreact (= 0.62.2)
- React-jsi (= 0.62.2)
- React-jsinspector (0.62.2)
- react-native-aes (1.3.9):
- React
- react-native-background-timer (2.1.1):
- React
- react-native-blur (0.8.0):
Expand Down Expand Up @@ -423,6 +425,7 @@ DEPENDENCIES:
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
- react-native-aes (from `../node_modules/react-native-aes-crypto`)
- react-native-background-timer (from `../node_modules/react-native-background-timer`)
- "react-native-blur (from `../node_modules/@react-native-community/blur`)"
- react-native-branch (from `../node_modules/react-native-branch`)
Expand Down Expand Up @@ -515,6 +518,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/jsiexecutor"
React-jsinspector:
:path: "../node_modules/react-native/ReactCommon/jsinspector"
react-native-aes:
:path: "../node_modules/react-native-aes-crypto"
react-native-background-timer:
:path: "../node_modules/react-native-background-timer"
react-native-blur:
Expand Down Expand Up @@ -624,6 +629,7 @@ SPEC CHECKSUMS:
React-jsi: b6dc94a6a12ff98e8877287a0b7620d365201161
React-jsiexecutor: 1540d1c01bb493ae3124ed83351b1b6a155db7da
React-jsinspector: 512e560d0e985d0e8c479a54a4e5c147a9c83493
react-native-aes: ff31f0dd4c791eb423a631ee04570fcf3c618924
react-native-background-timer: 1b6e6b4e10f1b74c367a1fdc3c72b67c619b222b
react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c
react-native-branch: ed90b087f2ff6a2f81643ead0b321531877f2e69
Expand Down
Loading

0 comments on commit 84cc298

Please sign in to comment.