import crypto from 'crypto';
import { concat as uint8ArrayConcat } from 'uint8arrays/concat';
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string';
// Based off of code from https://github.com/luke-park/SecureCompatibleEncryptionExamples
export function create(opts) {
    const algorithm = opts?.algorithm ?? 'aes-128-gcm';
    const keyLength = opts?.keyLength ?? 16;
    const nonceLength = opts?.nonceLength ?? 12;
    const digest = opts?.digest ?? 'sha256';
    const saltLength = opts?.saltLength ?? 16;
    const iterations = opts?.iterations ?? 32767;
    const algorithmTagLength = opts?.algorithmTagLength ?? 16;
    function encryptWithKey(data, key) {
        const nonce = crypto.randomBytes(nonceLength);
        // Create the cipher instance.
        const cipher = crypto.createCipheriv(algorithm, key, nonce);
        // Encrypt and prepend nonce.
        const ciphertext = uint8ArrayConcat([cipher.update(data), cipher.final()]);
        // @ts-expect-error getAuthTag is not a function
        return uint8ArrayConcat([nonce, ciphertext, cipher.getAuthTag()]);
    }
    /**
     * Uses the provided password to derive a pbkdf2 key. The key
     * will then be used to encrypt the data.
     */
    async function encrypt(data, password) {
        // Generate a 128-bit salt
        const salt = crypto.randomBytes(saltLength);
        if (typeof password === 'string') {
            password = uint8ArrayFromString(password);
        }
        // Derive a key using PBKDF2.
        const key = crypto.pbkdf2Sync(password, salt, iterations, keyLength, digest);
        // Encrypt and prepend salt.
        return uint8ArrayConcat([salt, encryptWithKey(Uint8Array.from(data), key)]);
    }
    /**
     * Decrypts the given cipher text with the provided key. The `key` should
     * be a cryptographically safe key and not a plaintext password. To use
     * a plaintext password, use `decrypt`. The options used to create
     * this decryption cipher must be the same as those used to create
     * the encryption cipher.
     */
    function decryptWithKey(ciphertextAndNonce, key) {
        // Create Uint8Arrays of nonce, ciphertext and tag.
        const nonce = ciphertextAndNonce.subarray(0, nonceLength);
        const ciphertext = ciphertextAndNonce.subarray(nonceLength, ciphertextAndNonce.length - algorithmTagLength);
        const tag = ciphertextAndNonce.subarray(ciphertext.length + nonceLength);
        // Create the cipher instance.
        const cipher = crypto.createDecipheriv(algorithm, key, nonce);
        // Decrypt and return result.
        // @ts-expect-error getAuthTag is not a function
        cipher.setAuthTag(tag);
        return uint8ArrayConcat([cipher.update(ciphertext), cipher.final()]);
    }
    /**
     * Uses the provided password to derive a pbkdf2 key. The key
     * will then be used to decrypt the data. The options used to create
     * this decryption cipher must be the same as those used to create
     * the encryption cipher.
     *
     * @param {Uint8Array} data - The data to decrypt
     * @param {string|Uint8Array} password - A plain password
     */
    async function decrypt(data, password) {
        // Create Uint8Arrays of salt and ciphertextAndNonce.
        const salt = data.subarray(0, saltLength);
        const ciphertextAndNonce = data.subarray(saltLength);
        if (typeof password === 'string') {
            password = uint8ArrayFromString(password);
        }
        // Derive the key using PBKDF2.
        const key = crypto.pbkdf2Sync(password, salt, iterations, keyLength, digest);
        // Decrypt and return result.
        return decryptWithKey(ciphertextAndNonce, key);
    }
    const cipher = {
        encrypt,
        decrypt
    };
    return cipher;
}
//# sourceMappingURL=aes-gcm.js.map