"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.__tsfTypeAliases = exports.Crypto = exports.NumericCharacters = exports.UpperCaseAlphanumericCharacters = exports.AlphanumericCharacters = exports.PrintableCharacters = void 0;
exports.randomBytes = randomBytes;
exports.randomBytesSync = randomBytesSync;
exports.randomString = randomString;
exports.randomStringSync = randomStringSync;
const tslib_1 = require("tslib");
const _a = tslib_1.__importStar(require("@zyno-io/ts-reflection/type-metadata-runtime"));
const crypto = tslib_1.__importStar(require("node:crypto"));
const node_util_1 = require("node:util");
const app_1 = require("../../app");
const __tsf_metadata_type = _a.createCompactMetadataRegistryV1("[1,[{\"kind\":6},{\"kind\":18,\"typeName\":\"CryptoOptions\",\"types\":[{\"kind\":20,\"name\":\"secret\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"ivLength\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true}]}]]", []);
const randomBytesAsync = (0, node_util_1.promisify)(crypto.randomBytes);
async function randomBytes(length, shouldReturnHex) {
    const bytes = await randomBytesAsync(length);
    return shouldReturnHex ? bytes.toString('hex') : bytes;
}
function randomBytesSync(length, shouldReturnHex) {
    const bytes = crypto.randomBytes(length);
    return shouldReturnHex ? bytes.toString('hex') : bytes;
}
exports.PrintableCharacters = Array.from({ length: 127 - 32 }, (_, index) => String.fromCharCode(index + 32)).join('');
exports.AlphanumericCharacters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
exports.UpperCaseAlphanumericCharacters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
exports.NumericCharacters = '0123456789';
async function randomString(length, source = exports.AlphanumericCharacters) {
    return randomStringFromBytes(await randomBytes(length), source);
}
function randomStringSync(length, source = exports.AlphanumericCharacters) {
    return randomStringFromBytes(randomBytesSync(length), source);
}
let _CryptoMetadata_1;
class Crypto {
    static instance;
    static encrypt(data) {
        return this.getInstance().encrypt(data);
    }
    static decrypt(data) {
        return this.getInstance().decrypt(data);
    }
    static reset() {
        this.instance = undefined;
    }
    static getInstance() {
        this.instance ??= new Crypto();
        return this.instance;
    }
    key;
    ivLength;
    constructor(options = {}) {
        const config = options.secret === undefined ? (0, app_1.getAppConfig)() : undefined;
        this.key = parseSecret(options.secret ?? config?.CRYPTO_SECRET);
        this.ivLength = parseIvLength(options.ivLength ?? config?.CRYPTO_IV_LENGTH);
    }
    encrypt(data) {
        const inputIsBuffer = Buffer.isBuffer(data);
        const input = inputIsBuffer ? data : Buffer.from(data);
        const iv = crypto.randomBytes(this.ivLength);
        const cipher = crypto.createCipheriv('aes-256-gcm', this.key, iv);
        const encrypted = Buffer.concat([cipher.update(input), cipher.final()]);
        const output = Buffer.concat([iv, encrypted, cipher.getAuthTag()]);
        return inputIsBuffer ? output : output.toString('base64');
    }
    decrypt(data) {
        const inputIsBuffer = Buffer.isBuffer(data);
        const input = inputIsBuffer ? data : parseEncryptedString(data);
        if (input.length < this.ivLength + 16)
            throw new Error('Invalid encrypted payload');
        const iv = input.subarray(0, this.ivLength);
        const authTag = input.subarray(input.length - 16);
        const encrypted = input.subarray(this.ivLength, input.length - 16);
        const decipher = crypto.createDecipheriv('aes-256-gcm', this.key, iv);
        decipher.setAuthTag(authTag);
        const output = Buffer.concat([decipher.update(encrypted), decipher.final()]);
        return inputIsBuffer ? output : output.toString('utf8');
    }
    static __tsfType = _CryptoMetadata_1 = _a.decodeCompactMetadataV1("[1,{\"kind\":16,\"name\":\"Crypto\",\"typeName\":\"Crypto\",\"classType\":{\"$tsf\":0},\"properties\":[],\"methods\":[{\"name\":\"encrypt\",\"parameters\":[{\"name\":\"data\",\"type\":{\"$tsfType\":0},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":0}},{\"name\":\"encrypt\",\"parameters\":[{\"name\":\"data\",\"type\":{\"kind\":16,\"typeName\":\"Buffer\",\"classType\":{\"$tsf\":1}},\"optional\":false,\"default\":false}],\"returnType\":{\"kind\":16,\"typeName\":\"Buffer\",\"classType\":{\"$tsf\":1}}},{\"name\":\"encrypt\",\"parameters\":[{\"name\":\"data\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":16,\"typeName\":\"Buffer\",\"classType\":{\"$tsf\":1}}]},\"optional\":false,\"default\":false}],\"returnType\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":16,\"typeName\":\"Buffer\",\"classType\":{\"$tsf\":1}}]}},{\"name\":\"decrypt\",\"parameters\":[{\"name\":\"data\",\"type\":{\"$tsfType\":0},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":0}},{\"name\":\"decrypt\",\"parameters\":[{\"name\":\"data\",\"type\":{\"kind\":16,\"typeName\":\"Buffer\",\"classType\":{\"$tsf\":1}},\"optional\":false,\"default\":false}],\"returnType\":{\"kind\":16,\"typeName\":\"Buffer\",\"classType\":{\"$tsf\":1}}},{\"name\":\"decrypt\",\"parameters\":[{\"name\":\"data\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":16,\"typeName\":\"Buffer\",\"classType\":{\"$tsf\":1}}]},\"optional\":false,\"default\":false}],\"returnType\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":16,\"typeName\":\"Buffer\",\"classType\":{\"$tsf\":1}}]}}],\"hasConstructor\":true,\"constructorParameters\":[{\"name\":\"options\",\"type\":{\"$tsfType\":1},\"optional\":true,\"default\":true}]}]", [() => Crypto, () => (typeof Buffer !== "undefined" ? Buffer : (typeof exports !== "undefined" ? exports.Buffer : undefined))], __tsf_metadata_type);
}
exports.Crypto = Crypto;
_CryptoMetadata_1.classType = Crypto;
Crypto.__tsfType = _CryptoMetadata_1;
function randomStringFromBytes(bytes, source) {
    if (!source.length)
        throw new Error('randomString source must not be empty');
    return Array.from({ length: bytes.length }, (_, index) => source[bytes[index] % source.length]).join('');
}
function parseSecret(secret) {
    if (!secret)
        throw new Error('CRYPTO_SECRET is not set in application configuration');
    if (secret.length === 64 && /^[0-9a-f]+$/i.test(secret))
        return Buffer.from(secret, 'hex');
    const key = Buffer.from(secret);
    if (key.length !== 32)
        throw new Error('CRYPTO_SECRET must be 32 bytes or 64 hex characters');
    return key;
}
function parseIvLength(ivLength) {
    ivLength ??= 12;
    if (!Number.isInteger(ivLength) || ivLength < 12)
        throw new Error('CRYPTO_IV_LENGTH must be an integer of at least 12 bytes for AES-GCM');
    return ivLength;
}
function parseEncryptedString(data) {
    if (!/^[A-Za-z0-9+/]*={0,2}$/.test(data) || data.length % 4 === 1)
        throw new Error('Invalid encrypted payload');
    return Buffer.from(data, 'base64');
}
exports.__tsfTypeAliases = JSON.parse("[1,{\"CryptoOptions\":{\"kind\":18,\"typeName\":\"CryptoOptions\",\"types\":[{\"kind\":20,\"name\":\"secret\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"ivLength\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true}]}}]")[1];
//# sourceMappingURL=crypto.js.map