Use libsodium for key encryption and decryption

This commit is contained in:
Vishnu Mohandas 2020-10-01 06:59:24 +05:30
parent 31106f1eca
commit 8b41415408
6 changed files with 54 additions and 47 deletions

View file

@ -1,15 +1,15 @@
import { createProxyMiddleware } from 'http-proxy-middleware';
export const config = {
api: {
bodyParser: false,
},
api: {
bodyParser: false,
},
};
const API_ENDPOINT = process.env.NEXT_PUBLIC_ENTE_ENDPOINT || "http://api.staging.ente.io";
const API_ENDPOINT = process.env.NEXT_PUBLIC_ENTE_ENDPOINT || "http://192.168.0.100";
export default createProxyMiddleware({
target: API_ENDPOINT,
changeOrigin: true,
pathRewrite: { '^/api': '/' },
target: API_ENDPOINT,
changeOrigin: true,
pathRewrite: { '^/api': '/' },
});

View file

@ -1,4 +1,4 @@
import React, { useEffect, useState, useContext } from 'react';
import React, { useEffect, useState } from 'react';
import Container from 'components/Container';
import styled from 'styled-components';
import Card from 'react-bootstrap/Card';
@ -9,11 +9,9 @@ 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 { hash } from 'utils/crypto/scrypt';
import { strToUint8, base64ToUint8, secureRandomString } from 'utils/crypto/common';
import { decrypt, encrypt } from 'utils/crypto/aes';
import { keyAttributes } from 'types';
import { setKey, SESSION_KEYS, getKey } from 'utils/storage/sessionStorage';
import * as libsodium from 'utils/crypto/libsodium';
const Image = styled.img`
width: 200px;
@ -29,7 +27,7 @@ export default function Credentials() {
const router = useRouter();
const [keyAttributes, setKeyAttributes] = useState<keyAttributes>();
const [loading, setLoading] = useState(false);
useEffect(() => {
router.prefetch('/gallery');
const user = getData(LS_KEYS.USER);
@ -50,16 +48,20 @@ export default function Credentials() {
setLoading(true);
try {
const { passphrase } = values;
const kek = await hash(strToUint8(passphrase), base64ToUint8(keyAttributes.kekSalt));
const kekHash = await hash(base64ToUint8(kek), base64ToUint8(keyAttributes.kekHashSalt));
if (kekHash === keyAttributes.kekHash) {
const key = await decrypt(keyAttributes.encryptedKey, kek, keyAttributes.encryptedKeyIV);
const sessionKey = secureRandomString(32);
const sessionIV = secureRandomString(16);
const encryptionKey = await encrypt(key, sessionKey, sessionIV);
const kek = await libsodium.deriveKey(await libsodium.fromString(passphrase),
await libsodium.fromB64(keyAttributes.kekSalt));
if (await libsodium.verifyHash(keyAttributes.kekHash, kek)) {
const key = await libsodium.decrypt(
await libsodium.fromB64(keyAttributes.encryptedKey),
await libsodium.fromB64(keyAttributes.keyDecryptionNonce),
kek);
const sessionKeyAttributes = await libsodium.encrypt(key);
const sessionKey = await libsodium.toB64(sessionKeyAttributes.key);
const sessionNonce = await libsodium.toB64(sessionKeyAttributes.nonce);
const encryptionKey = await libsodium.toB64(sessionKeyAttributes.encryptedData);
setKey(SESSION_KEYS.ENCRYPTION_KEY, { encryptionKey });
setData(LS_KEYS.SESSION, { sessionKey, sessionIV });
setData(LS_KEYS.SESSION, { sessionKey, sessionNonce });
router.push('/gallery');
} else {
setFieldError('passphrase', constants.INCORRECT_PASSPHRASE);
@ -72,7 +74,7 @@ export default function Credentials() {
return (<Container>
<Image alt='vault' src='/vault.svg' />
<Card style={{ minWidth: '300px'}}>
<Card style={{ minWidth: '300px' }}>
<Card.Body>
<p className="text-center">{constants.ENTER_PASSPHRASE}</p>
<Formik<formValues>

View file

@ -7,13 +7,15 @@ import constants from 'utils/strings/constants';
import { Formik, FormikHelpers } from 'formik';
import * as Yup from 'yup';
import Button from 'react-bootstrap/Button';
import { secureRandomString, strToUint8, base64ToUint8, binToBase64 } from 'utils/crypto/common';
import { hash } from 'utils/crypto/scrypt';
import { encrypt } from 'utils/crypto/aes';
import { putKeyAttributes } from 'services/userService';
import { getData, LS_KEYS, setData } from 'utils/storage/localStorage';
import { useRouter } from 'next/router';
import { getKey, SESSION_KEYS, setKey } from 'utils/storage/sessionStorage';
import * as libsodium from 'utils/crypto/libsodium';
import * as Comlink from "comlink";
const CryptoWorker = typeof window !== 'undefined'
&& Comlink.wrap(new Worker("worker/crypto.worker.js", { type: 'module' }));
const Image = styled.img`
width: 200px;
@ -31,7 +33,7 @@ export default function Generate() {
const [token, setToken] = useState<string>();
const router = useRouter();
const key = getKey(SESSION_KEYS.ENCRYPTION_KEY);
useEffect(() => {
router.prefetch('/gallery');
const user = getData(LS_KEYS.USER);
@ -49,24 +51,27 @@ export default function Generate() {
try {
const { passphrase, confirm } = values;
if (passphrase === confirm) {
const key = secureRandomString(32);
const kekSalt = secureRandomString(32);
const kek = await hash(strToUint8(passphrase), base64ToUint8(kekSalt));
const kekHashSalt = secureRandomString(32);
const kekHash = await hash(base64ToUint8(kek), base64ToUint8(kekHashSalt));
const encryptedKeyIV = secureRandomString(16);
const encryptedKey = await encrypt(key, kek, encryptedKeyIV);
const cryptoWorker = await new CryptoWorker();
const key = await libsodium.generateMasterKey();
const kekSalt = await libsodium.generateSaltToDeriveKey();
const kek = await libsodium.deriveKey(
await libsodium.fromString(passphrase), kekSalt);
const kekHash = await cryptoWorker.hash(kek);
const encryptedKeyAttributes = await cryptoWorker.encrypt(key, kek);
const encryptedKey = encryptedKeyAttributes.encryptedData;
const keyDecryptionNonce = encryptedKeyAttributes.nonce;
const keyAttributes = {
kekSalt, kekHashSalt, kekHash,
encryptedKeyIV, encryptedKey,
kekSalt, kekHash, encryptedKey, keyDecryptionNonce,
};
await putKeyAttributes(token, keyAttributes);
setData(LS_KEYS.KEY_ATTRIBUTES, keyAttributes);
const sessionKey = secureRandomString(32);
const sessionIV = secureRandomString(16);
const encryptionKey = await encrypt(key, sessionKey, sessionIV);
const sessionKeyAttributes = await cryptoWorker.encrypt(key);
const sessionKey = await libsodium.toB64(sessionKeyAttributes.key);
const sessionNonce = await libsodium.toB64(sessionKeyAttributes.nonce);
const encryptionKey = await libsodium.toB64(sessionKeyAttributes.encryptedData);
setKey(SESSION_KEYS.ENCRYPTION_KEY, { encryptionKey });
setData(LS_KEYS.SESSION, { sessionKey, sessionIV });
setData(LS_KEYS.SESSION, { sessionKey, sessionNonce });
router.push('/gallery');
} else {
setFieldError('confirm', constants.PASSPHRASE_MATCH_ERROR);
@ -76,7 +81,7 @@ export default function Generate() {
}
setLoading(false);
}
return (<Container>
<Image alt='vault' src='/vault.svg' />
<Card>

View file

@ -1,7 +1,6 @@
export interface keyAttributes {
kekSalt: string;
kekHash: string;
kekHashSalt: string;
encryptedKey: string;
encryptedKeyIV: string;
keyDecryptionNonce: string;
};

View file

@ -1,6 +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 apiEndpoint = process.env.NEXT_PUBLIC_ENTE_ENDPOINT || "http://192.168.0.100";
const endpoint = !dev ? apiEndpoint : '/api';
return endpoint;
}

View file

@ -1,9 +1,10 @@
import { decrypt } from "utils/crypto/aes";
import { getData, LS_KEYS } from "utils/storage/localStorage";
import { getKey, SESSION_KEYS } from "utils/storage/sessionStorage";
import { decryptToB64 } from "utils/crypto/libsodium";
export const getActualKey = async () => {
const key = getKey(SESSION_KEYS.ENCRYPTION_KEY).encryptionKey;
export const getActualKey = async () => {
const encryptedKey = getKey(SESSION_KEYS.ENCRYPTION_KEY).encryptionKey;
const session = getData(LS_KEYS.SESSION);
return await decrypt(key, session.sessionKey, session.sessionIV);
const key = await decryptToB64(encryptedKey, session.sessionNonce, session.sessionKey);
return key;
}