unsealing channel subject in mobile app

This commit is contained in:
balzack 2022-12-13 23:22:20 -08:00
parent 356ec4dc45
commit fd086e2333
6 changed files with 117 additions and 9 deletions

View File

@ -33,6 +33,9 @@ import { removeContactChannelTopic } from 'api/removeContactChannelTopic';
import { getContactChannelNotifications } from 'api/getContactChannelNotifications';
import { setContactChannelNotifications } from 'api/setContactChannelNotifications';
import CryptoJS from 'crypto-js';
import { JSEncrypt } from 'jsencrypt'
export function useCardContext() {
const [state, setState] = useState({
cards: new Map(),
@ -595,6 +598,36 @@ export function useCardContext() {
const { detail, profile } = getCardEntry(cardId);
return await setContactChannelNotifications(profile.node, `${profile.guid}.${detail.token}`, channelId, notify);
},
unsealChannelSubject: async (cardId, channelId, revision, sealKey) => {
try {
const { guid } = session.current;
const card = cards.current.get(cardId);
const channel = card.channels.get(channelId);
const { subjectEncrypted, subjectIv, seals } = JSON.parse(channel.detail.data);
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));
}
}
});
await store.actions.setCardChannelItemUnsealedDetail(guid, cardId, channelId, revision, channel.unsealedDetail);
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

