2022-02-02 10:32:02 +05:00
|
|
|
import {
|
|
|
|
|
from_base64,
|
|
|
|
|
to_base64,
|
|
|
|
|
randombytes_buf,
|
|
|
|
|
crypto_pwhash,
|
|
|
|
|
crypto_pwhash_SALTBYTES,
|
|
|
|
|
crypto_pwhash_ALG_ARGON2I13,
|
2022-08-26 16:19:39 +05:00
|
|
|
crypto_aead_xchacha20poly1305_ietf_KEYBYTES
|
2022-02-02 10:32:02 +05:00
|
|
|
} from "libsodium-wrappers";
|
|
|
|
|
import { EncryptionKey, SerializedKey } from "./types";
|
|
|
|
|
|
|
|
|
|
export default class KeyUtils {
|
|
|
|
|
static deriveKey(password: string, salt?: string): EncryptionKey {
|
|
|
|
|
let saltBytes: Uint8Array;
|
|
|
|
|
if (!salt) saltBytes = randombytes_buf(crypto_pwhash_SALTBYTES);
|
|
|
|
|
else {
|
|
|
|
|
saltBytes = from_base64(salt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!saltBytes)
|
|
|
|
|
throw new Error("Could not generate bytes from the given salt.");
|
|
|
|
|
|
|
|
|
|
const key = crypto_pwhash(
|
|
|
|
|
crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
|
|
|
|
|
password,
|
|
|
|
|
saltBytes,
|
|
|
|
|
3, // operations limit
|
|
|
|
|
1024 * 1024 * 8, // memory limit (8MB)
|
|
|
|
|
crypto_pwhash_ALG_ARGON2I13
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
key,
|
2022-08-26 16:19:39 +05:00
|
|
|
salt: typeof salt === "string" ? salt : to_base64(saltBytes)
|
2022-02-02 10:32:02 +05:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static exportKey(password: string, salt?: string): SerializedKey {
|
|
|
|
|
const { key, salt: keySalt } = this.deriveKey(password, salt);
|
|
|
|
|
return { key: to_base64(key), salt: keySalt };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Takes in either a password or a serialized encryption key
|
|
|
|
|
* and spits out a key that can be directly used for encryption/decryption.
|
|
|
|
|
* @param input
|
|
|
|
|
*/
|
|
|
|
|
static transform(input: SerializedKey): EncryptionKey {
|
|
|
|
|
if ("password" in input && !!input.password) {
|
|
|
|
|
const { password, salt } = input;
|
|
|
|
|
return this.deriveKey(password, salt);
|
|
|
|
|
} else if ("key" in input && !!input.salt && !!input.key) {
|
|
|
|
|
return { key: from_base64(input.key), salt: input.salt };
|
|
|
|
|
}
|
|
|
|
|
throw new Error("Invalid input.");
|
|
|
|
|
}
|
|
|
|
|
}
|