adding seal setting modal

This commit is contained in:
balzack 2024-09-25 13:01:28 -07:00
parent ca63fcb64e
commit 9afb12a0d9
8 changed files with 245 additions and 59 deletions

View File

@ -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;

View File

@ -9,8 +9,8 @@
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
<string>3B52.1</string>
<string>C617.1</string>
</array>
</dict>
<dict>

View File

@ -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

View File

@ -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",

View File

@ -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;
}
}

View File

@ -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,
},

View File

@ -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 }) {
<Icon size={24} source="cloud-lock-outline" />
</View>
<View style={styles.control}>
<TouchableOpacity activeOpacity={1} onPress={() => setSealing(true)}>
<TouchableOpacity activeOpacity={1} onPress={setSeal}>
<Text style={styles.controlLabel}>{state.strings.manageTopics}</Text>
</TouchableOpacity>
</View>
@ -435,51 +434,38 @@ export function Settings({ showLogout }: { showLogout: boolean }) {
reducedTransparencyFallbackColor="dark"
/>
<KeyboardAwareScrollView style={styles.container} contentContainerStyle={styles.content}>
<Surface elevation={1} mode="flat" style={styles.surface}>
<Surface elevation={5} mode="flat" style={styles.surface}>
<Text style={styles.modalLabel}>{ state.strings.manageTopics }</Text>
<IconButton style={styles.modalClose} icon="close" size={24} onPress={() => setSealing(false)} />
{ !sealDelete && !sealReset && state.config.sealSet && state.config.sealUnlocked && (
<>
<Text>seal is unlocked</Text>
<Text style={styles.modalDescription}>{ state.strings.sealUnlocked }</Text>
{ !sealConfig && (
<Text>hide options</Text>
<View style={styles.modalControls}>
<IconButton style={styles.modalOption} iconColor={Colors.primary} icon="menu-right-outline" size={32} onPress={() => {setSealConfig(true)}} />
<Button mode="outlined" onPress={() => setSealing(false)}>{ state.strings.cancel }</Button>
<Button mode="contained" loading={savingSeal} onPress={sealForget}>{ state.strings.disable }</Button>
</View>
)}
{ sealConfig && (
<Text>show options</Text>
<View style={styles.modalControls}>
<Button mode="contained" onPress={() => setSealReset(true)}>{ state.strings.update }</Button>
<Button mode="contained" style={styles.deleteButton} loading={savingSeal} onPress={() => setSealDelete(true)}>{ state.strings.delete }</Button>
<IconButton style={styles.modalOption} iconColor={Colors.primary} icon="menu-left-outline" size={32} onPress={() => {setSealConfig(false)}} />
</View>
)}
</>
)}
{ !sealDelete && sealReset && state.config.sealSet && state.config.sealUnlocked && (
<>
<Text>update seal password</Text>
</>
)}
{ !sealDelete && state.config.sealSet && !state.config.sealUnlocked && (
<>
<Text>seal is locked</Text>
{ !sealConfig && (
<Text>hide options</Text>
)}
{ sealConfig && (
<Text>show options</Text>
)}
</>
)}
{ sealDelete && state.config.sealSet && (
<>
<Text>deleting seal</Text>
</>
)}
{ !state.config.sealSet && (
<>
<Text style={styles.modalDescription}>{ state.strings.sealUnset }</Text>
<Text style={styles.modalDescription}>{ state.strings.changePassword }</Text>
<TextInput
style={styles.input}
mode="flat"
autoCapitalize="none"
autoComplete="off"
autoCorrect={false}
value={state.password}
value={state.sealPassword}
label={state.strings.password}
secureTextEntry={!showPassword}
left={<TextInput.Icon style={styles.icon} icon="lock" />}
@ -496,11 +482,133 @@ export function Settings({ showLogout }: { showLogout: boolean }) {
/>
)
}
onChangeText={value => actions.setPassword(value)}
onChangeText={value => actions.setSealPassword(value)}
/>
<TextInput
style={styles.input}
mode="flat"
autoCapitalize="none"
autoComplete="off"
autoCorrect={false}
value={state.sealConfirm}
label={state.strings.confirmPassword}
secureTextEntry={!showConfirm}
left={<TextInput.Icon style={styles.icon} icon="lock" />}
right={
showPassword ? (
<TextInput.Icon style={styles.icon}
icon="eye-off"
onPress={() => setShowConfirm(false)}
/>
) : (
<TextInput.Icon style={styles.icon}
icon="eye"
onPress={() => setShowConfirm(true)}
/>
)
}
onChangeText={value => actions.setSealConfirm(value)}
/>
<View style={styles.modalControls}>
<Button mode="outlined" onPress={() => setSealing(false)}>{ state.strings.cancel }</Button>
<Button mode="contained" loading={savingSeal} onPress={sealCreate}>{ state.strings.save }</Button>
<Button mode="contained" disabled={state.sealPassword.length === 0 || state.sealConfirm !== state.sealPassword} loading={savingSeal} onPress={sealUpdate}>{ state.strings.update }</Button>
</View>
</>
)}
{ !sealDelete && state.config.sealSet && !state.config.sealUnlocked && (
<>
<Text style={styles.modalDescription}>{ state.strings.sealLocked }</Text>
<TextInput
style={styles.input}
mode="flat"
autoCapitalize="none"
autoComplete="off"
autoCorrect={false}
value={state.sealPassword}
label={state.strings.password}
secureTextEntry={!showPassword}
left={<TextInput.Icon style={styles.icon} icon="lock" />}
right={
showPassword ? (
<TextInput.Icon style={styles.icon}
icon="eye-off"
onPress={() => setShowPassword(false)}
/>
) : (
<TextInput.Icon style={styles.icon}
icon="eye"
onPress={() => setShowPassword(true)}
/>
)
}
onChangeText={value => actions.setSealPassword(value)}
/>
{ !sealConfig && (
<View style={styles.modalControls}>
<IconButton style={styles.modalOption} iconColor={Colors.primary} icon="menu-right-outline" size={32} onPress={() => {setSealConfig(true)}} />
<Button mode="outlined" onPress={() => setSealing(false)}>{ state.strings.cancel }</Button>
<Button mode="contained" disabled={state.sealPassword.length === 0} loading={savingSeal} onPress={sealUnlock}>{ state.strings.unlock }</Button>
</View>
)}
{ sealConfig && (
<View style={styles.modalControls}>
<Button mode="contained" style={styles.deleteButton} loading={savingSeal} onPress={() => setSealDelete(true)}>{ state.strings.delete }</Button>
<IconButton style={styles.modalOption} iconColor={Colors.primary} icon="menu-left-outline" size={32} onPress={() => {setSealConfig(false)}} />
</View>
)}
</>
)}
{ sealDelete && state.config.sealSet && (
<>
<Text style={styles.modalDescription}>{ state.strings.sealDelete }</Text>
<TextInput
style={styles.input}
mode="flat"
autoCapitalize="none"
autoComplete="off"
autoCorrect={false}
value={state.sealDelete}
label={state.strings.typeDelete}
left={<TextInput.Icon style={styles.icon} icon="lock" />}
onChangeText={value => actions.setSealDelete(value)}
/>
<View style={styles.modalControls}>
<Button mode="contained" style={styles.deleteButton} disabled={state.sealDelete !== state.strings.deleteKey} loading={savingSeal} onPress={sealRemove}>{ state.strings.delete }</Button>
</View>
</>
)}
{ !state.config.sealSet && (
<>
<Text style={styles.modalDescription}>{ state.strings.sealUnset }</Text>
<Text style={styles.modalDescription}>{ state.strings.delayMessage }</Text>
<TextInput
style={styles.input}
mode="flat"
autoCapitalize="none"
autoComplete="off"
autoCorrect={false}
value={state.sealPassword}
label={state.strings.password}
secureTextEntry={!showPassword}
left={<TextInput.Icon style={styles.icon} icon="lock" />}
right={
showPassword ? (
<TextInput.Icon style={styles.icon}
icon="eye-off"
onPress={() => setShowPassword(false)}
/>
) : (
<TextInput.Icon style={styles.icon}
icon="eye"
onPress={() => setShowPassword(true)}
/>
)
}
onChangeText={value => actions.setSealPassword(value)}
/>
<View style={styles.modalControls}>
<Button mode="outlined" onPress={() => setSealing(false)}>{ state.strings.cancel }</Button>
<Button mode="contained" disabled={state.sealPassword.length === 0} loading={savingSeal} onPress={sealCreate}>{ state.strings.save }</Button>
</View>
</>
)}

View File

@ -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"