mirror of
https://github.com/balzack/databag.git
synced 2025-02-12 03:29:16 +00:00
sending encrypted asset blocks from mobile app
This commit is contained in:
parent
ea349dffbf
commit
000d306bd1
@ -57,6 +57,27 @@ export function updateChannelSubject(subject, contentKey) {
|
|||||||
return { subjectEncrypted, subjectIv };
|
return { subjectEncrypted, subjectIv };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function encryptBlock(block, contentKey) {
|
||||||
|
const key = CryptoJS.enc.Hex.parse(contentKey);
|
||||||
|
const iv = CryptoJS.lib.WordArray.random(128 / 8);
|
||||||
|
const encrypted = CryptoJS.AES.encrypt(block, key, { iv: iv });
|
||||||
|
const blockEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
|
||||||
|
const blockIv = iv.toString();
|
||||||
|
|
||||||
|
return { blockEncrypted, blockIv };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decryptBlock(blockEncrypted, blockIv, contentKey) {
|
||||||
|
const iv = CryptoJS.enc.Hex.parse(blockIv);
|
||||||
|
const key = CryptoJS.enc.Hex.parse(contentKey);
|
||||||
|
const enc = CryptoJS.enc.Base64.parse(blockEncrypted);
|
||||||
|
const cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv });
|
||||||
|
const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv });
|
||||||
|
const block = dec.toString(CryptoJS.enc.Utf8);
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
export function decryptChannelSubject(subject, contentKey) {
|
export function decryptChannelSubject(subject, contentKey) {
|
||||||
const { subjectEncrypted, subjectIv } = JSON.parse(subject);
|
const { subjectEncrypted, subjectIv } = JSON.parse(subject);
|
||||||
const iv = CryptoJS.enc.Hex.parse(subjectIv);
|
const iv = CryptoJS.enc.Hex.parse(subjectIv);
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
import { useState, useRef } from 'react';
|
import { useState, useRef } from 'react';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import { createThumbnail } from "react-native-create-thumbnail";
|
||||||
|
import ImageResizer from '@bam.tech/react-native-image-resizer';
|
||||||
|
import RNFS from 'react-native-fs';
|
||||||
|
|
||||||
|
const ENCRYPTED_BLOCK_SIZE = (128 * 1024); //100k
|
||||||
|
|
||||||
export function useUploadContext() {
|
export function useUploadContext() {
|
||||||
|
|
||||||
@ -116,6 +121,23 @@ export function useUploadContext() {
|
|||||||
return { state, actions }
|
return { state, actions }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getThumb(file, type, position) {
|
||||||
|
if (type === 'image') {
|
||||||
|
const thumb = await ImageResizer.createResizedImage(file, 192, 192, "JPEG", 50, 0, null);
|
||||||
|
const base = await RNFS.readFile(thumb.path, 'base64')
|
||||||
|
return `data:image/jpeg;base64,${base}`;
|
||||||
|
}
|
||||||
|
else if (type === 'video') {
|
||||||
|
const shot = await createThumbnail({ url: url, timeStamp: position * 1000 })
|
||||||
|
const thumb = await ImageResizer.createResizedImage('file://' + shot.path, 192, 192, "JPEG", 50, 0, null);
|
||||||
|
const base = await RNFS.readFile(thumb.path, 'base64')
|
||||||
|
return `data:image/jpeg;base64,${base}`;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function upload(entry, update, complete) {
|
async function upload(entry, update, complete) {
|
||||||
if (!entry.files?.length) {
|
if (!entry.files?.length) {
|
||||||
try {
|
try {
|
||||||
@ -133,7 +155,28 @@ async function upload(entry, update, complete) {
|
|||||||
const file = entry.files.shift();
|
const file = entry.files.shift();
|
||||||
entry.active = {};
|
entry.active = {};
|
||||||
try {
|
try {
|
||||||
if (file.type === 'image') {
|
if (file.encrypted) {
|
||||||
|
const { data, type, size, getEncryptedBlock, position } = file;
|
||||||
|
const thumb = await getThumb(data, type, position);
|
||||||
|
const parts = [];
|
||||||
|
for (let pos = 0; pos < size; pos += ENCRYPTED_BLOCK_SIZE) {
|
||||||
|
const { blockEncrypted, blockIv } = await getEncryptedBlock(pos, ENCRYPTED_BLOCK_SIZE);
|
||||||
|
const partId = await axios.post(`${entry.baseUrl}block${entry.urlParams}`, blockEncrypted, {
|
||||||
|
signal: entry.cancel.signal,
|
||||||
|
onUploadProgress: (ev) => {
|
||||||
|
const { loaded, total } = ev;
|
||||||
|
const partLoaded = pos + Math.floor(blockEncrypted.length * loaded / total);
|
||||||
|
entry.active = { partLoaded, size }
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
parts.push({ blockIv, partId });
|
||||||
|
}
|
||||||
|
entry.assets.push({
|
||||||
|
encrypted: { type, thumb, parts }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (file.type === 'image') {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
if (file.data.startsWith('file:')) {
|
if (file.data.startsWith('file:')) {
|
||||||
formData.append("asset", {uri: file.data, name: 'asset', type: 'application/octent-stream'});
|
formData.append("asset", {uri: file.data, name: 'asset', type: 'application/octent-stream'});
|
||||||
|
@ -3,10 +3,8 @@ import { UploadContext } from 'context/UploadContext';
|
|||||||
import { ConversationContext } from 'context/ConversationContext';
|
import { ConversationContext } from 'context/ConversationContext';
|
||||||
import { Image } from 'react-native';
|
import { Image } from 'react-native';
|
||||||
import Colors from 'constants/Colors';
|
import Colors from 'constants/Colors';
|
||||||
import { getChannelSeals, getContentKey, encryptTopicSubject } from 'context/sealUtil';
|
import { encryptBlock, decryptBlock, getChannelSeals, getContentKey, encryptTopicSubject } from 'context/sealUtil';
|
||||||
import { AccountContext } from 'context/AccountContext';
|
import { AccountContext } from 'context/AccountContext';
|
||||||
import { createThumbnail } from "react-native-create-thumbnail";
|
|
||||||
import ImageResizer from '@bam.tech/react-native-image-resizer';
|
|
||||||
import RNFS from 'react-native-fs';
|
import RNFS from 'react-native-fs';
|
||||||
|
|
||||||
export function useAddTopic(contentKey) {
|
export function useAddTopic(contentKey) {
|
||||||
@ -57,6 +55,10 @@ export function useAddTopic(contentKey) {
|
|||||||
updateState({ conflict });
|
updateState({ conflict });
|
||||||
}, [state.assets, state.locked, state.enableImage, state.enableAudio, state.enableVideo]);
|
}, [state.assets, state.locked, state.enableImage, state.enableAudio, state.enableVideo]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateState({ assets: [] });
|
||||||
|
}, [contentKey]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const cardId = conversation.state.card?.card?.cardId;
|
const cardId = conversation.state.card?.card?.cardId;
|
||||||
const channelId = conversation.state.channel?.channelId;
|
const channelId = conversation.state.channel?.channelId;
|
||||||
@ -103,39 +105,51 @@ export function useAddTopic(contentKey) {
|
|||||||
updateState({ enableImage, enableAudio, enableVideo, locked });
|
updateState({ enableImage, enableAudio, enableVideo, locked });
|
||||||
}, [conversation.state]);
|
}, [conversation.state]);
|
||||||
|
|
||||||
|
const setAsset = async (file) => {
|
||||||
|
const url = file.startsWith('file:') ? file : `file://${file}`;
|
||||||
|
if (contentKey) {
|
||||||
|
const stat = await RNFS.stat(url);
|
||||||
|
const getEncryptedBlock = async (pos, len) => {
|
||||||
|
if (pos + len > stat.size) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const block = await RNFS.read(file, len, pos, 'base64');
|
||||||
|
return getEncryptedBlock(block, contentKey);
|
||||||
|
}
|
||||||
|
return { data: url, encrypted: true, size: stat.size, getEncryptedBlock };
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return { data: url, encrypted: false };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
setMessage: (message) => {
|
setMessage: (message) => {
|
||||||
updateState({ message });
|
updateState({ message });
|
||||||
},
|
},
|
||||||
addImage: async (data) => {
|
addImage: async (data) => {
|
||||||
const url = data.startsWith('file:') ? data : 'file://' + data;
|
|
||||||
|
|
||||||
assetId.current++;
|
assetId.current++;
|
||||||
Image.getSize(url, (width, height) => {
|
const asset = await setAsset(data);
|
||||||
const asset = { key: assetId.current, type: 'image', data: url, ratio: width/height };
|
asset.key = assetId.current;
|
||||||
updateState({ assets: [ ...state.assets, asset ] });
|
asset.type = 'image';
|
||||||
})
|
asset.ratio = 1;
|
||||||
},
|
|
||||||
addVideo: async (data) => {
|
|
||||||
const url = data.startsWith('file:') ? data : 'file://' + data
|
|
||||||
|
|
||||||
const shot = await createThumbnail({ url: url, timeStamp: 5000, })
|
|
||||||
console.log(shot);
|
|
||||||
|
|
||||||
const thumb = await ImageResizer.createResizedImage('file://' + shot.path, 192, 192, "JPEG", 50, 0, null);
|
|
||||||
console.log(thumb);
|
|
||||||
|
|
||||||
const base = await RNFS.readFile(thumb.path, 'base64')
|
|
||||||
console.log('data:image/jpeg;base64,' + base);
|
|
||||||
|
|
||||||
assetId.current++;
|
|
||||||
const asset = { key: assetId.current, type: 'video', data: url, ratio: 1, duration: 0, position: 0 };
|
|
||||||
updateState({ assets: [ ...state.assets, asset ] });
|
updateState({ assets: [ ...state.assets, asset ] });
|
||||||
},
|
},
|
||||||
addAudio: (data, label) => {
|
addVideo: async (data) => {
|
||||||
const url = data.startsWith('file:') ? data : 'file://' + data
|
|
||||||
assetId.current++;
|
assetId.current++;
|
||||||
const asset = { key: assetId.current, type: 'audio', data: url, label };
|
const asset = await setAsset(data);
|
||||||
|
asset.key = assetId.current;
|
||||||
|
asset.type = 'video';
|
||||||
|
asset.position = 0;
|
||||||
|
asset.ratio = 1;
|
||||||
|
updateState({ assets: [ ...state.assets, asset ] });
|
||||||
|
},
|
||||||
|
addAudio: async (data, label) => {
|
||||||
|
assetId.current++;
|
||||||
|
const asset = await setAsset(data);
|
||||||
|
asset.key = assetId.current;
|
||||||
|
asset.type = 'audio';
|
||||||
|
asset.label = label;
|
||||||
updateState({ assets: [ ...state.assets, asset ] });
|
updateState({ assets: [ ...state.assets, asset ] });
|
||||||
},
|
},
|
||||||
setVideoPosition: (key, position) => {
|
setVideoPosition: (key, position) => {
|
||||||
|
@ -56,7 +56,7 @@ export function useAddTopic(contentKey) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
updateState({ assets: [] });
|
updateState({ assets: [] });
|
||||||
return () => { console.log("RETURN CLEAR"); clearObjects() };
|
return () => { clearObjects() };
|
||||||
}, [contentKey]);
|
}, [contentKey]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
Loading…
Reference in New Issue
Block a user