import * as crypto from 'crypto';
export function generateDEK() {
    return crypto.randomBytes(32);
}
export function wrapDEK(dek, kek) {
    const iv = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv('aes-256-gcm', kek, iv);
    let wrappedKey = cipher.update(dek);
    wrappedKey = Buffer.concat([wrappedKey, cipher.final()]);
    const tag = cipher.getAuthTag();
    return {
        wrappedKey,
        iv,
        tag
    };
}
export function unwrapDEK(wrappedKey, iv, tag, kek) {
    const decipher = crypto.createDecipheriv('aes-256-gcm', kek, iv);
    decipher.setAuthTag(tag);
    let dek = decipher.update(wrappedKey);
    dek = Buffer.concat([dek, decipher.final()]);
    return dek;
}
export function encryptWithEnvelope(data, kek) {
    const dek = generateDEK();
    const dataIv = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv('aes-256-gcm', dek, dataIv);
    let encryptedData = cipher.update(data);
    encryptedData = Buffer.concat([encryptedData, cipher.final()]);
    const tag = cipher.getAuthTag();
    const { wrappedKey, iv: wrapIv, tag: wrapTag } = wrapDEK(dek, kek);
    return {
        encryptedData,
        encryptedKey: wrappedKey,
        iv: dataIv,
        wrapIv: wrapIv,
        wrapTag: wrapTag,
        tag
    };
}
export function decryptWithEnvelope(envelope, kek) {
    let wrapIv;
    let dataIv;
    if (envelope.wrapIv && envelope.wrapTag) {
        wrapIv = envelope.wrapIv;
        const wrapTag = envelope.wrapTag;
        dataIv = envelope.iv;
        var dek = unwrapDEK(envelope.encryptedKey, wrapIv, wrapTag, kek);
    }
    else {
        throw new Error('Legacy encrypted data detected. Data encrypted with old format cannot be decrypted. Please re-encrypt with new format.');
    }
    const decipher = crypto.createDecipheriv('aes-256-gcm', dek, dataIv);
    decipher.setAuthTag(envelope.tag);
    let decryptedData = decipher.update(envelope.encryptedData);
    decryptedData = Buffer.concat([decryptedData, decipher.final()]);
    return {
        decryptedData,
        dek
    };
}
export function serializeEnvelope(envelope) {
    const serialized = {
        encryptedData: envelope.encryptedData.toString('base64'),
        encryptedKey: envelope.encryptedKey.toString('base64'),
        iv: envelope.iv.toString('base64'),
        tag: envelope.tag.toString('base64')
    };
    if (envelope.wrapIv) {
        serialized.wrapIv = envelope.wrapIv.toString('base64');
    }
    if (envelope.wrapTag) {
        serialized.wrapTag = envelope.wrapTag.toString('base64');
    }
    return JSON.stringify(serialized);
}
export function deserializeEnvelope(serialized) {
    const parsed = JSON.parse(serialized);
    const result = {
        encryptedData: Buffer.from(parsed.encryptedData, 'base64'),
        encryptedKey: Buffer.from(parsed.encryptedKey, 'base64'),
        iv: Buffer.from(parsed.iv, 'base64'),
        tag: Buffer.from(parsed.tag, 'base64')
    };
    if (parsed.wrapIv) {
        result.wrapIv = Buffer.from(parsed.wrapIv, 'base64');
    }
    if (parsed.wrapTag) {
        result.wrapTag = Buffer.from(parsed.wrapTag, 'base64');
    }
    return result;
}
export function encryptString(data, kek) {
    const dataBuffer = Buffer.from(data, 'utf-8');
    const envelope = encryptWithEnvelope(dataBuffer, kek);
    return serializeEnvelope(envelope);
}
export function decryptString(encryptedData, kek) {
    const envelope = deserializeEnvelope(encryptedData);
    const result = decryptWithEnvelope(envelope, kek);
    return result.decryptedData.toString('utf-8');
}
export function encryptObject(obj, kek) {
    const jsonString = JSON.stringify(obj);
    return encryptString(jsonString, kek);
}
export function decryptObject(encryptedData, kek) {
    const jsonString = decryptString(encryptedData, kek);
    return JSON.parse(jsonString);
}
//# sourceMappingURL=envelope.js.map