diff --git a/app/core/Encryptor/Encryptor.test.ts b/app/core/Encryptor/Encryptor.test.ts index e4a2ae20ed8..a389648de5a 100644 --- a/app/core/Encryptor/Encryptor.test.ts +++ b/app/core/Encryptor/Encryptor.test.ts @@ -1,6 +1,11 @@ import { NativeModules } from 'react-native'; import { Encryptor } from './Encryptor'; -import { ENCRYPTION_LIBRARY, LEGACY_DERIVATION_OPTIONS } from './constants'; +import { + ShaAlgorithm, + CipherAlgorithm, + ENCRYPTION_LIBRARY, + LEGACY_DERIVATION_OPTIONS, +} from './constants'; const Aes = NativeModules.Aes; const AesForked = NativeModules.AesForked; @@ -55,7 +60,13 @@ describe('Encryptor', () => { { lib: ENCRYPTION_LIBRARY.original, expectedKeyValue: 'mockedKey', - expectedPBKDF2Args: ['testPassword', 'mockedSalt', 5000, 256], + expectedPBKDF2Args: [ + 'testPassword', + 'mockedSalt', + 5000, + 256, + ShaAlgorithm.Sha512, + ], }, ], [ @@ -83,15 +94,21 @@ describe('Encryptor', () => { ); expect(decryptedObject).toEqual(expect.any(Object)); + + const expectedDecryptionArgs = + lib === ENCRYPTION_LIBRARY.original + ? [ + mockVault.cipher, + expectedKeyValue, + mockVault.iv, + CipherAlgorithm.cbc, + ] + : [mockVault.cipher, expectedKeyValue, mockVault.iv]; expect( lib === ENCRYPTION_LIBRARY.original ? decryptAesSpy : decryptAesForkedSpy, - ).toHaveBeenCalledWith( - mockVault.cipher, - expectedKeyValue, - mockVault.iv, - ); + ).toHaveBeenCalledWith(...expectedDecryptionArgs); expect( lib === ENCRYPTION_LIBRARY.original ? pbkdf2AesSpy diff --git a/app/core/Encryptor/Encryptor.ts b/app/core/Encryptor/Encryptor.ts index cf01f619ac1..c086f4e4055 100644 --- a/app/core/Encryptor/Encryptor.ts +++ b/app/core/Encryptor/Encryptor.ts @@ -150,7 +150,7 @@ class Encryptor implements WithKeyEncryptor { key: EncryptionKey, payload: EncryptionResult, ): Promise => { - // TODO: Check for key and payload compatiblity? + // TODO: Check for key and payload compatibility? // We assume that both `payload.lib` and `key.lib` are the same here! const lib = getEncryptionLibrary(payload.lib); diff --git a/app/core/Encryptor/constants.ts b/app/core/Encryptor/constants.ts index fd80b770cbf..d823aa2b0af 100644 --- a/app/core/Encryptor/constants.ts +++ b/app/core/Encryptor/constants.ts @@ -17,10 +17,34 @@ export enum KeyDerivationIteration { OWASP2023Default = 900_000, } +/** + * Supported SHA algorithms in react-native-aes v3.0.3 + */ +export enum ShaAlgorithm { + Sha256 = 'sha256', + Sha512 = 'sha512', +} + +/** + * Supported cipher algorithms in react-native-aes v3.0.3 + * + * Important Note: Make sure to validate the compatibility of the algorithm with the underlying library. + * The react-native-aes-crypto does a string validation for the algorithm, so it's important to make sure + * we're using the correct string. + * + * References: + * - encrypt: https://github.com/MetaMask/metamask-mobile/pull/9947#:~:text=When-,encrypting,-and%20decypting%20the + * - decrypt: https://github.com/MetaMask/metamask-mobile/pull/9947#:~:text=When%20encrypting%20and-,decypting,-the%20library%20uses + */ +export enum CipherAlgorithm { + cbc = 'aes-cbc-pkcs7padding', + ctr = 'aes-ctr-pkcs5padding', +} + /** * Used as a "tag" to identify the underlying encryption library. * - * When no tag is speficied, this means our "forked" encryption library has been used. + * When no tag is specified, this means our "forked" encryption library has been used. */ export const ENCRYPTION_LIBRARY = { original: 'original', diff --git a/app/core/Encryptor/lib.ts b/app/core/Encryptor/lib.ts index 676578b0926..e26f3bbe940 100644 --- a/app/core/Encryptor/lib.ts +++ b/app/core/Encryptor/lib.ts @@ -1,9 +1,11 @@ import { NativeModules } from 'react-native'; import { - ENCRYPTION_LIBRARY, KDF_ALGORITHM, - LEGACY_DERIVATION_OPTIONS, + ShaAlgorithm, + CipherAlgorithm, + ENCRYPTION_LIBRARY, SHA256_DIGEST_LENGTH, + LEGACY_DERIVATION_OPTIONS, } from './constants'; import { EncryptionLibrary, KeyDerivationOptions } from './types'; @@ -29,7 +31,14 @@ class AesEncryptionLibrary implements EncryptionLibrary { password, salt, opts.params.iterations, + // We're using SHA512 but returning a key with length 256 bits. + // Truncating the output to 256 bits is intentional and considered safe. + // + // References: + // - https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf + // - https://eprint.iacr.org/2010/548.pdf SHA256_DIGEST_LENGTH, + ShaAlgorithm.Sha512, ); }; @@ -39,10 +48,10 @@ class AesEncryptionLibrary implements EncryptionLibrary { await Aes.randomKey(size); encrypt = async (data: string, key: string, iv: unknown): Promise => - await Aes.encrypt(data, key, iv); + await Aes.encrypt(data, key, iv, CipherAlgorithm.cbc); decrypt = async (data: string, key: string, iv: unknown): Promise => - await Aes.decrypt(data, key, iv); + await Aes.decrypt(data, key, iv, CipherAlgorithm.cbc); } class AesForkedEncryptionLibrary implements EncryptionLibrary { diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e13b075ea46..f8c5129c5de 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -500,8 +500,8 @@ PODS: - React-jsinspector (0.72.15) - React-logger (0.72.15): - glog - - react-native-aes (1.3.9): - - React + - react-native-aes (3.0.3): + - React-Core - react-native-background-timer (2.1.1): - React - react-native-ble-plx (3.1.2): @@ -1199,7 +1199,7 @@ SPEC CHECKSUMS: React-jsiexecutor: ce8ecfcd3b7dbc9cb65a661110be17f5afd18aa3 React-jsinspector: b86a8abae760c28d69366bbc1d991561e51341ed React-logger: ed7c9e01e58529065e7da6bf8318baf15024283e - react-native-aes: ff31f0dd4c791eb423a631ee04570fcf3c618924 + react-native-aes: 0143040f4e0cb19296b69b4acc7ddd8d3df9d62d react-native-background-timer: 1b6e6b4e10f1b74c367a1fdc3c72b67c619b222b react-native-ble-plx: f0557dbb6bd1f26cca75a67b5f33cfc7f7f9abed react-native-blob-jsi-helper: 13c10135af4614dbc0712afba5960784cd44f043 diff --git a/package.json b/package.json index 00fd01bc3b3..a8fe59991eb 100644 --- a/package.json +++ b/package.json @@ -275,7 +275,7 @@ "randomfill": "^1.0.4", "react": "18.2.0", "react-native": "0.72.15", - "react-native-aes-crypto": "1.3.9", + "react-native-aes-crypto": "3.0.3", "react-native-aes-crypto-forked": "git+https://github.com/MetaMask/react-native-aes-crypto-forked.git#397d5db5250e8e7408294807965b5b9fd4ca6a25", "react-native-animatable": "^1.3.3", "react-native-background-timer": "2.1.1", diff --git a/patches/react-native-aes-crypto+1.3.9.patch b/patches/react-native-aes-crypto+1.3.9.patch deleted file mode 100644 index f20d70f5905..00000000000 --- a/patches/react-native-aes-crypto+1.3.9.patch +++ /dev/null @@ -1,49 +0,0 @@ -diff --git a/node_modules/react-native-aes-crypto/android/build.gradle b/node_modules/react-native-aes-crypto/android/build.gradle -index a68c5b7..2cb6ef1 100644 ---- a/node_modules/react-native-aes-crypto/android/build.gradle -+++ b/node_modules/react-native-aes-crypto/android/build.gradle -@@ -20,7 +20,7 @@ buildscript { - } - - apply plugin: 'com.android.library' --apply plugin: 'maven' -+apply plugin: 'maven-publish' - - // Matches values in recent template from React Native 0.59 / 0.60 - // https://github.com/facebook/react-native/blob/0.59-stable/template/android/build.gradle#L5-L9 -diff --git a/node_modules/react-native-aes-crypto/android/src/main/java/com/tectiv3/aes/RCTAes.java b/node_modules/react-native-aes-crypto/android/src/main/java/com/tectiv3/aes/RCTAes.java -index 6843507..a13ec93 100755 ---- a/node_modules/react-native-aes-crypto/android/src/main/java/com/tectiv3/aes/RCTAes.java -+++ b/node_modules/react-native-aes-crypto/android/src/main/java/com/tectiv3/aes/RCTAes.java -@@ -83,6 +83,14 @@ public class RCTAes extends ReactContextBaseJavaModule { - } - } - -+ @ReactMethod(isBlockingSynchronousMethod = true) -+ public String pbkdf2Sync(String pwd, String salt, Integer cost, Integer length) -+ throws NoSuchAlgorithmException, InvalidKeySpecException, UnsupportedEncodingException -+ { -+ String strs = pbkdf2(pwd, salt, cost, length); -+ return strs; -+ } -+ - @ReactMethod - public void hmac256(String data, String pwd, Promise promise) { - try { -diff --git a/node_modules/react-native-aes-crypto/ios/RCTAes/RCTAes.m b/node_modules/react-native-aes-crypto/ios/RCTAes/RCTAes.m -index ded93b6..c3a6461 100755 ---- a/node_modules/react-native-aes-crypto/ios/RCTAes/RCTAes.m -+++ b/node_modules/react-native-aes-crypto/ios/RCTAes/RCTAes.m -@@ -51,6 +51,12 @@ @implementation RCTAes - } - } - -+RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(pbkdf2Sync:(NSString *)password salt:(NSString *)salt -+ cost:(NSInteger)cost length:(NSInteger)length){ -+ -+ return [AesCrypt pbkdf2:password salt:salt cost:cost length:length]; -+} -+ - RCT_EXPORT_METHOD(hmac256:(NSString *)base64 key:(NSString *)key - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) { diff --git a/yarn.lock b/yarn.lock index eca9fede68e..b19b1d4002f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25444,10 +25444,10 @@ react-mixin@^3.0.5: version "1.2.1" resolved "git+https://github.com/MetaMask/react-native-aes-crypto-forked.git#397d5db5250e8e7408294807965b5b9fd4ca6a25" -react-native-aes-crypto@1.3.9: - version "1.3.9" - resolved "https://registry.yarnpkg.com/react-native-aes-crypto/-/react-native-aes-crypto-1.3.9.tgz#6f068535462c1b7d7d70a4152d519ae9911c3e69" - integrity sha512-xMl/VCWC9riSyidFsCjXQbR+BIxCZyQtRfVR4dLZbHsk3AMZnVw8VojKVIXRmQtSm3uEMdTUvcLm9HaB3Xf+Cw== +react-native-aes-crypto@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/react-native-aes-crypto/-/react-native-aes-crypto-3.0.3.tgz#147a8240c92719ddb925ec6c3e46dcfb69f9f7db" + integrity sha512-6yJjAix83xMDMCDUQLVpoHQMkTfeWoKrDXWP2Yrl68o2053aHPk3hTW6sCTYLj5HmftZ9S9DQ3DelP7WVlIDtw== react-native-animatable@1.3.3, react-native-animatable@^1.3.3: version "1.3.3"