import crypto from "node:crypto";
const KEY_LEN = 32;
const SCRYPT_N = 16384;
const SCRYPT_R = 8;
const SCRYPT_P = 1;
const SALT_LEN = 16;
const IV_LEN = 12;
const ALGO = "aes-256-gcm";
/**
 * Decrypts a value encrypted with a symmetric key.
 * For backwards compatibility, uses the legacy libsodium implementation if the value is a string.
 * Otherwise, uses the AES-256-GCM implementation with scrypt key derivation.
 */
export function decryptWithKey(value, key) {
    if (typeof value === "string") {
        return libsodiumDecrypt(value, key);
    }
    return aes256Decrypt(value, key);
}
/**
 * Encrypt a value with a symmetric key using AES-256-GCM with scrypt key derivation.
 */
export async function encrypt(value, passphrase) {
    const salt = crypto.randomBytes(SALT_LEN);
    const key = await deriveScryptKey(passphrase, salt);
    const iv = crypto.randomBytes(IV_LEN);
    const cipher = crypto.createCipheriv(ALGO, key, iv);
    const ciphertext = Buffer.concat([
        cipher.update(value, "utf8"),
        cipher.final(),
    ]);
    const tag = cipher.getAuthTag();
    return {
        version: "v1",
        ciphertext: ciphertext.toString("base64"),
        iv: iv.toString("base64"),
        salt: salt.toString("base64"),
        tag: tag.toString("base64"),
    };
}
/**
 * Decrypt a value encrypted with a symmetric key using AES-256-GCM with scrypt key derivation.
 * @internal - Exposed for testing. Use `decryptWithKey` instead.
 */
export async function aes256Decrypt(parts, passphrase) {
    const salt = Buffer.from(parts.salt, "base64");
    const iv = Buffer.from(parts.iv, "base64");
    const ciphertext = Buffer.from(parts.ciphertext, "base64");
    const tag = Buffer.from(parts.tag, "base64");
    const key = await deriveScryptKey(passphrase, salt);
    const decipher = crypto.createDecipheriv(ALGO, key, iv);
    decipher.setAuthTag(tag);
    const plaintext = Buffer.concat([
        decipher.update(ciphertext),
        decipher.final(),
    ]);
    return plaintext.toString("utf8");
}
async function deriveScryptKey(passphrase, salt) {
    return new Promise((resolve, reject) => {
        crypto.scrypt(passphrase, salt, KEY_LEN, {
            N: SCRYPT_N,
            r: SCRYPT_R,
            p: SCRYPT_P,
        }, (err, derivedKey) => {
            if (err)
                reject(err);
            else
                resolve(derivedKey);
        });
    });
}
/**
 * Encrypt a value with a symmetric key using libsodium
 * @internal - Exposed to test backwards compatibility. Use `encrypt` instead.
 *
 * @param value - The value to encrypt
 * @param key - The encryption key
 * @returns The base64-encoded encrypted value with nonce
 */
export async function libsodiumEncrypt(value, key) {
    const sodium = (await import("libsodium-wrappers")).default;
    // Initialize libsodium
    await sodium.ready;
    // Derive a key from the passphrase
    const cryptoKey = sodium.crypto_generichash(sodium.crypto_secretbox_KEYBYTES, sodium.from_string(key), null);
    // Generate a random nonce
    const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);
    // Encrypt the message
    const encryptedBin = sodium.crypto_secretbox_easy(sodium.from_string(value), nonce, cryptoKey);
    // Combine nonce and ciphertext, then encode to base64
    const combined = new Uint8Array(nonce.length + encryptedBin.length);
    combined.set(nonce);
    combined.set(encryptedBin, nonce.length);
    return sodium.to_base64(combined, sodium.base64_variants.ORIGINAL);
}
/**
 * Decrypt a value encrypted with a symmetric key using libsodium.
 * @internal - Exposed to test backwards compatibility. Use `decryptWithKey` instead.
 *
 * @param encryptedValue - The base64-encoded encrypted value with nonce
 * @param key - The decryption key
 * @returns The decrypted string
 */
export async function libsodiumDecrypt(encryptedValue, key) {
    const sodium = (await import("libsodium-wrappers")).default;
    // Initialize libsodium
    await sodium.ready;
    // Derive a key from the passphrase
    const cryptoKey = sodium.crypto_generichash(sodium.crypto_secretbox_KEYBYTES, sodium.from_string(key), null);
    // Decode the base64 combined value
    const combined = sodium.from_base64(encryptedValue, sodium.base64_variants.ORIGINAL);
    // Extract nonce and ciphertext
    const nonce = combined.slice(0, sodium.crypto_secretbox_NONCEBYTES);
    const ciphertext = combined.slice(sodium.crypto_secretbox_NONCEBYTES);
    // Decrypt the message
    const decryptedBin = sodium.crypto_secretbox_open_easy(ciphertext, nonce, cryptoKey);
    return sodium.to_string(decryptedBin);
}
//# sourceMappingURL=encrypt.js.map