unsealing channel summary in mobile app

This commit is contained in:
balzack 2022-12-17 13:19:07 -08:00
parent 1a0a9bca69
commit 29f58ebf7e
7 changed files with 140 additions and 49 deletions

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.5</string>
<string>1.8</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@ -669,7 +669,7 @@ SPEC CHECKSUMS:
FirebaseInstallations: 99d24bac0243cf8b0e96cf5426340d211f0bcc80
FirebaseMessaging: 4487bbff9b9b927ba1dd3ea40d1ceb58e4ee3cb5
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 3d02b25ca00c2d456734d0bcff864cbc62f6ae1a
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
GoogleDataTransport: 1c8145da7117bd68bbbed00cf304edb6a24de00f
GoogleUtilities: 1d20a6ad97ef46f67bbdec158ce00563a671ebb7
nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431

View File

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

View File

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

View File

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

View File

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

View File

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