mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-23 23:19:40 +01:00
feat: impl libsodium crypto
This commit is contained in:
41
packages/core/utils/__tests__/crypto.test.js
Normal file
41
packages/core/utils/__tests__/crypto.test.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import Crypto from "../crypto";
|
||||||
|
|
||||||
|
test("libsodium should load", async () => {
|
||||||
|
const crypto = new Crypto();
|
||||||
|
await crypto.init();
|
||||||
|
expect(crypto.isReady).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
|
||||||
|
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",
|
||||||
|
});
|
||||||
|
expect(result.cipher).not.toBe("hello world");
|
||||||
|
expect(result.iv).toBeDefined();
|
||||||
|
expect(result.salt).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
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 decrypted = crypto.decrypt({
|
||||||
|
password: "i_am_a_password",
|
||||||
|
data: { ...result },
|
||||||
|
});
|
||||||
|
expect(decrypted).toBe("hello world");
|
||||||
|
});
|
||||||
87
packages/core/utils/crypto.js
Normal file
87
packages/core/utils/crypto.js
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import _sodium from "libsodium-wrappers";
|
||||||
|
|
||||||
|
export async function newSodium() {
|
||||||
|
await _sodium.ready;
|
||||||
|
return _sodium;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Crypto {
|
||||||
|
constructor() {
|
||||||
|
this.isReady = false;
|
||||||
|
this.sodium = _sodium;
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
this.sodium = await newSodium();
|
||||||
|
this.isReady = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_throwIfNotReady() {
|
||||||
|
if (!this.isReady)
|
||||||
|
throw new Error("libsodium is not ready yet. Please call init()");
|
||||||
|
}
|
||||||
|
|
||||||
|
_deriveKey(password, keyLength, salt) {
|
||||||
|
this._throwIfNotReady();
|
||||||
|
salt =
|
||||||
|
salt || this.sodium.randombytes_buf(this.sodium.crypto_pwhash_SALTBYTES);
|
||||||
|
const key = this.sodium.crypto_pwhash(
|
||||||
|
keyLength,
|
||||||
|
password,
|
||||||
|
salt,
|
||||||
|
3, // operations limit
|
||||||
|
1024 * 1024 * 4, // memory limit (4MB)
|
||||||
|
this.sodium.crypto_pwhash_ALG_ARGON2I13
|
||||||
|
);
|
||||||
|
const saltHex = this.sodium.to_hex(salt);
|
||||||
|
this.sodium.memzero(salt);
|
||||||
|
return { key, salt: saltHex };
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypt({ password, data }) {
|
||||||
|
this._throwIfNotReady();
|
||||||
|
const { key, salt } = this._deriveKey(
|
||||||
|
password,
|
||||||
|
this.sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES
|
||||||
|
);
|
||||||
|
const nonce = this.sodium.randombytes_buf(
|
||||||
|
this.sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES
|
||||||
|
);
|
||||||
|
const cipher = this.sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(
|
||||||
|
data,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
nonce,
|
||||||
|
key,
|
||||||
|
"base64"
|
||||||
|
);
|
||||||
|
const iv = this.sodium.to_hex(nonce);
|
||||||
|
this.sodium.memzero(nonce);
|
||||||
|
this.sodium.memzero(key);
|
||||||
|
return {
|
||||||
|
cipher,
|
||||||
|
iv,
|
||||||
|
salt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
decrypt({ password, data: { salt, iv, cipher } }) {
|
||||||
|
this._throwIfNotReady();
|
||||||
|
const { key } = this._deriveKey(
|
||||||
|
password,
|
||||||
|
this.sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
|
||||||
|
this.sodium.from_hex(salt)
|
||||||
|
);
|
||||||
|
const plainText = this.sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||||
|
undefined,
|
||||||
|
this.sodium.from_base64(cipher),
|
||||||
|
undefined,
|
||||||
|
this.sodium.from_hex(iv),
|
||||||
|
key,
|
||||||
|
"text"
|
||||||
|
);
|
||||||
|
this.sodium.memzero(key);
|
||||||
|
return plainText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default Crypto;
|
||||||
Reference in New Issue
Block a user