Added web worker for parallel decryption.
This commit is contained in:
parent
bbe3286150
commit
8e76f6b5bd
14 changed files with 1864 additions and 29 deletions
|
@ -1,3 +1,16 @@
|
|||
const WorkerPlugin = require('worker-plugin');
|
||||
|
||||
module.exports = {
|
||||
target: 'serverless'
|
||||
target: 'serverless',
|
||||
webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
|
||||
if (!isServer) {
|
||||
config.plugins.push(
|
||||
new WorkerPlugin({
|
||||
// use "self" as the global object when receiving hot updates.
|
||||
globalObject: 'self',
|
||||
})
|
||||
)
|
||||
}
|
||||
return config
|
||||
},
|
||||
};
|
|
@ -14,6 +14,7 @@
|
|||
"formik": "^2.1.5",
|
||||
"http-proxy-middleware": "^1.0.5",
|
||||
"next": "9.5.3",
|
||||
"node-forge": "^0.10.0",
|
||||
"react": "16.13.1",
|
||||
"react-bootstrap": "^1.3.0",
|
||||
"react-dom": "16.13.1",
|
||||
|
@ -28,6 +29,7 @@
|
|||
"@types/yup": "^0.29.7",
|
||||
"babel-plugin-styled-components": "^1.11.1",
|
||||
"next-on-netlify": "^2.4.0",
|
||||
"typescript": "^4.0.2"
|
||||
"typescript": "^4.0.2",
|
||||
"worker-plugin": "^5.0.0"
|
||||
}
|
||||
}
|
||||
|
|
18
src/pages/gallery/components/PreviewCard.tsx
Normal file
18
src/pages/gallery/components/PreviewCard.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import React from 'react';
|
||||
import Card from 'react-bootstrap/Card';
|
||||
import { fileData } from 'services/fileService';
|
||||
|
||||
interface IProps {
|
||||
data: fileData,
|
||||
}
|
||||
|
||||
export default function PreviewCard(props: IProps) {
|
||||
const { data } = props;
|
||||
|
||||
return (<Card>
|
||||
<Card.Body>
|
||||
<div>ID: {data?.id}</div>
|
||||
<div>MetaData: {JSON.stringify(data?.metadata)}</div>
|
||||
</Card.Body>
|
||||
</Card>);
|
||||
}
|
|
@ -1,34 +1,36 @@
|
|||
import React, { useContext, useEffect } from 'react';
|
||||
import React, { useLayoutEffect, useState } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import Container from 'components/Container';
|
||||
import Card from 'react-bootstrap/Card';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import { clearData } from 'utils/storage/localStorage';
|
||||
import { clearKeys, getKey, SESSION_KEYS } from 'utils/storage/sessionStorage';
|
||||
import Spinner from 'react-bootstrap/Spinner';
|
||||
import { getKey, SESSION_KEYS } from 'utils/storage/sessionStorage';
|
||||
import { fileData, getFiles } from 'services/fileService';
|
||||
import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
||||
import PreviewCard from './components/PreviewCard';
|
||||
import { getActualKey } from 'utils/common/key';
|
||||
|
||||
export default function Gallery() {
|
||||
const router = useRouter();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [data, setData] = useState<fileData[]>();
|
||||
|
||||
useEffect(() => {
|
||||
useLayoutEffect(() => {
|
||||
const key = getKey(SESSION_KEYS.ENCRYPTION_KEY);
|
||||
const token = getData(LS_KEYS.USER).token;
|
||||
if (!key) {
|
||||
router.push("/");
|
||||
}
|
||||
const main = async () => {
|
||||
setLoading(true);
|
||||
const encryptionKey = await getActualKey();
|
||||
const resp = await getFiles("0", token, "100", encryptionKey);
|
||||
setLoading(false);
|
||||
setData(resp);
|
||||
};
|
||||
main();
|
||||
}, []);
|
||||
|
||||
const logout = () => {
|
||||
clearKeys();
|
||||
clearData();
|
||||
router.push('/');
|
||||
if (!data || loading) {
|
||||
return <Spinner animation="border" variant="primary"/>;
|
||||
}
|
||||
|
||||
return (<Container>
|
||||
<Card className="text-center">
|
||||
<Card.Body>
|
||||
Imagine a very nice and secure gallery of your memories here.<br/>
|
||||
<br/>
|
||||
<Button block onClick={logout}>Logout</Button>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Container>);
|
||||
return (data || []).map(item => <PreviewCard key={item.id} data={item} />);
|
||||
}
|
||||
|
|
33
src/services/fileService.ts
Normal file
33
src/services/fileService.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { getEndpoint } from "utils/common/apiUtil";
|
||||
import HTTPService from "./HTTPService";
|
||||
|
||||
const ENDPOINT = getEndpoint();
|
||||
|
||||
export interface fileData {
|
||||
id: number;
|
||||
metadata: {
|
||||
currentTimestamp: number,
|
||||
},
|
||||
};
|
||||
|
||||
const getFileDataUsingWorker = (data: any, key: string) => {
|
||||
return new Promise((resolve) => {
|
||||
const worker = new Worker('worker/decrypt.worker.js', { type: 'module' });
|
||||
const onWorkerMessage = (event) => resolve(event.data);
|
||||
worker.addEventListener('message', onWorkerMessage);
|
||||
worker.postMessage({ data, key });
|
||||
});
|
||||
}
|
||||
|
||||
export const getFiles = async (sinceTimestamp: string, token: string, limit: string, key: string) => {
|
||||
const resp = await HTTPService.get(`${ENDPOINT}/encrypted-files/diff`, {
|
||||
sinceTimestamp, token, limit,
|
||||
});
|
||||
|
||||
const promises: Promise<fileData>[] = resp.data.diff.map((data) => getFileDataUsingWorker(data, key));
|
||||
console.time('Metadata Parsing');
|
||||
const decrypted = await Promise.all(promises);
|
||||
console.timeEnd('Metadata Parsing');
|
||||
|
||||
return decrypted;
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
import HTTPService from './HTTPService';
|
||||
import { keyAttributes } from 'types';
|
||||
import { getEndpoint } from 'utils/common/apiUtil';
|
||||
|
||||
const dev = process.env.NODE_ENV === 'development';
|
||||
const API_ENDPOINT = process.env.NEXT_PUBLIC_ENTE_ENDPOINT || "https://api.staging.ente.io";
|
||||
const ENDPOINT = !dev ? API_ENDPOINT : '/api'
|
||||
const ENDPOINT = getEndpoint();
|
||||
|
||||
export const getOtt = (email: string) => {
|
||||
return HTTPService.get(`${ENDPOINT}/users/ott`, { email })
|
||||
|
|
1697
src/utils/aescrypt/aescrypt.js
Normal file
1697
src/utils/aescrypt/aescrypt.js
Normal file
File diff suppressed because it is too large
Load diff
21
src/utils/aescrypt/index.ts
Normal file
21
src/utils/aescrypt/index.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { resolve } from 'path';
|
||||
import { aescrypt } from './aescrypt';
|
||||
|
||||
const decrypt = (file: Uint8Array, password: String, binaryResponse: Boolean = false) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
aescrypt.decrypt(file, password, !binaryResponse, ({ data, error}) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
decrypt,
|
||||
}
|
6
src/utils/common/apiUtil.ts
Normal file
6
src/utils/common/apiUtil.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export const getEndpoint = () => {
|
||||
const dev = process.env.NODE_ENV === 'development';
|
||||
const apiEndpoint = process.env.NEXT_PUBLIC_ENTE_ENDPOINT || "https://api.staging.ente.io";
|
||||
const endpoint = !dev ? apiEndpoint : '/api';
|
||||
return endpoint;
|
||||
}
|
9
src/utils/common/key.ts
Normal file
9
src/utils/common/key.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { decrypt } from "utils/crypto/aes";
|
||||
import { getData, LS_KEYS } from "utils/storage/localStorage";
|
||||
import { getKey, SESSION_KEYS } from "utils/storage/sessionStorage";
|
||||
|
||||
export const getActualKey = async () => {
|
||||
const key = getKey(SESSION_KEYS.ENCRYPTION_KEY).encryptionKey;
|
||||
const session = getData(LS_KEYS.SESSION);
|
||||
return await decrypt(key, session.sessionKey, session.sessionIV);
|
||||
}
|
|
@ -13,7 +13,7 @@ export async function encrypt(data: string, key: string, iv: string) {
|
|||
false, ['encrypt', 'decrypt']
|
||||
);
|
||||
|
||||
const result = await window.crypto.subtle.encrypt(
|
||||
const result = await crypto.subtle.encrypt(
|
||||
{
|
||||
name: "AES-CBC",
|
||||
iv: base64ToUint8(iv),
|
||||
|
@ -38,7 +38,7 @@ export async function decrypt(data: string, key: string, iv: string) {
|
|||
false, ['encrypt', 'decrypt']
|
||||
);
|
||||
|
||||
const result = await window.crypto.subtle.decrypt(
|
||||
const result = await crypto.subtle.decrypt(
|
||||
{
|
||||
name: "AES-CBC",
|
||||
iv: base64ToUint8(iv),
|
||||
|
|
22
src/worker/decrypt.worker.js
Normal file
22
src/worker/decrypt.worker.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { decrypt } from "utils/crypto/aes";
|
||||
import { base64ToUint8 } from "utils/crypto/common";
|
||||
import aescrypt from 'utils/aescrypt';
|
||||
|
||||
function decryptFile(event) {
|
||||
const main = async () => {
|
||||
const data = event.data.data;
|
||||
const key = event.data.key;
|
||||
const password = await decrypt(data.encryptedPassword, key, data.encryptedPasswordIV);
|
||||
const metadata = await aescrypt.decrypt(base64ToUint8(data.encryptedMetadata), atob(password));
|
||||
self.postMessage({
|
||||
id: data.id,
|
||||
ownerId: data.ownerId,
|
||||
updationTime: data.updationTime,
|
||||
password,
|
||||
metadata: JSON.parse(metadata),
|
||||
});
|
||||
}
|
||||
main();
|
||||
}
|
||||
|
||||
self.addEventListener('message', decryptFile);
|
|
@ -4,7 +4,8 @@
|
|||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
"esnext",
|
||||
"webworker"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
|
|
14
yarn.lock
14
yarn.lock
|
@ -3473,7 +3473,7 @@ loader-utils@2.0.0, loader-utils@^2.0.0:
|
|||
emojis-list "^3.0.0"
|
||||
json5 "^2.1.2"
|
||||
|
||||
loader-utils@^1.2.3:
|
||||
loader-utils@^1.1.0, loader-utils@^1.2.3:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613"
|
||||
integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==
|
||||
|
@ -3893,6 +3893,11 @@ node-fetch@2.6.0:
|
|||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
|
||||
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
|
||||
|
||||
node-forge@^0.10.0:
|
||||
version "0.10.0"
|
||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
|
||||
integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==
|
||||
|
||||
node-gyp-build@^4.2.2:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739"
|
||||
|
@ -5630,6 +5635,13 @@ worker-farm@^1.7.0:
|
|||
dependencies:
|
||||
errno "~0.1.7"
|
||||
|
||||
worker-plugin@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/worker-plugin/-/worker-plugin-5.0.0.tgz#113b5fe1f4a5d6a957cecd29915bedafd70bb537"
|
||||
integrity sha512-AXMUstURCxDD6yGam2r4E34aJg6kW85IiaeX72hi+I1cxyaMUtrvVY6sbfpGKAj5e7f68Acl62BjQF5aOOx2IQ==
|
||||
dependencies:
|
||||
loader-utils "^1.1.0"
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
|
|
Loading…
Add table
Reference in a new issue