feat: use libsodium for all cryptography

This commit is contained in:
thecodrr
2020-04-11 17:20:37 +05:00
parent 7dae60e4f0
commit 668fe6fd33
7 changed files with 30 additions and 53 deletions

View File

@@ -7,7 +7,7 @@ async function read(key) {
async function readMulti(keys) {
return new Promise((resolve, reject) => {
const result = [];
keys.forEach(key => {
keys.forEach((key) => {
result.push([key, storage[key]]);
});
resolve(result);
@@ -24,20 +24,10 @@ function clear() {
storage = {};
}
function encrypt(password, data) {
return new Promise((resolve, reject) =>
resolve({ iv: "some iv", cipher: data })
);
}
function decrypt(password, data) {
return new Promise((resolve, reject) => resolve(data.cipher));
}
module.exports = {
read,
readMulti,
write,
remove,
clear,
encrypt,
decrypt
};

View File

@@ -5,7 +5,7 @@ beforeEach(async () => {
});
test("create vault", () =>
databaseTest().then(async db => {
databaseTest().then(async (db) => {
expect(await db.vault.create("password")).toBe(true);
const lockKey = await db.context.read("lockKey");
expect(lockKey).toBeDefined();
@@ -14,29 +14,29 @@ test("create vault", () =>
}));
test("unlock vault", () =>
databaseTest().then(async db => {
databaseTest().then(async (db) => {
expect(await db.vault.create("password")).toBe(true);
expect(await db.vault.unlock("password")).toBe(true);
}));
test("unlock non-existent vault", () =>
databaseTest().then(async db => {
databaseTest().then(async (db) => {
db.vault
.unlock("password")
.catch(err => expect(err.message).toBe("ERR_NO_VAULT"));
.catch((err) => expect(err.message).toBe("ERR_NO_VAULT"));
}));
test("unlock vault with wrong password", () =>
databaseTest().then(async db => {
databaseTest().then(async (db) => {
await db.vault.create("password");
db.vault
return db.vault
.unlock("passwrd")
.catch(err => expect(err.message).toBe("ERR_WRNG_PWD"));
.catch((err) => expect(err.message).toBe("ERR_WRONG_PASSWORD"));
}));
test("lock a note when no vault has been created", () =>
noteTest().then(({ db, id }) => {
db.vault.add(id).catch(err => {
return db.vault.add(id).catch((err) => {
expect(err.message).toBe("ERR_NO_VAULT");
});
}));

View File

@@ -8,6 +8,7 @@ import Vault from "./vault";
import Lookup from "./lookup";
import Content from "../collections/content";
import Conflicts from "./conflicts";
import Crypto from "../utils/crypto";
class Database {
constructor(context) {
@@ -40,6 +41,8 @@ class Database {
this.vault = new Vault(this, this.context);
this.conflicts = new Conflicts(this);
this.lookup = new Lookup(this);
this.crypto = new Crypto();
await this.crypto.init();
}
sync() {

View File

@@ -13,14 +13,14 @@ export default class Vault {
this.ERRORS = {
noVault: "ERR_NO_VAULT",
vaultLocked: "ERR_VAULT_LOCKED",
wrongPassword: "ERR_WRONG_PASSWORD"
wrongPassword: "ERR_WRONG_PASSWORD",
};
}
async create(password) {
const lockKey = await this._context.read("lockKey");
if (!lockKey || !lockKey.cipher || !lockKey.iv) {
const encryptedData = await this._context.encrypt(password, this._key);
const encryptedData = await this._db.crypto.encrypt(password, this._key);
await this._context.write("lockKey", encryptedData);
this._password = password;
}
@@ -32,7 +32,7 @@ export default class Vault {
if (!(await this._exists(lockKey))) throw new Error("ERR_NO_VAULT");
var data;
try {
data = await this._context.decrypt(password, lockKey);
data = this._db.crypto.decrypt(password, lockKey);
} catch (e) {
throw new Error(this.ERRORS.wrongPassword);
}
@@ -96,8 +96,8 @@ export default class Vault {
if (!delta.ops) delta = await this._db.delta.get(deltaId);
if (text === textId) text = await this._db.text.get(textId);
text = await this._context.encrypt(this._password, text);
delta = await this._context.encrypt(this._password, delta);
text = this._db.crypto.encrypt(this._password, text);
delta = this._db.crypto.encrypt(this._password, delta);
await this._db.text.add({ id: textId, data: text });
await this._db.delta.add({ id: deltaId, data: delta });
@@ -107,14 +107,14 @@ export default class Vault {
let { text, delta } = { ...content };
text = await this._db.text.get(text);
text = await this._context.decrypt(this._password, text);
text = this._db.crypto.decrypt(this._password, text);
delta = await this._db.text.get(delta);
delta = await this._context.decrypt(this._password, delta);
delta = JSON.parse(this._db.crypto.decrypt(this._password, delta));
return {
delta,
text
text,
};
}
@@ -135,7 +135,7 @@ export default class Vault {
return await this._db.notes.add({
id,
locked: true
locked: true,
});
}
@@ -147,7 +147,7 @@ export default class Vault {
if (perm) {
await this._db.notes.add({
id: note.id,
locked: false
locked: false,
});
await this._db.delta.add({ id: note.content.delta, data: delta });
await this._db.text.add({ id: note.content.text, data: text });
@@ -156,7 +156,7 @@ export default class Vault {
return {
...note,
content: { delta }
content: { delta },
};
}
}

View File

@@ -17,10 +17,4 @@ export default class Storage {
remove(key) {
return this.storage.remove(key);
}
encrypt(password, data) {
return this.storage.encrypt(password, data);
}
decrypt(password, cipher) {
return this.storage.decrypt(password, cipher);
}
}

View File

@@ -8,18 +8,13 @@ test("libsodium should load", async () => {
test("crypto should throw if init has not been called", () => {
const crypto = new Crypto();
expect(() =>
crypto.encrypt({ password: "i_am_a_password", data: "hello world" })
).toThrow();
expect(() => crypto.encrypt("i_am_a_password", "hello world")).toThrow();
});
test("encrypt should encrypt the data", async () => {
const crypto = new Crypto();
await crypto.init();
const result = crypto.encrypt({
password: "i_am_a_password",
data: "hello world",
});
const result = crypto.encrypt("i_am_a_password", "hello world");
expect(result.cipher).not.toBe("hello world");
expect(result.iv).toBeDefined();
expect(result.salt).toBeDefined();
@@ -28,14 +23,8 @@ test("encrypt should encrypt the data", async () => {
test("decrypt should result in plain text", async () => {
const crypto = new Crypto();
await crypto.init();
const result = crypto.encrypt({
password: "i_am_a_password",
data: "hello world",
});
const result = crypto.encrypt("i_am_a_password", "hello world");
const decrypted = crypto.decrypt({
password: "i_am_a_password",
data: { ...result },
});
const decrypted = crypto.decrypt("i_am_a_password", { ...result });
expect(decrypted).toBe("hello world");
});

View File

@@ -38,7 +38,8 @@ class Crypto {
return { key, salt: saltHex };
}
encrypt({ password, data }) {
encrypt(password, data) {
if (typeof data === "object") data = JSON.stringify(data);
this._throwIfNotReady();
const { key, salt } = this._deriveKey(
password,
@@ -65,7 +66,7 @@ class Crypto {
};
}
decrypt({ password, data: { salt, iv, cipher } }) {
decrypt(password, { salt, iv, cipher }) {
this._throwIfNotReady();
const { key } = this._deriveKey(
password,