diff --git a/app/mobile/ios/Podfile.lock b/app/mobile/ios/Podfile.lock
index 2e908c83..e1bdc265 100644
--- a/app/mobile/ios/Podfile.lock
+++ b/app/mobile/ios/Podfile.lock
@@ -669,7 +669,7 @@ SPEC CHECKSUMS:
FirebaseInstallations: 99d24bac0243cf8b0e96cf5426340d211f0bcc80
FirebaseMessaging: 4487bbff9b9b927ba1dd3ea40d1ceb58e4ee3cb5
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
- glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
+ glog: 3d02b25ca00c2d456734d0bcff864cbc62f6ae1a
GoogleDataTransport: 1c8145da7117bd68bbbed00cf304edb6a24de00f
GoogleUtilities: 1d20a6ad97ef46f67bbdec158ce00563a671ebb7
nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431
diff --git a/app/mobile/src/context/useAccountContext.hook.js b/app/mobile/src/context/useAccountContext.hook.js
index 3f81c84d..eef7251e 100644
--- a/app/mobile/src/context/useAccountContext.hook.js
+++ b/app/mobile/src/context/useAccountContext.hook.js
@@ -1,5 +1,6 @@
import { useState, useRef, useContext } from 'react';
import { StoreContext } from 'context/StoreContext';
+import { setAccountSeal } from 'api/setAccountSeal';
import { setAccountSearchable } from 'api/setAccountSearchable';
import { setAccountNotifications } from 'api/setAccountNotifications';
import { getAccountStatus } from 'api/getAccountStatus';
@@ -78,6 +79,7 @@ export function useAccountContext() {
updateState({ sealKey: key });
},
unlockAccountSeal: async (key) => {
+ const { guid } = session.current;
await store.actions.setAccountSealKey(guid, key);
updateState({ sealKey: key });
},
diff --git a/app/mobile/src/session/profile/profileBody/ProfileBody.jsx b/app/mobile/src/session/profile/profileBody/ProfileBody.jsx
index fd807a6c..1415a02e 100644
--- a/app/mobile/src/session/profile/profileBody/ProfileBody.jsx
+++ b/app/mobile/src/session/profile/profileBody/ProfileBody.jsx
@@ -41,6 +41,20 @@ export function ProfileBody({ navigation }) {
}
}
+ const saveSeal = async () => {
+ try {
+ await actions.saveSeal();
+ actions.hideSealEdit();
+ }
+ catch (err) {
+ console.log(err);
+ Alert.alert(
+ 'Failed to Update Topic Sealing',
+ 'Please try again.',
+ )
+ }
+ }
+
const saveDetails = async () => {
try {
await actions.saveDetails();
@@ -258,9 +272,6 @@ export function ProfileBody({ navigation }) {
-
-
-
@@ -292,7 +303,7 @@ export function ProfileBody({ navigation }) {
{ state.showSealUnlock && (
@@ -306,7 +317,7 @@ export function ProfileBody({ navigation }) {
{ !state.showSealPassword && (
@@ -316,7 +327,7 @@ export function ProfileBody({ navigation }) {
{ state.showSealPassword && (
@@ -326,7 +337,7 @@ export function ProfileBody({ navigation }) {
{ !state.showSealConfirm && (
@@ -336,7 +347,7 @@ export function ProfileBody({ navigation }) {
{ state.showSealConfirm && (
@@ -356,6 +367,7 @@ export function ProfileBody({ navigation }) {
{ state.sealMode === 'unlocked' && (
+
)}
@@ -363,23 +375,39 @@ export function ProfileBody({ navigation }) {
Cancel
- { state.sealMode !== 'unlocking' && (
-
- Save
-
+ { state.canSaveSeal && (
+ <>
+ { state.sealMode !== 'unlocking' && (
+
+ Save
+
+ )}
+ { state.sealMode === 'unlocking' && (
+
+ Unlock
+
+ )}
+ >
)}
- { state.sealMode === 'unlocking' && (
-
- Unlock
-
+ { !state.canSaveSeal && (
+ <>
+ { state.sealMode !== 'unlocking' && (
+
+ Save
+
+ )}
+ { state.sealMode === 'unlocking' && (
+
+ Unlock
+
+ )}
+ >
)}
+
-
-
-
-
);
};
diff --git a/app/mobile/src/session/profile/profileBody/ProfileBody.styled.js b/app/mobile/src/session/profile/profileBody/ProfileBody.styled.js
index b48174ad..d7280da5 100644
--- a/app/mobile/src/session/profile/profileBody/ProfileBody.styled.js
+++ b/app/mobile/src/session/profile/profileBody/ProfileBody.styled.js
@@ -140,8 +140,8 @@ export const styles = StyleSheet.create({
sealUpdate: {
position: 'absolute',
top: 0,
- height: '100%',
- left: 0,
+ height: 36,
+ left: 8,
width: '100%',
},
sealable: {
diff --git a/app/mobile/src/session/profile/profileBody/useProfileBody.hook.js b/app/mobile/src/session/profile/profileBody/useProfileBody.hook.js
index 2d4311c3..07cd5884 100644
--- a/app/mobile/src/session/profile/profileBody/useProfileBody.hook.js
+++ b/app/mobile/src/session/profile/profileBody/useProfileBody.hook.js
@@ -41,7 +41,8 @@ export function useProfileBody() {
sealKey: null,
sealEnabled: false,
sealUnlocked: false,
-
+ canSaveSeal: false,
+
sealEdit: false,
sealMode: null,
sealable: false,
@@ -111,36 +112,107 @@ export function useProfileBody() {
return encoded
};
+ const sealEnable = async () => {
+ // generate key to encrypt private key
+ const salt = CryptoJS.lib.WordArray.random(128 / 8);
+ const aes = CryptoJS.PBKDF2(state.sealPassword, salt, {
+ keySize: 256 / 32,
+ iterations: 1024,
+ });
+
+ // generate rsa key for sealing channel, delay for activity indicator
+ await new Promise(r => setTimeout(r, 1000));
+ const crypto = new JSEncrypt({ default_key_size: 2048 });
+ const key = crypto.getKey();
+
+ // encrypt private key
+ const iv = CryptoJS.lib.WordArray.random(128 / 8);
+ const privateKey = convertPem(crypto.getPrivateKey());
+ const publicKey = convertPem(crypto.getPublicKey());
+ const enc = CryptoJS.AES.encrypt(privateKey, aes, { iv: iv });
+
+ const seal = {
+ passwordSalt: salt.toString(),
+ privateKeyIv: iv.toString(),
+ privateKeyEncrypted: enc.ciphertext.toString(CryptoJS.enc.Base64),
+ publicKey: publicKey,
+ }
+ const sealKey = {
+ public: publicKey,
+ private: privateKey,
+ }
+ await account.actions.setAccountSeal(seal, sealKey);
+ };
+
+ const sealDisable = async () => {
+ await account.actions.setAccountSeal({}, {});
+ };
+
+ const sealUnlock = async () => {
+ // generate key to encrypt private key
+ const salt = CryptoJS.enc.Hex.parse(state.seal.passwordSalt);
+ const aes = CryptoJS.PBKDF2(state.sealUnlock, salt, {
+ keySize: 256 / 32,
+ iterations: 1024,
+ });
+
+ // decrypt private key
+ const iv = CryptoJS.enc.Hex.parse(state.seal.privateKeyIv);
+ const enc = CryptoJS.enc.Base64.parse(state.seal.privateKeyEncrypted)
+ let cipherParams = CryptoJS.lib.CipherParams.create({
+ ciphertext: enc,
+ iv: iv
+ });
+ const dec = CryptoJS.AES.decrypt(cipherParams, aes, { iv: iv });
+
+ // store unlocked seal
+ const sealKey = {
+ public: state.seal.publicKey,
+ private: dec.toString(CryptoJS.enc.Utf8),
+ };
+ await account.actions.unlockAccountSeal(sealKey);
+ };
+
+ const sealUpdate = async () => {
+ // generate key to encrypt private key
+ const salt = CryptoJS.lib.WordArray.random(128 / 8);
+ const aes = CryptoJS.PBKDF2(state.sealPassword, salt, {
+ keySize: 256 / 32,
+ iterations: 1024,
+ });
+
+ // encrypt private key
+ const iv = CryptoJS.lib.WordArray.random(128 / 8);
+ const enc = CryptoJS.AES.encrypt(state.sealKey.private, aes, { iv: iv });
+
+ // update account
+ const seal = {
+ passwordSalt: salt.toString(),
+ privateKeyIv: iv.toString(),
+ privateKeyEncrypted: enc.ciphertext.toString(CryptoJS.enc.Base64),
+ publicKey: state.sealKey.public,
+ }
+ const sealKey = { ...state.sealKey }
+ await account.actions.setAccountSeal(seal, sealKey);
+ };
+
+ useEffect(() => {
+ if (state.sealMode === 'unlocking' && state.sealUnlock != null && state.sealUnlock !== '') {
+ return updateState({ canSaveSeal: true });
+ }
+ if (state.sealMode === 'enabling' && state.sealPassword != null && state.sealPassword === state.sealConfirm) {
+ return updateState({ canSaveSeal: true });
+ }
+ if (state.sealMode === 'disabling' && state.sealDelete === 'delete') {
+ return updateState({ canSaveSeal: true });
+ }
+ if (state.sealMode === 'updating' && state.sealPassword != null && state.sealPassword === state.sealConfirm) {
+ return updateState({ canSaveSeal: true });
+ }
+ updateState({ canSaveSeal: false });
+ }, [state.sealMode, state.sealable, state.sealUnlock, state.sealPassword, state.sealConfirm, state.sealDelete]);
+
const actions = {
- sealTest: async () => {
- console.log("SEAL TEST");
-
- // generate key to encrypt private key
- const salt = CryptoJS.lib.WordArray.random(128 / 8);
- const aes = CryptoJS.PBKDF2('testpassword', salt, {
- keySize: 256 / 32,
- iterations: 1024,
- });
-
- // generate rsa key for sealing channel, delay for activity indicator
- await new Promise(r => setTimeout(r, 1000));
- const crypto = new JSEncrypt({ default_key_size: 2048 });
- const key = crypto.getKey();
-
- // encrypt private key
- const iv = CryptoJS.lib.WordArray.random(128 / 8);
- const privateKey = convertPem(crypto.getPrivateKey());
- const enc = CryptoJS.AES.encrypt(privateKey, aes, { iv: iv });
-
- const seal = {
- passwordSalt: salt.toString(),
- privateKeyIv: iv.toString(),
- privateKeyEncrypted: enc.ciphertext.toString(CryptoJS.enc.Base64),
- publicKey: convertPem(crypto.getPublicKey()),
- }
- console.log("SEAL:", seal);
-
- },
showSealEdit: () => {
let sealMode = null;
const sealable = state.sealEnabled;
@@ -153,7 +225,7 @@ export function useProfileBody() {
else {
sealMode = 'disabled';
}
- updateState({ sealEdit: true, sealable, sealMode });
+ updateState({ sealEdit: true, sealable, sealMode, sealUnlock: null, sealPassword: null, sealConfirm: null, sealDelete: null });
},
hideSealEdit: () => {
updateState({ sealEdit: false });
@@ -181,6 +253,23 @@ export function useProfileBody() {
}
updateState({ sealable, sealMode });
},
+ saveSeal: async () => {
+ if (state.sealMode === 'enabling') {
+ await sealEnable();
+ }
+ else if (state.sealMode === 'disabling') {
+ await sealDisable();
+ }
+ else if (state.sealMode === 'unlocking') {
+ await sealUnlock();
+ }
+ else if (state.sealMode === 'updating') {
+ await sealUpdate();
+ }
+ else {
+ console.log(state.sealMode);
+ }
+ },
showSealUnlock: () => {
updateState({ showSealUnlock: true });
},