mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-23 06:59:31 +01:00
feat: use libsodium for all cryptography
This commit is contained in:
@@ -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
|
||||
};
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user