diff --git a/app/client/mobile/ios/Databag.xcodeproj/project.pbxproj b/app/client/mobile/ios/Databag.xcodeproj/project.pbxproj index 52f06f34..e97fb077 100644 --- a/app/client/mobile/ios/Databag.xcodeproj/project.pbxproj +++ b/app/client/mobile/ios/Databag.xcodeproj/project.pbxproj @@ -605,7 +605,10 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; @@ -677,7 +680,10 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; diff --git a/app/client/mobile/ios/Databag/PrivacyInfo.xcprivacy b/app/client/mobile/ios/Databag/PrivacyInfo.xcprivacy index 5b037f0c..d634def7 100644 --- a/app/client/mobile/ios/Databag/PrivacyInfo.xcprivacy +++ b/app/client/mobile/ios/Databag/PrivacyInfo.xcprivacy @@ -9,8 +9,8 @@ NSPrivacyAccessedAPICategoryFileTimestamp NSPrivacyAccessedAPITypeReasons - C617.1 3B52.1 + C617.1 diff --git a/app/client/mobile/ios/Podfile.lock b/app/client/mobile/ios/Podfile.lock index 784ca797..6799d3fe 100644 --- a/app/client/mobile/ios/Podfile.lock +++ b/app/client/mobile/ios/Podfile.lock @@ -956,6 +956,8 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - react-native-rsa-native (2.0.5): + - React - react-native-safe-area-context (4.11.0): - React-Core - react-native-sqlite-storage (6.0.1): @@ -1210,15 +1212,15 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNImageCropPicker (0.39.0): + - RNImageCropPicker (0.41.2): - React-Core - React-RCTImage - - RNImageCropPicker/QBImagePickerController (= 0.39.0) - - TOCropViewController - - RNImageCropPicker/QBImagePickerController (0.39.0): + - RNImageCropPicker/QBImagePickerController (= 0.41.2) + - TOCropViewController (~> 2.7.4) + - RNImageCropPicker/QBImagePickerController (0.41.2): - React-Core - React-RCTImage - - TOCropViewController + - TOCropViewController (~> 2.7.4) - RNReanimated (3.15.2): - DoubleConversion - glog @@ -1306,6 +1308,8 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - RNSecureRandom (1.0.1): + - React - RNVectorIcons (10.2.0): - DoubleConversion - glog @@ -1365,6 +1369,7 @@ DEPENDENCIES: - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) - "react-native-blur (from `../node_modules/@react-native-community/blur`)" + - react-native-rsa-native (from `../node_modules/react-native-rsa-native`) - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) - react-native-sqlite-storage (from `../node_modules/react-native-sqlite-storage`) - React-nativeconfig (from `../node_modules/react-native/ReactCommon`) @@ -1394,6 +1399,7 @@ DEPENDENCIES: - RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`) - RNReanimated (from `../node_modules/react-native-reanimated`) - RNScreens (from `../node_modules/react-native-screens`) + - RNSecureRandom (from `../node_modules/react-native-securerandom`) - RNVectorIcons (from `../node_modules/react-native-vector-icons`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) @@ -1466,6 +1472,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon" react-native-blur: :path: "../node_modules/@react-native-community/blur" + react-native-rsa-native: + :path: "../node_modules/react-native-rsa-native" react-native-safe-area-context: :path: "../node_modules/react-native-safe-area-context" react-native-sqlite-storage: @@ -1524,6 +1532,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-reanimated" RNScreens: :path: "../node_modules/react-native-screens" + RNSecureRandom: + :path: "../node_modules/react-native-securerandom" RNVectorIcons: :path: "../node_modules/react-native-vector-icons" Yoga: @@ -1561,6 +1571,7 @@ SPEC CHECKSUMS: React-logger: fa92ba4d3a5d39ac450f59be2a3cec7b099f0304 React-Mapbuffer: 9f68550e7c6839d01411ac8896aea5c868eff63a react-native-blur: 30d91a67da86a4d4d924b0c7c36f6e01479a246b + react-native-rsa-native: 12132eb627797529fdb1f0d22fd0f8f9678df64a react-native-safe-area-context: 851c62c48dce80ccaa5637b6aa5991a1bc36eca9 react-native-sqlite-storage: f6d515e1c446d1e6d026aa5352908a25d4de3261 React-nativeconfig: fa5de9d8f4dbd5917358f8ad3ad1e08762f01dcb @@ -1587,9 +1598,10 @@ SPEC CHECKSUMS: React-utils: a06061b3887c702235d2dac92dacbd93e1ea079e ReactCommon: f00e436b3925a7ae44dfa294b43ef360fbd8ccc4 RNGestureHandler: 67e78f16895947f7e57ab91e75e914d3e9ef7239 - RNImageCropPicker: 14fe1c29298fb4018f3186f455c475ab107da332 + RNImageCropPicker: 771e2ca319d2cf92e04ebf334ece892ee9a6728f RNReanimated: 70454122c0c5cf9b661d5a505c3408f29c85b180 RNScreens: aa943ad421c3ced3ef5a47ede02b0cbfc43a012e + RNSecureRandom: 07efbdf2cd99efe13497433668e54acd7df49fef RNVectorIcons: 845eda5c7819bd29699cafd0fc98c9d4afe28c96 SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654 diff --git a/app/client/mobile/package.json b/app/client/mobile/package.json index d1b09281..26882d32 100644 --- a/app/client/mobile/package.json +++ b/app/client/mobile/package.json @@ -15,9 +15,9 @@ "@react-navigation/drawer": "^6.7.2", "@react-navigation/native": "^6.1.18", "@react-navigation/native-stack": "^6.11.0", - "databag-client-sdk": "^0.0.22", "@types/crypto-js": "^4.2.2", "crypto-js": "^4.2.0", + "databag-client-sdk": "^0.0.22", "jsencrypt": "^3.3.2", "react": "18.2.0", "react-dom": "^18.3.1", @@ -30,6 +30,7 @@ "react-native-rsa-native": "^2.0.5", "react-native-safe-area-context": "^4.10.8", "react-native-screens": "^3.34.0", + "react-native-securerandom": "^1.0.1", "react-native-sqlite-storage": "^6.0.1", "react-native-vector-icons": "^10.1.0", "react-router-dom": "^6.26.0", diff --git a/app/client/mobile/src/NativeCrypto.ts b/app/client/mobile/src/NativeCrypto.ts index f6f4979a..974374ff 100644 --- a/app/client/mobile/src/NativeCrypto.ts +++ b/app/client/mobile/src/NativeCrypto.ts @@ -2,13 +2,14 @@ import { Crypto } from 'databag-client-sdk'; import CryptoJS from 'crypto-js'; import { JSEncrypt } from 'jsencrypt' import { RSA } from 'react-native-rsa-native'; +import { generateSecureRandom } from 'react-native-securerandom'; export class NativeCrypto implements Crypto { // generate salt for pbk function public async pbkdfSalt(): Promise<{ saltHex: string }> { - const salt = CryptoJS.lib.WordArray.random(128 / 8); - const saltHex = salt.toString(); + const salt = await generateSecureRandom(16); + const saltHex = this.uint8ToHexStr(salt); return { saltHex }; } @@ -22,15 +23,15 @@ export class NativeCrypto implements Crypto { // generate random aes key public async aesKey(): Promise<{ aesKeyHex: string }> { - const aes = CryptoJS.lib.WordArray.random(256 / 8); - const aesKeyHex = aes.toString(); + const aes = await generateSecureRandom(32); + const aesHex = this.uint8ToHexStr(aes); return { aesKeyHex }; } // generate iv to use to aes function public async aesIv(): Promise<{ ivHex: string }> { - const iv = CryptoJS.lib.WordArray.random(128 / 8); - const ivHex = iv.toString(); + const iv = await generateSecureRandom(16); + const ivHex = this.uint8ToHexStr(iv); return { ivHex }; } @@ -102,7 +103,13 @@ export class NativeCrypto implements Crypto { } } return encoded -}; - + }; + private uint8ToHexStr(bin: Uint8Array) { + let hex = ''; + bin.forEach(val => { + hex += val.toString(16); + }); + return hex; + } } diff --git a/app/client/mobile/src/settings/Settings.styled.ts b/app/client/mobile/src/settings/Settings.styled.ts index 10c9923e..295b7f6d 100644 --- a/app/client/mobile/src/settings/Settings.styled.ts +++ b/app/client/mobile/src/settings/Settings.styled.ts @@ -60,8 +60,9 @@ export const styles = StyleSheet.create({ display: 'flex', flexDirection: 'row', gap: 16, - paddingTop: 16, justifyContent: 'flex-end', + alignItems: 'center', + marginTop: 16, }, modalDescription: { paddingTop: 16, @@ -192,6 +193,13 @@ export const styles = StyleSheet.create({ height: '100%', backgroundColor: 'transparent', }, + modalOption: { + backgroundColor: 'transparent', + flexGrow: 1, + }, + deleteButton: { + backgroundColor: Colors.danger, + }, label: { fontSize: 16, }, diff --git a/app/client/mobile/src/settings/Settings.tsx b/app/client/mobile/src/settings/Settings.tsx index c4c1901b..a922fd29 100644 --- a/app/client/mobile/src/settings/Settings.tsx +++ b/app/client/mobile/src/settings/Settings.tsx @@ -6,6 +6,7 @@ import {useSettings} from './useSettings.hook'; import ImagePicker from 'react-native-image-crop-picker'; import {BlurView} from '@react-native-community/blur'; import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view'; +import { Colors } from '../constants/Colors'; export function Settings({ showLogout }: { showLogout: boolean }) { const { state, actions } = useSettings(); @@ -20,6 +21,7 @@ export function Settings({ showLogout }: { showLogout: boolean }) { const [savingRegistry, setSavingRegistry] = useState(false); const [savingNotifications, setSavingNotifications] = useState(false); const [showPassword, setShowPassword] = useState(false); + const [showConfirm, setShowConfirm] = useState(false); const selectImage = async () => { try { @@ -45,9 +47,7 @@ export function Settings({ showLogout }: { showLogout: boolean }) { actions.setSealPassword(''); actions.setSealConfirm(''); actions.setSealDelete(''); - sealOpen(); - setSavingSeal(true); - setSavingSeal(false); + setSealing(true); } } @@ -100,7 +100,6 @@ export function Settings({ showLogout }: { showLogout: boolean }) { if (!savingSeal) { setSavingSeal(true); try { - await new Promise(r => setTimeout(r, 100)); await actions.setSeal(); setSealing(false); } catch (err) { @@ -260,7 +259,7 @@ export function Settings({ showLogout }: { showLogout: boolean }) { - setSealing(true)}> + {state.strings.manageTopics} @@ -435,51 +434,38 @@ export function Settings({ showLogout }: { showLogout: boolean }) { reducedTransparencyFallbackColor="dark" /> - + { state.strings.manageTopics } setSealing(false)} /> { !sealDelete && !sealReset && state.config.sealSet && state.config.sealUnlocked && ( <> - seal is unlocked + { state.strings.sealUnlocked } { !sealConfig && ( - hide options + + {setSealConfig(true)}} /> + + + )} { sealConfig && ( - show options + + + + {setSealConfig(false)}} /> + )} )} { !sealDelete && sealReset && state.config.sealSet && state.config.sealUnlocked && ( <> - update seal password - - )} - { !sealDelete && state.config.sealSet && !state.config.sealUnlocked && ( - <> - seal is locked - { !sealConfig && ( - hide options - )} - { sealConfig && ( - show options - )} - - )} - { sealDelete && state.config.sealSet && ( - <> - deleting seal - - )} - { !state.config.sealSet && ( - <> - { state.strings.sealUnset } + { state.strings.changePassword } } @@ -496,11 +482,133 @@ export function Settings({ showLogout }: { showLogout: boolean }) { /> ) } - onChangeText={value => actions.setPassword(value)} + onChangeText={value => actions.setSealPassword(value)} + /> + } + right={ + showPassword ? ( + setShowConfirm(false)} + /> + ) : ( + setShowConfirm(true)} + /> + ) + } + onChangeText={value => actions.setSealConfirm(value)} /> - + + + + )} + { !sealDelete && state.config.sealSet && !state.config.sealUnlocked && ( + <> + { state.strings.sealLocked } + } + right={ + showPassword ? ( + setShowPassword(false)} + /> + ) : ( + setShowPassword(true)} + /> + ) + } + onChangeText={value => actions.setSealPassword(value)} + /> + { !sealConfig && ( + + {setSealConfig(true)}} /> + + + + )} + { sealConfig && ( + + + {setSealConfig(false)}} /> + + )} + + )} + { sealDelete && state.config.sealSet && ( + <> + { state.strings.sealDelete } + } + onChangeText={value => actions.setSealDelete(value)} + /> + + + + + )} + { !state.config.sealSet && ( + <> + { state.strings.sealUnset } + { state.strings.delayMessage } + } + right={ + showPassword ? ( + setShowPassword(false)} + /> + ) : ( + setShowPassword(true)} + /> + ) + } + onChangeText={value => actions.setSealPassword(value)} + /> + + + )} diff --git a/app/client/mobile/yarn.lock b/app/client/mobile/yarn.lock index 76a7350b..bc87a0be 100644 --- a/app/client/mobile/yarn.lock +++ b/app/client/mobile/yarn.lock @@ -2791,6 +2791,13 @@ __metadata: languageName: node linkType: hard +"@types/crypto-js@npm:^4.2.2": + version: 4.2.2 + resolution: "@types/crypto-js@npm:4.2.2" + checksum: 727daa0d2db35f0abefbab865c23213b6ee6a270e27e177939bbe4b70d1e84c2202d9fac4ea84859c4b4d49a4ee50f948f601327a39b69ec013288018ba07ca5 + languageName: node + linkType: hard + "@types/graceful-fs@npm:^4.1.3": version: 4.1.9 resolution: "@types/graceful-fs@npm:4.1.9" @@ -3141,23 +3148,28 @@ __metadata: "@react-navigation/drawer": ^6.7.2 "@react-navigation/native": ^6.1.18 "@react-navigation/native-stack": ^6.11.0 + "@types/crypto-js": ^4.2.2 "@types/react": ^18.2.6 "@types/react-test-renderer": ^18.0.0 babel-jest: ^29.6.3 + crypto-js: ^4.2.0 databag-client-sdk: ^0.0.22 eslint: ^8.19.0 jest: ^29.6.3 + jsencrypt: ^3.3.2 prettier: 2.8.8 react: 18.2.0 react-dom: ^18.3.1 react-native: 0.74.3 react-native-gesture-handler: ^2.19.0 - react-native-image-crop-picker: ^0.39.0 + react-native-image-crop-picker: ^0.41.2 react-native-keyboard-aware-scroll-view: ^0.9.5 react-native-paper: ^5.12.5 react-native-reanimated: ^3.15.2 + react-native-rsa-native: ^2.0.5 react-native-safe-area-context: ^4.10.8 react-native-screens: ^3.34.0 + react-native-securerandom: ^1.0.1 react-native-sqlite-storage: ^6.0.1 react-native-vector-icons: ^10.1.0 react-router-dom: ^6.26.0 @@ -3632,7 +3644,7 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": +"base64-js@npm:*, base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 @@ -4172,6 +4184,13 @@ __metadata: languageName: node linkType: hard +"crypto-js@npm:^4.2.0": + version: 4.2.0 + resolution: "crypto-js@npm:4.2.0" + checksum: f051666dbc077c8324777f44fbd3aaea2986f198fe85092535130d17026c7c2ccf2d23ee5b29b36f7a4a07312db2fae23c9094b644cc35f7858b1b4fcaf27774 + languageName: node + linkType: hard + "csstype@npm:^3.0.2": version: 3.1.3 resolution: "csstype@npm:3.1.3" @@ -6735,6 +6754,13 @@ __metadata: languageName: node linkType: hard +"jsencrypt@npm:^3.3.2": + version: 3.3.2 + resolution: "jsencrypt@npm:3.3.2" + checksum: b78e896dc1c3d6bb2e9ce2437e679c1abd4dda098c29f3d1d00024543b03f7e962d0be08b5c1024a94e400c32eb40c720f8293cb5fe6b4b3859cf6b31eabf6a4 + languageName: node + linkType: hard + "jsesc@npm:^2.5.1": version: 2.5.2 resolution: "jsesc@npm:2.5.2" @@ -8247,12 +8273,12 @@ __metadata: languageName: node linkType: hard -"react-native-image-crop-picker@npm:^0.39.0": - version: 0.39.0 - resolution: "react-native-image-crop-picker@npm:0.39.0" +"react-native-image-crop-picker@npm:^0.41.2": + version: 0.41.2 + resolution: "react-native-image-crop-picker@npm:0.41.2" peerDependencies: react-native: ">=0.40.0" - checksum: cb524d99e1b85ea798aa397e7216851f8d777cf469664556c319abe0f38ccb4f62f6850da327d9aebc034a8ea7dbf7d5f4dadbefc2072efadd2d2f961ea00026 + checksum: c1333b3b761e948bd0bc856773f68dcc859c7ce39dda3e14d2c32c17d55927735bfc5cbbb7ac5e8bac21feda87c93399f2a596aca7d5b0611cc1e8203d97ad2e languageName: node linkType: hard @@ -8316,6 +8342,13 @@ __metadata: languageName: node linkType: hard +"react-native-rsa-native@npm:^2.0.5": + version: 2.0.5 + resolution: "react-native-rsa-native@npm:2.0.5" + checksum: 7a720ab3ef1898a4f365decc2913139dbfb090901806bf4c20932c1511bde32cd02586725142e9c273bb575c907a5ee87a84edc8c0b01cce252ad80436370740 + languageName: node + linkType: hard + "react-native-safe-area-context@npm:^4.10.8": version: 4.11.0 resolution: "react-native-safe-area-context@npm:4.11.0" @@ -8339,6 +8372,17 @@ __metadata: languageName: node linkType: hard +"react-native-securerandom@npm:^1.0.1": + version: 1.0.1 + resolution: "react-native-securerandom@npm:1.0.1" + dependencies: + base64-js: "*" + peerDependencies: + react-native: "*" + checksum: ccf42b8a8a83809fd77097aa141efd2e9533e94c094e8dcb0054659707e276cbb6466d3408d0e0594f0bd67af4fbc5881f2ad2d93513b5b2aae25dfd2888274b + languageName: node + linkType: hard + "react-native-sqlite-storage@npm:^6.0.1": version: 6.0.1 resolution: "react-native-sqlite-storage@npm:6.0.1"