@ -334,6 +334,34 @@ export function useChannelContext() {
const { server, appToken } = session.current;
return await setChannelNotifications(server, appToken, channelId, notify);
},
unsealChannelSubject: async (channelId, revision, sealKey) => {
try {
const { guid } = session.current;
const channel = channels.current.get(channelId);
const { subjectEncrypted, subjectIv, seals } = JSON.parse(channel.detail.data);
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));
}
}
});
await store.actions.setChannelItemUnsealedDetail(guid, channelId, revision, channel.unsealedDetail);
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_v064.db';
const DATABAG_DB = 'databag_v068.db';
export function useStoreContext() {
const [state, setState] = useState({});
@ -221,6 +221,9 @@ export function useStoreContext() {
setChannelItemDetail: async (guid, channelId, revision, detail) => {
await db.current.executeSql(`UPDATE channel_${guid} set detail_revision=?, detail=?, unsealed_detail=null where channel_id=?`, [revision, encodeObject(detail), channelId]);
},
setChannelItemUnsealedDetail: async (guid, channelId, revision, unsealed) => {
await db.current.executeSql(`UPDATE channel_${guid} set unsealed_detail=? where detail_revision=? AND channel_id=?`, [encodeObject(unsealed), revision, channelId]);
},
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]);
},
@ -236,7 +239,7 @@ export function useStoreContext() {
};
},
getChannelItems: async (guid) => {
const values = await getAppValues(db.current, `SELECT channel_id, read_revision, revision, sync_revision, blocked, detail_revision, topic_revision, detail, summary FROM channel_${guid}`, []);
const values = await getAppValues(db.current, `SELECT channel_id, read_revision, revision, sync_revision, blocked, detail_revision, topic_revision, detail, unsealed_detail, summary, unsealed_summary FROM channel_${guid}`, []);
return values.map(channel => ({
channelId: channel.channel_id,
revision: channel.revision,
@ -254,7 +257,7 @@ export function useStoreContext() {
getChannelTopicItems: async (guid, channelId) => {
const values = await getAppValues(db.current, `SELECT topic_id, revision, blocked, detail_revision, detail FROM channel_topic_${guid} WHERE channel_id=?`, [channelId]);
const values = await getAppValues(db.current, `SELECT topic_id, revision, blocked, detail_revision, detail, unsealed_detail FROM channel_topic_${guid} WHERE channel_id=?`, [channelId]);
return values.map(topic => ({
topicId: topic.topic_id,
revision: topic.revision,
@ -305,6 +308,9 @@ export function useStoreContext() {
setCardChannelItemDetail: async (guid, cardId, channelId, revision, detail) => {
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) => {
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]);
},
@ -320,7 +326,7 @@ export function useStoreContext() {
};
},
getCardChannelItems: async (guid) => {
const values = await getAppValues(db.current, `SELECT card_id, channel_id, read_revision, sync_revision, revision, blocked, detail_revision, topic_revision, detail, summary FROM card_channel_${guid}`, []);
const values = await getAppValues(db.current, `SELECT card_id, channel_id, read_revision, sync_revision, revision, blocked, detail_revision, topic_revision, detail, unsealed_detail, summary, unsealed_summary FROM card_channel_${guid}`, []);
return values.map(channel => ({
cardId: channel.card_id,
channelId: channel.channel_id,
@ -341,7 +347,7 @@ export function useStoreContext() {
},
getCardChannelTopicItems: async (guid, cardId, channelId) => {
const values = await getAppValues(db.current, `SELECT topic_id, revision, blocked, detail_revision, detail FROM card_channel_topic_${guid} WHERE card_id=? AND channel_id=?`, [cardId, channelId]);
const values = await getAppValues(db.current, `SELECT topic_id, revision, blocked, detail_revision, detail, unsealed_detail FROM card_channel_topic_${guid} WHERE card_id=? AND channel_id=?`, [cardId, channelId]);
return values.map(topic => ({
topicId: topic.topic_id,
revision: topic.revision,

View File

@ -3,6 +3,8 @@ import { TouchableOpacity } from 'react-native-gesture-handler';
import { Logo } from 'utils/Logo';
import { styles } from './ChannelItem.styled';
import { useChannelItem } from './useChannelItem.hook';
import Colors from 'constants/Colors';
import Ionicons from '@expo/vector-icons/MaterialCommunityIcons';
export function ChannelItem({ item, openConversation }) {
@ -12,7 +14,15 @@ export function ChannelItem({ item, openConversation }) {
<TouchableOpacity style={styles.container} activeOpacity={1} onPress={() => openConversation(item.cardId, item.channelId, item.revision)}>
<Logo src={item.logo} width={32} height={32} radius={6} />
<View style={styles.detail}>
<Text style={styles.subject} numberOfLines={1} ellipsizeMode={'tail'}>{ item.subject }</Text>
<View style={styles.subject}>
{ item.locked && !item.unlocked && (
<Ionicons name="lock" style={styles.subjectIcon} size={16} color={Colors.text} />
)}
{ item.locked && item.unlocked && (
<Ionicons name="lock-open-variant-outline" style={styles.subjectIcon} size={16} color={Colors.text} />
)}
<Text style={styles.subjectText} numberOfLines={1} ellipsizeMode={'tail'}>{ item.subject }</Text>
</View>
<Text style={styles.message} numberOfLines={1} ellipsizeMode={'tail'}>{ item.message }</Text>
</View>
{ item.updated && (

View File

@ -22,6 +22,13 @@ export const styles = StyleSheet.create({
flexShrink: 1,
},
subject: {
display: 'flex',
flexDirection: 'row',
},
subjectIcon: {
paddingRight: 4,
},
subjectText: {
color: Colors.text,
fontSize: 14,
},

View File

@ -133,8 +133,32 @@ export function useChannels() {
logo = 'appstore';
}
let locked = false;
let unlocked = false;
let subject = null;
if (item?.detail?.data) {
if (item?.detail?.dataType === 'sealed') {
locked = true;
if (state.sealable) {
try {
if (item.unsealedDetail == null) {
if (item.cardId) {
card.actions.unsealChannelSubject(item.cardId, item.channelId, item.detailRevision, account.state.sealKey);
}
else {
channel.actions.unsealChannelSubject(item.channelId, item.detailRevision, account.state.sealKey);
}
}
else {
unlocked = true;
subject = item.unsealedDetail.subject;
}
}
catch (err) {
console.log(err)
}
}
}
else {
try {
subject = JSON.parse(item?.detail?.data).subject;
}
@ -170,7 +194,7 @@ export function useChannels() {
}
}
return { cardId: item.cardId, channelId: item.channelId, contacts, logo, subject, message, updated, revision: item.revision, timestamp: created, blocked: item.blocked === 1 };
return { cardId: item.cardId, channelId: item.channelId, contacts, logo, subject, locked, unlocked, message, updated, revision: item.revision, timestamp: created, blocked: item.blocked === 1 };
}
useEffect(() => {
@ -214,7 +238,7 @@ export function useChannels() {
});
updateState({ channels: sorted });
}, [channel, card, state.filter]);
}, [channel, card, state.filter, state.sealable]);
const actions = {
setSealed: (sealed) => {