Files
notesnook/packages/core/utils/crypto.js
2020-04-11 17:20:37 +05:00

89 lines
2.1 KiB
JavaScript

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) {
if (typeof data === "object") data = JSON.stringify(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, { 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;