commit
c95fa68326
|
@ -9,10 +9,12 @@ import { Formik, FormikHelpers } from 'formik';
|
|||
import { getData, LS_KEYS, setData } from 'utils/storage/localStorage';
|
||||
import { useRouter } from 'next/router';
|
||||
import * as Yup from 'yup';
|
||||
import { keyAttributes } from 'types';
|
||||
import { KeyAttributes } from 'types';
|
||||
import { setKey, SESSION_KEYS, getKey } from 'utils/storage/sessionStorage';
|
||||
import CryptoWorker from 'utils/crypto/cryptoWorker';
|
||||
import CryptoWorker, { generateIntermediateKeyAttributes } from 'utils/crypto';
|
||||
import { logoutUser } from 'services/userService';
|
||||
import { isFirstLogin } from 'utils/storage';
|
||||
import { Spinner } from 'react-bootstrap';
|
||||
|
||||
const Image = styled.img`
|
||||
width: 200px;
|
||||
|
@ -26,7 +28,7 @@ interface formValues {
|
|||
|
||||
export default function Credentials() {
|
||||
const router = useRouter();
|
||||
const [keyAttributes, setKeyAttributes] = useState<keyAttributes>();
|
||||
const [keyAttributes, setKeyAttributes] = useState<KeyAttributes>();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -57,7 +59,7 @@ export default function Credentials() {
|
|||
passphrase,
|
||||
keyAttributes.kekSalt,
|
||||
keyAttributes.opsLimit,
|
||||
keyAttributes.memLimit,
|
||||
keyAttributes.memLimit
|
||||
);
|
||||
|
||||
try {
|
||||
|
@ -66,6 +68,14 @@ export default function Credentials() {
|
|||
keyAttributes.keyDecryptionNonce,
|
||||
kek
|
||||
);
|
||||
if (isFirstLogin()) {
|
||||
const intermediateKeyAttributes = await generateIntermediateKeyAttributes(
|
||||
passphrase,
|
||||
keyAttributes,
|
||||
key
|
||||
);
|
||||
setData(LS_KEYS.KEY_ATTRIBUTES, intermediateKeyAttributes);
|
||||
}
|
||||
const sessionKeyAttributes = await cryptoWorker.encryptToB64(
|
||||
key
|
||||
);
|
||||
|
@ -128,7 +138,7 @@ export default function Credentials() {
|
|||
onBlur={handleBlur('passphrase')}
|
||||
isInvalid={Boolean(
|
||||
touched.passphrase &&
|
||||
errors.passphrase
|
||||
errors.passphrase
|
||||
)}
|
||||
disabled={loading}
|
||||
autoFocus={true}
|
||||
|
@ -138,7 +148,11 @@ export default function Credentials() {
|
|||
</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
<Button block type="submit" disabled={loading}>
|
||||
{constants.VERIFY_PASSPHRASE}
|
||||
{loading ? (
|
||||
<Spinner animation="border" />
|
||||
) : (
|
||||
constants.VERIFY_PASSPHRASE
|
||||
)}
|
||||
</Button>
|
||||
<br />
|
||||
<div>
|
||||
|
|
|
@ -29,7 +29,8 @@ import ConfirmDialog, { CONFIRM_ACTION } from 'components/ConfirmDialog';
|
|||
import FullScreenDropZone from 'components/FullScreenDropZone';
|
||||
import Sidebar from 'components/Sidebar';
|
||||
import UploadButton from './components/UploadButton';
|
||||
import { checkConnectivity } from 'utils/common/utilFunctions';
|
||||
import { checkConnectivity } from 'utils/common';
|
||||
import { isFirstLogin, setIsFirstLogin } from 'utils/storage';
|
||||
import { logoutUser } from 'services/userService';
|
||||
const DATE_CONTAINER_HEIGHT = 45;
|
||||
const IMAGE_CONTAINER_HEIGHT = 200;
|
||||
|
@ -170,7 +171,8 @@ export default function Gallery(props: Props) {
|
|||
return;
|
||||
}
|
||||
const main = async () => {
|
||||
setIsFirstLoad((await getCollectionUpdationTime()) == 0);
|
||||
setIsFirstLoad(isFirstLogin());
|
||||
setIsFirstLogin(false);
|
||||
const data = await localFiles();
|
||||
const collections = await getLocalCollections();
|
||||
const nonEmptyCollections = getNonEmptyCollections(
|
||||
|
|
|
@ -12,7 +12,9 @@ import { getData, LS_KEYS, setData } from 'utils/storage/localStorage';
|
|||
import { useRouter } from 'next/router';
|
||||
import { getKey, SESSION_KEYS, setKey } from 'utils/storage/sessionStorage';
|
||||
import { B64EncryptionResult } from 'services/uploadService';
|
||||
import CryptoWorker from 'utils/crypto/cryptoWorker';
|
||||
import CryptoWorker from 'utils/crypto';
|
||||
import { generateIntermediateKeyAttributes } from 'utils/crypto';
|
||||
import { Spinner } from 'react-bootstrap';
|
||||
|
||||
const Image = styled.img`
|
||||
width: 200px;
|
||||
|
@ -25,7 +27,7 @@ interface formValues {
|
|||
confirm: string;
|
||||
}
|
||||
|
||||
interface KEK {
|
||||
export interface KEK {
|
||||
key: string;
|
||||
opsLimit: number;
|
||||
memLimit: number;
|
||||
|
@ -99,7 +101,15 @@ export default function Generate() {
|
|||
getData(LS_KEYS.USER).name,
|
||||
keyAttributes
|
||||
);
|
||||
setData(LS_KEYS.KEY_ATTRIBUTES, keyAttributes);
|
||||
|
||||
setData(
|
||||
LS_KEYS.KEY_ATTRIBUTES,
|
||||
await generateIntermediateKeyAttributes(
|
||||
passphrase,
|
||||
keyAttributes,
|
||||
key
|
||||
)
|
||||
);
|
||||
|
||||
const sessionKeyAttributes = await cryptoWorker.encryptToB64(
|
||||
key
|
||||
|
@ -196,7 +206,11 @@ export default function Generate() {
|
|||
disabled={loading}
|
||||
style={{ marginTop: '28px' }}
|
||||
>
|
||||
{constants.SET_PASSPHRASE}
|
||||
{loading ? (
|
||||
<Spinner animation="border" />
|
||||
) : (
|
||||
constants.SET_PASSPHRASE
|
||||
)}
|
||||
</Button>
|
||||
</Form>
|
||||
)}
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
clearFiles,
|
||||
isTokenValid,
|
||||
} from 'services/userService';
|
||||
import { setIsFirstLogin } from 'utils/storage';
|
||||
|
||||
const Image = styled.img`
|
||||
width: 350px;
|
||||
|
@ -41,7 +42,6 @@ export default function Verify() {
|
|||
if (!user?.email) {
|
||||
router.push('/');
|
||||
} else if (user.token) {
|
||||
console.log(user.token);
|
||||
if (await isTokenValid()) {
|
||||
router.push('/credentials');
|
||||
} else {
|
||||
|
@ -71,6 +71,7 @@ export default function Verify() {
|
|||
keyAttributes && setData(LS_KEYS.KEY_ATTRIBUTES, keyAttributes);
|
||||
subscription && setData(LS_KEYS.SUBSCRIPTION, subscription);
|
||||
clearFiles();
|
||||
setIsFirstLogin(true);
|
||||
if (resp.data.keyAttributes?.encryptedKey) {
|
||||
router.push('/credentials');
|
||||
} else {
|
||||
|
|
|
@ -7,7 +7,7 @@ import HTTPService from './HTTPService';
|
|||
import { B64EncryptionResult } from './uploadService';
|
||||
import { getActualKey, getToken } from 'utils/common/key';
|
||||
import { user } from './userService';
|
||||
import CryptoWorker from 'utils/crypto/cryptoWorker';
|
||||
import CryptoWorker from 'utils/crypto';
|
||||
import { ErrorHandler } from 'utils/common/errorUtil';
|
||||
|
||||
const ENDPOINT = getEndpoint();
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { getToken } from 'utils/common/key';
|
||||
import { file } from './fileService';
|
||||
import HTTPService from './HTTPService';
|
||||
import { getEndpoint, getFileUrl, getThumbnailUrl } from 'utils/common/apiUtil';
|
||||
import { getFileExtension, runningInBrowser } from 'utils/common/utilFunctions';
|
||||
import CryptoWorker from 'utils/crypto/cryptoWorker';
|
||||
import { getFileUrl, getThumbnailUrl } from 'utils/common/apiUtil';
|
||||
import { getFileExtension, runningInBrowser } from 'utils/common';
|
||||
import CryptoWorker from 'utils/crypto';
|
||||
|
||||
const TYPE_HEIC = 'heic';
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import localForage from 'utils/storage/localForage';
|
|||
|
||||
import { collection } from './collectionService';
|
||||
import { DataStream, MetadataObject } from './uploadService';
|
||||
import CryptoWorker from 'utils/crypto/cryptoWorker';
|
||||
import CryptoWorker from 'utils/crypto';
|
||||
import { getToken } from 'utils/common/key';
|
||||
import { selectedState } from 'pages/gallery';
|
||||
import { ErrorHandler } from 'utils/common/errorUtil';
|
||||
|
|
|
@ -4,9 +4,9 @@ import EXIF from 'exif-js';
|
|||
import { fileAttribute } from './fileService';
|
||||
import { collection } from './collectionService';
|
||||
import { FILE_TYPE } from 'pages/gallery';
|
||||
import { checkConnectivity } from 'utils/common/utilFunctions';
|
||||
import { checkConnectivity } from 'utils/common';
|
||||
import { ErrorHandler } from 'utils/common/errorUtil';
|
||||
import CryptoWorker from 'utils/crypto/cryptoWorker';
|
||||
import CryptoWorker from 'utils/crypto';
|
||||
import * as convert from 'xml-js';
|
||||
import { ENCRYPTION_CHUNK_SIZE } from 'types';
|
||||
import { getToken } from 'utils/common/key';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import HTTPService from './HTTPService';
|
||||
import { keyAttributes } from 'types';
|
||||
import { KeyAttributes } from 'types';
|
||||
import { getEndpoint } from 'utils/common/apiUtil';
|
||||
import { clearKeys } from 'utils/storage/sessionStorage';
|
||||
import router from 'next/router';
|
||||
|
@ -29,7 +29,7 @@ export const verifyOtt = (email: string, ott: string) => {
|
|||
export const putAttributes = (
|
||||
token: string,
|
||||
name: string,
|
||||
keyAttributes: keyAttributes
|
||||
keyAttributes: KeyAttributes
|
||||
) => {
|
||||
console.log('name ' + name);
|
||||
return HTTPService.put(
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
export interface keyAttributes {
|
||||
export interface KeyAttributes {
|
||||
kekSalt: string;
|
||||
encryptedKey: string;
|
||||
keyDecryptionNonce: string;
|
||||
opsLimit: number;
|
||||
memLimit: number;
|
||||
publicKey: string;
|
||||
encryptedSecretKey: string;
|
||||
secretKeyDecryptionNonce: string;
|
||||
}
|
||||
|
||||
export const ENCRYPTION_CHUNK_SIZE = 4 * 1024 * 1024;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { getData, LS_KEYS, setData } from 'utils/storage/localStorage';
|
||||
import { errorCodes } from './errorUtil';
|
||||
|
||||
export function checkConnectivity() {
|
|
@ -1,4 +1,4 @@
|
|||
import CryptoWorker from 'utils/crypto/cryptoWorker';
|
||||
import CryptoWorker from 'utils/crypto';
|
||||
import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
||||
import { getKey, SESSION_KEYS } from 'utils/storage/sessionStorage';
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
import * as Comlink from 'comlink';
|
||||
import { runningInBrowser } from 'utils/common/utilFunctions';
|
||||
|
||||
const CryptoWorker: any =
|
||||
runningInBrowser() &&
|
||||
Comlink.wrap(new Worker('worker/crypto.worker.js', { type: 'module' }));
|
||||
|
||||
export default CryptoWorker;
|
38
src/utils/crypto/index.ts
Normal file
38
src/utils/crypto/index.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { KEK } from 'pages/generate';
|
||||
import { B64EncryptionResult } from 'services/uploadService';
|
||||
import { KeyAttributes } from 'types';
|
||||
import * as Comlink from 'comlink';
|
||||
import { runningInBrowser } from 'utils/common';
|
||||
|
||||
const CryptoWorker: any =
|
||||
runningInBrowser() &&
|
||||
Comlink.wrap(new Worker('worker/crypto.worker.js', { type: 'module' }));
|
||||
|
||||
export async function generateIntermediateKeyAttributes(
|
||||
passphrase,
|
||||
keyAttributes,
|
||||
key
|
||||
): Promise<KeyAttributes> {
|
||||
const cryptoWorker = await new CryptoWorker();
|
||||
const intermediateKekSalt: string = await cryptoWorker.generateSaltToDeriveKey();
|
||||
const intermediateKek: KEK = await cryptoWorker.deriveIntermediateKey(
|
||||
passphrase,
|
||||
intermediateKekSalt
|
||||
);
|
||||
const encryptedKeyAttributes: B64EncryptionResult = await cryptoWorker.encryptToB64(
|
||||
key,
|
||||
intermediateKek.key
|
||||
);
|
||||
return {
|
||||
kekSalt: intermediateKekSalt,
|
||||
encryptedKey: encryptedKeyAttributes.encryptedData,
|
||||
keyDecryptionNonce: encryptedKeyAttributes.nonce,
|
||||
publicKey: keyAttributes.publicKey,
|
||||
encryptedSecretKey: keyAttributes.encryptedSecretKey,
|
||||
secretKeyDecryptionNonce: keyAttributes.secretKeyDecryptionNonce,
|
||||
opsLimit: intermediateKek.opsLimit,
|
||||
memLimit: intermediateKek.memLimit,
|
||||
};
|
||||
}
|
||||
|
||||
export default CryptoWorker;
|
|
@ -294,6 +294,25 @@ export async function deriveSensitiveKey(passphrase: string, salt: string) {
|
|||
throw null;
|
||||
}
|
||||
|
||||
export async function deriveIntermediateKey(passphrase: string, salt: string) {
|
||||
await sodium.ready;
|
||||
const key = await toB64(
|
||||
sodium.crypto_pwhash(
|
||||
sodium.crypto_secretbox_KEYBYTES,
|
||||
await fromString(passphrase),
|
||||
await fromB64(salt),
|
||||
sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
|
||||
sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
|
||||
sodium.crypto_pwhash_ALG_DEFAULT
|
||||
)
|
||||
);
|
||||
return {
|
||||
key: key,
|
||||
opsLimit: sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
|
||||
memLimit: sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateMasterKey() {
|
||||
await sodium.ready;
|
||||
return await toB64(sodium.crypto_kdf_keygen());
|
||||
|
|
8
src/utils/storage/index.ts
Normal file
8
src/utils/storage/index.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { getData, LS_KEYS, setData } from './localStorage';
|
||||
|
||||
export const isFirstLogin = () =>
|
||||
getData(LS_KEYS.IS_FIRST_LOGIN)?.status ?? false;
|
||||
|
||||
export function setIsFirstLogin(status) {
|
||||
setData(LS_KEYS.IS_FIRST_LOGIN, { status });
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { runningInBrowser } from 'utils/common/utilFunctions';
|
||||
import { runningInBrowser } from 'utils/common';
|
||||
const localForage: LocalForage = runningInBrowser() && require('localforage');
|
||||
|
||||
if (runningInBrowser()) {
|
||||
|
|
|
@ -3,6 +3,7 @@ export enum LS_KEYS {
|
|||
SESSION = 'session',
|
||||
KEY_ATTRIBUTES = 'keyAttributes',
|
||||
SUBSCRIPTION = 'subscription',
|
||||
IS_FIRST_LOGIN = 'isFirstLogin',
|
||||
}
|
||||
|
||||
export const setData = (key: LS_KEYS, value: object) => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { runningInBrowser } from 'utils/common/utilFunctions';
|
||||
import { runningInBrowser } from 'utils/common';
|
||||
import englishConstants from './englishConstants';
|
||||
|
||||
/** Enums of supported locale */
|
||||
|
|
|
@ -84,6 +84,10 @@ export class Crypto {
|
|||
return libsodium.deriveSensitiveKey(passphrase, salt);
|
||||
}
|
||||
|
||||
async deriveIntermediateKey(passphrase, salt) {
|
||||
return libsodium.deriveIntermediateKey(passphrase, salt);
|
||||
}
|
||||
|
||||
async decryptB64(data, nonce, key) {
|
||||
return libsodium.decryptB64(data, nonce, key);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue