diff --git a/app/mobile/ios/Databag/Info.plist b/app/mobile/ios/Databag/Info.plist index 879e8c36..9447df13 100644 --- a/app/mobile/ios/Databag/Info.plist +++ b/app/mobile/ios/Databag/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.5 + 1.8 CFBundleSignature ???? CFBundleVersion diff --git a/app/mobile/ios/Podfile.lock b/app/mobile/ios/Podfile.lock index e1bdc265..2e908c83 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: 3d02b25ca00c2d456734d0bcff864cbc62f6ae1a + glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b 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 eef7251e..fc33ad65 100644 --- a/app/mobile/src/context/useAccountContext.hook.js +++ b/app/mobile/src/context/useAccountContext.hook.js @@ -16,7 +16,7 @@ export function useAccountContext() { const curRevision = useRef(null); const setRevision = useRef(null); const syncing = useRef(false); - + const updateState = (value) => { setState((s) => ({ ...s, ...value })) } diff --git a/app/mobile/src/context/useCardContext.hook.js b/app/mobile/src/context/useCardContext.hook.js index 5e2f9680..2a0a7afa 100644 --- a/app/mobile/src/context/useCardContext.hook.js +++ b/app/mobile/src/context/useCardContext.hook.js @@ -56,6 +56,20 @@ export function useCardContext() { setState((s) => ({ ...s, ...value })) } + const unsealKey = (seals, sealKey) => { + let unsealedKey; + if (seals?.length) { + seals.forEach(seal => { + if (seal.publicKey === sealKey.public) { + let crypto = new JSEncrypt(); + crypto.setPrivateKey(sealKey.private); + unsealedKey = crypto.decrypt(seal.sealedKey); + } + }); + } + return unsealedKey; + } + const getCardEntry = (cardId) => { const card = cards.current.get(cardId); if (!card) { @@ -135,9 +149,9 @@ export function useCardContext() { detailRevision: channel?.data?.detailRevision, topicRevision: channel?.data?.topicRevision, detail: channel?.data?.channelDetail, - unsealed_detail: null, + unsealedDetail: null, summary: channel?.data?.channelSummary, - unsealed_summary: null, + unsealedSummary: null, }); } const setCardChannelItem = (cardId, channel) => { @@ -153,7 +167,7 @@ export function useCardContext() { let channel = card.channels.get(channelId); if (channel) { channel.detail = detail; - channel.unsealed_detail = null; + channel.unsealedDetail = null; channel.detailRevision = revision; card.channels.set(channelId, channel); cards.current.set(cardId, card); @@ -166,7 +180,7 @@ export function useCardContext() { let channel = card.channels.get(channelId); if (channel) { channel.summary = summary; - channel.unsealed_summary = null; + channel.unsealedSummary = null; channel.topicRevision = revision; card.channels.set(channelId, channel); cards.current.set(cardId, card); @@ -619,25 +633,15 @@ export function useCardContext() { const card = cards.current.get(cardId); const channel = card.channels.get(channelId); const { subjectEncrypted, subjectIv, seals } = JSON.parse(channel.detail.data); - let unsealed = false; - if (seals?.length) { - seals.forEach(seal => { - if (seal.publicKey === sealKey.public) { - let crypto = new JSEncrypt(); - crypto.setPrivateKey(sealKey.private); - const unsealedKey = crypto.decrypt(seal.sealedKey); - const iv = CryptoJS.enc.Hex.parse(subjectIv); - const key = CryptoJS.enc.Hex.parse(unsealedKey); - const enc = CryptoJS.enc.Base64.parse(subjectEncrypted); - let cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv }); - const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv }); - if (revision === channel.detailRevision) { - channel.unsealedDetail = JSON.parse(dec.toString(CryptoJS.enc.Utf8)); - unsealed = true; - } - } - }); - if (unsealed) { + const unsealedKey = unsealKey(seals, sealKey); + if (unsealedKey) { + const iv = CryptoJS.enc.Hex.parse(subjectIv); + const key = CryptoJS.enc.Hex.parse(unsealedKey); + const enc = CryptoJS.enc.Base64.parse(subjectEncrypted); + let cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv }); + const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv }); + if (revision === channel.detailRevision) { + channel.unsealedDetail = JSON.parse(dec.toString(CryptoJS.enc.Utf8)); await store.actions.setCardChannelItemUnsealedDetail(guid, cardId, channelId, revision, channel.unsealedDetail); card.channels.set(channelId, { ...channel }); cards.current.set(cardId, { ...card }); @@ -649,6 +653,33 @@ export function useCardContext() { console.log(err); } }, + unsealChannelSummary: async (cardId, channelId, revision, sealKey) => { + try { + const { guid } = session.current; + const card = cards.current.get(cardId); + const channel = card.channels.get(channelId); + const { seals } = JSON.parse(channel.detail.data); + const { messageEncrypted, messageIv } = JSON.parse(channel.summary.lastTopic.data); + const unsealedKey = unsealKey(seals, sealKey); + if (unsealedKey) { + const iv = CryptoJS.enc.Hex.parse(messageIv); + const key = CryptoJS.enc.Hex.parse(unsealedKey); + const enc = CryptoJS.enc.Base64.parse(messageEncrypted); + let cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv }); + const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv }); + if (revision === channel.topicRevision) { + channel.unsealedSummary = JSON.parse(dec.toString(CryptoJS.enc.Utf8)); + await store.actions.setCardChannelItemUnsealedSummary(guid, cardId, channelId, revision, channel.unsealedSummary); + card.channels.set(channelId, { ...channel }); + cards.current.set(cardId, { ...card }); + updateState({ cards: cards.current }); + } + } + } + catch(err) { + console.log(err); + } + }, resync: (cardId) => { resync.current.push(cardId); sync(); diff --git a/app/mobile/src/context/useChannelContext.hook.js b/app/mobile/src/context/useChannelContext.hook.js index 1fce52bb..b7127921 100644 --- a/app/mobile/src/context/useChannelContext.hook.js +++ b/app/mobile/src/context/useChannelContext.hook.js @@ -38,6 +38,20 @@ export function useChannelContext() { setState((s) => ({ ...s, ...value })) } + const unsealKey = (seals, sealKey) => { + let unsealedKey; + if (seals?.length) { + seals.forEach(seal => { + if (seal.publicKey === sealKey.public) { + let crypto = new JSEncrypt(); + crypto.setPrivateKey(sealKey.private); + unsealedKey = crypto.decrypt(seal.sealedKey); + } + }); + } + return unsealedKey + }; + const setChannel = (channelId, channel) => { let update = channels.current.get(channelId); if (!update) { @@ -286,6 +300,10 @@ export function useChannelContext() { const { guid } = session.current; await store.actions.setChannelTopicItemUnsealedDetail(guid, channelId, topicId, revision, unsealed); }, + setTopicUnsealedSummary: async (channelId, topicId, revision, unsealed) => { + const { guid } = seassion.current; + await store.actions.setChannelTopicItemUnsealedSummary(guid, channelId, topicId, revision, unsealed); + }, setSubject: async (channelId, subject) => { const { server, appToken } = session.current; return await setChannelSubject(server, appToken, channelId, 'superbasic', { subject }); @@ -373,25 +391,15 @@ export function useChannelContext() { const { guid } = session.current; const channel = channels.current.get(channelId); const { subjectEncrypted, subjectIv, seals } = JSON.parse(channel.detail.data); - if (seals?.length) { - let unsealed = false; - seals.forEach(seal => { - if (seal.publicKey === sealKey.public) { - let crypto = new JSEncrypt(); - crypto.setPrivateKey(sealKey.private); - const unsealedKey = crypto.decrypt(seal.sealedKey); - const iv = CryptoJS.enc.Hex.parse(subjectIv); - const key = CryptoJS.enc.Hex.parse(unsealedKey); - const enc = CryptoJS.enc.Base64.parse(subjectEncrypted); - let cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv }); - const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv }); - if (revision === channel.detailRevision) { - channel.unsealedDetail = JSON.parse(dec.toString(CryptoJS.enc.Utf8)); - unsealed = true; - } - } - }); - if (unsealed) { + const unsealedKey = unsealKey(seals, sealKey); + if (unsealedKey) { + const iv = CryptoJS.enc.Hex.parse(subjectIv); + const key = CryptoJS.enc.Hex.parse(unsealedKey); + const enc = CryptoJS.enc.Base64.parse(subjectEncrypted); + let cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv }); + const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv }); + if (revision === channel.detailRevision) { + channel.unsealedDetail = JSON.parse(dec.toString(CryptoJS.enc.Utf8)); await store.actions.setChannelItemUnsealedDetail(guid, channelId, revision, channel.unsealedDetail); channels.current.set(channelId, { ...channel }); updateState({ channels: channels.current }); @@ -402,6 +410,31 @@ export function useChannelContext() { console.log(err); } }, + unsealChannelSummary: async (channelId, revision, sealKey) => { + try { + const { guid } = session.current; + const channel = channels.current.get(channelId); + const { seals } = JSON.parse(channel.detail.data); + const { messageEncrypted, messageIv } = JSON.parse(channel.summary.lastTopic.data); + const unsealedKey = unsealKey(seals, sealKey); + if (unsealedKey) { + const iv = CryptoJS.enc.Hex.parse(messageIv); + const key = CryptoJS.enc.Hex.parse(unsealedKey); + const enc = CryptoJS.enc.Base64.parse(messageEncrypted); + let cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv }); + const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv }); + if (revision === channel.topicRevision) { + channel.unsealedSummary = JSON.parse(dec.toString(CryptoJS.enc.Utf8)); + await store.actions.setChannelItemUnsealedSummary(guid, channelId, revision, channel.unsealedSummary); + channels.current.set(channelId, { ...channel }); + updateState({ channels: channels.current }); + } + } + } + catch(err) { + console.log(err); + } + }, } return { state, actions } diff --git a/app/mobile/src/context/useStoreContext.hook.js b/app/mobile/src/context/useStoreContext.hook.js index 735b0448..8eb86e85 100644 --- a/app/mobile/src/context/useStoreContext.hook.js +++ b/app/mobile/src/context/useStoreContext.hook.js @@ -1,7 +1,7 @@ import { useEffect, useState, useRef, useContext } from 'react'; import SQLite from "react-native-sqlite-storage"; -const DATABAG_DB = 'databag_v068.db'; +const DATABAG_DB = 'databag_v081.db'; export function useStoreContext() { const [state, setState] = useState({}); @@ -227,6 +227,9 @@ export function useStoreContext() { setChannelItemSummary: async (guid, channelId, revision, summary) => { await db.current.executeSql(`UPDATE channel_${guid} set topic_revision=?, summary=?, unsealed_summary=null where channel_id=?`, [revision, encodeObject(summary), channelId]); }, + setChannelItemUnsealedSummary: async (guid, channelId, revision, unsealed) => { + await db.current.executeSql(`UPDATE channel_${guid} set unsealed_summary=? where topic_revision=? AND channel_id=?`, [encodeObject(unsealed), revision, channelId]); + }, getChannelItemView: async (guid, channelId) => { const values = await getAppValues(db.current, `SELECT revision, detail_revision, topic_revision FROM channel_${guid} WHERE channel_id=?`, [channelId]); if (!values.length) { @@ -272,7 +275,6 @@ export function useStoreContext() { await db.current.executeSql(`INSERT OR REPLACE INTO channel_topic_${guid} (channel_id, topic_id, revision, detail_revision, detail, unsealed_detail) values (?, ?, ?, ?, ?, null);`, [channelId, id, revision, data.detailRevision, encodeObject(data.topicDetail)]); }, setChannelTopicItemUnsealedDetail: async (guid, channelId, topicId, revision, unsealed) => { -console.log("SAVING:", channelId, revision, unsealed); await db.current.executeSql(`UPDATE channel_topic_${guid} set unsealed_detail=? where detail_revision=? AND channel_id=? AND topic_id=?`, [encodeObject(unsealed), revision, channelId, topicId]); }, clearChannelTopicItem: async (guid, channelId, topicId) => { @@ -313,12 +315,14 @@ console.log("SAVING:", channelId, revision, unsealed); await db.current.executeSql(`UPDATE card_channel_${guid} set detail_revision=?, detail=?, unsealed_detail=null where card_id=? and channel_id=?`, [revision, encodeObject(detail), cardId, channelId]); }, setCardChannelItemUnsealedDetail: async (guid, cardId, channelId, revision, unsealed) => { -console.log("SAVING:", cardId, channelId, revision, unsealed); await db.current.executeSql(`UPDATE card_channel_${guid} set unsealed_detail=? where detail_revision=? AND card_id=? AND channel_id=?`, [encodeObject(unsealed), revision, cardId, channelId]); }, setCardChannelItemSummary: async (guid, cardId, channelId, revision, summary) => { await db.current.executeSql(`UPDATE card_channel_${guid} set topic_revision=?, summary=?, unsealed_summary=null where card_id=? and channel_id=?`, [revision, encodeObject(summary), cardId, channelId]); }, + setCardChannelItemUnsealedSummary: async (guid, cardId, channelId, revision, unsealed) => { + await db.current.executeSql(`UPDATE card_channel_${guid} set unsealed_summary=? where topic_revision=? AND card_id=? AND channel_id=?`, [encodeObject(unsealed), revision, cardId, channelId]); + }, getCardChannelItemView: async (guid, cardId, channelId) => { const values = await getAppValues(db.current, `SELECT revision, detail_revision, topic_revision FROM card_channel_${guid} WHERE card_id=? and channel_id=?`, [cardId, channelId]); if (!values.length) { diff --git a/app/mobile/src/session/channels/useChannels.hook.js b/app/mobile/src/session/channels/useChannels.hook.js index 13cfe81f..b96d50f0 100644 --- a/app/mobile/src/session/channels/useChannels.hook.js +++ b/app/mobile/src/session/channels/useChannels.hook.js @@ -94,7 +94,8 @@ export function useChannels() { let updated = false; const login = app.state.loginTimestamp; - const { created, guid } = item?.summary?.lastTopic; + const created = item?.summary?.lastTopic?.created; + const guid = item?.summary?.lastTopic?.guid; if (created && login && login < created) { if (!item.readRevision || item.readRevision < item.revision) { if (profile.state.profile.guid != guid) { @@ -193,6 +194,28 @@ export function useChannels() { console.log(err); } } + if (item?.summary?.lastTopic?.dataType === 'sealedtopic') { + if (state.sealable) { + try { + if (item.unsealedSummary == null) { + if (item.cardId) { + card.actions.unsealChannelSummary(item.cardId, item.channelId, item.topicRevision, account.state.sealKey); + } + else { + channel.actions.unsealChannelSummary(item.channelId, item.topicRevision, account.state.sealKey); + } + } + else { + if (typeof item.unsealedSummary.message.text === 'string') { + message = item.unsealedSummary.message.text; + } + } + } + catch (err) { + console.log(err) + } + } + } return { cardId: item.cardId, channelId: item.channelId, contacts, logo, subject, locked, unlocked, message, updated, revision: item.revision, timestamp: created, blocked: item.blocked === 1 }; }