浏览代码

Update file decryption mechanism

Vishnu Mohandas 4 年之前
父节点
当前提交
01e57a212b
共有 3 个文件被更改,包括 86 次插入71 次删除
  1. 68 43
      src/services/fileService.ts
  2. 7 0
      src/utils/crypto/libsodium.ts
  3. 11 28
      src/worker/crypto.worker.js

+ 68 - 43
src/services/fileService.ts

@@ -2,68 +2,93 @@ import { getEndpoint } from "utils/common/apiUtil";
 import HTTPService from "./HTTPService";
 import * as Comlink from "comlink";
 
-const CryptoWorker:any = typeof window !== 'undefined'
+const CryptoWorker: any = typeof window !== 'undefined'
     && Comlink.wrap(new Worker("worker/crypto.worker.js", { type: 'module' }));
 const ENDPOINT = getEndpoint();
 
-export interface decryptionParams {
-    encryptedKey: string;
-    keyDecryptionNonce: string;
-    header: string;
-    nonce: string;
+export interface fileAttribute {
+    encryptedData: string;
+    decryptionHeader: string;
 };
-export interface fileData {
+
+export interface collection {
     id: number;
-    file: {
-        decryptionParams: decryptionParams;
-    },
-    thumbnail: {
-        decryptionParams: decryptionParams;
-    },
-    metadata: {
-        currentTime: number;
-        modificationTime: number;
-        latitude: number;
-        longitude: number;
-        title: string;
-        deviceFolder: string;
-    };
-    src: string,
-    w: number,
-    h: number,
-    data?: string;
+    ownerID: number;
+    key: string;
+    name: string;
+    type: string;
+    creationTime: number;
+}
+
+export interface file {
+    id: number;
+    collectionID: number;
+    file: fileAttribute;
+    thumbnail: fileAttribute;
+    metadata: fileAttribute;
+    encryptedKey: string;
+    keyDecryptionNonce: string;
+    key: Uint8Array;
+    src: string;
+    w: number;
+    h: number;
 };
 
-const getFileMetaDataUsingWorker = async (data: any, key: string) => {
+const getCollectionKeyUsingWorker = async (collection: any, key: Uint8Array) => {
     const worker = await new CryptoWorker();
-    return worker.decryptMetadata({ data, key });
+    const collectionKey = await worker.decrypt(
+        await worker.fromB64(collection.encryptedKey),
+        await worker.fromB64(collection.keyDecryptionNonce),
+        key);
+    return {
+        ...collection,
+        key: collectionKey
+    };
 }
 
-const getFileUsingWorker = async (data: any, key: string) => {
-    const worker = await new CryptoWorker();
-    return worker.decryptThumbnail({ data, key });
+const getCollections = async (token: string, key: Uint8Array): Promise<collection[]> => {
+    const resp = await HTTPService.get(`${ENDPOINT}/collections/owned`, {
+        token
+    });
+
+    const promises: Promise<collection>[] = resp.data.collections.map(
+        (collection: collection) => getCollectionKeyUsingWorker(collection, key));
+    return await Promise.all(promises);
 }
 
 export const getFiles = async (sinceTime: string, token: string, limit: string, key: string) => {
-    const resp = await HTTPService.get(`${ENDPOINT}/encrypted-files/diff`, {
+    const worker = await new CryptoWorker();
+
+    const collections = await getCollections(token, await worker.fromB64(key));
+    const collectionMap = {}
+    for (const collectionIndex in collections) {
+        collectionMap[collections[collectionIndex].id] = collections[collectionIndex];
+    }
+    const resp = await HTTPService.get(`${ENDPOINT}/files/diff`, {
         sinceTime, token, limit,
     });
 
-    const promises: Promise<fileData>[] = resp.data.diff.map((data) => getFileMetaDataUsingWorker(data, key));
-    const decrypted = await Promise.all(promises);
-
-    return decrypted;
+    const promises: Promise<file>[] = resp.data.diff.map(
+        async (file: file) => {
+            file.key = await worker.decrypt(
+                await worker.fromB64(file.encryptedKey),
+                await worker.fromB64(file.keyDecryptionNonce),
+                collectionMap[file.collectionID].key)
+            file.metadata = await worker.decryptMetadata(file);
+            return file;
+        });
+    return await Promise.all(promises);
 }
 
-export const getPreview = async (token: string, data: fileData, key: string) => {
+export const getPreview = async (token: string, file: file) => {
     const resp = await HTTPService.get(
-        `${ENDPOINT}/encrypted-files/preview/${data.id}`,
+        `${ENDPOINT}/files/preview/${file.id}`,
         { token }, null, { responseType: 'arraybuffer' },
     );
-    const decrypted: any = await getFileUsingWorker({
-        ...data,
-        file: resp.data,
-    }, key);
-    const url = URL.createObjectURL(new Blob([decrypted.data]));
-    return url;
+    const worker = await new CryptoWorker();
+    const decrypted: any = await worker.decryptFile(
+        new Uint8Array(resp.data),
+        await worker.fromB64(file.thumbnail.decryptionHeader),
+        file.key);
+    return URL.createObjectURL(new Blob([decrypted]));
 }

+ 7 - 0
src/utils/crypto/libsodium.ts

@@ -1,5 +1,12 @@
 import sodium from 'libsodium-wrappers';
 
+export async function decryptChaCha(data: Uint8Array, header: Uint8Array, key: Uint8Array) {
+    await sodium.ready;
+    const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull(header, key);
+    const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull(pullState, data, null);
+    return pullResult.message;
+}
+
 export async function encryptToB64(data: string, key: string) {
     await sodium.ready;
     var bKey: Uint8Array;

+ 11 - 28
src/worker/crypto.worker.js

@@ -2,36 +2,19 @@ import * as Comlink from 'comlink';
 import * as libsodium from 'utils/crypto/libsodium';
 
 export class Crypto {
-    async decryptMetadata(event) {
-        const { data } = event;
-        const key = await libsodium.decryptToB64(
-            data.metadata.decryptionParams.encryptedKey,
-            data.metadata.decryptionParams.keyDecryptionNonce,
-            event.key);
-        const metadata = await libsodium.fromB64(await libsodium.decryptToB64(
-            data.metadata.encryptedData,
-            data.metadata.decryptionParams.nonce,
-            key));
-        return {
-            ...data,
-            metadata: JSON.parse(new TextDecoder().decode(metadata))
-        };
+    async decryptMetadata(file) {
+        const encodedMetadata = await libsodium.decryptChaCha(
+            await libsodium.fromB64(file.metadata.encryptedData),
+            await libsodium.fromB64(file.metadata.decryptionHeader),
+            file.key);
+        return JSON.parse(new TextDecoder().decode(encodedMetadata));
     }
 
-    async decryptThumbnail(event) {
-        const { data } = event;
-        const key = await libsodium.decryptToB64(
-            data.thumbnail.decryptionParams.encryptedKey,
-            data.thumbnail.decryptionParams.keyDecryptionNonce,
-            event.key);
-        const thumbnail = await libsodium.decrypt(
-            new Uint8Array(data.file),
-            await libsodium.fromB64(data.thumbnail.decryptionParams.nonce),
-            await libsodium.fromB64(key));
-        return {
-            id: data.id,
-            data: thumbnail,
-        };
+    async decryptFile(fileData, header, key) {
+        return libsodium.decryptChaCha(
+            fileData,
+            header,
+            key);
     }
 
     async encrypt(data, key) {