"use strict";
/*
 * server component for the TimeLimit App
 * Copyright (C) 2019 - 2022 Jonas Lochmann
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.DecryptException = void 0;
exports.decrypt = decrypt;
const crypto_1 = require("crypto");
const token_1 = require("../../util/token");
const shared_secret_1 = require("./shared-secret");
async function decrypt({ database, transaction, familyId, deviceId, encryptedData, authData }) {
    const parts = encryptedData.split('.');
    if (parts.length !== 3)
        throw new MalformedDataDecryptException('expected three parts');
    const ivAndEncrypted = Buffer.from(parts[0], 'base64');
    const otherPublicKey = Buffer.from(parts[1], 'base64');
    const keyId = parts[2];
    if (ivAndEncrypted.length < 12 + 16)
        throw new MalformedDataDecryptException('too short for iv and auth tag');
    if (!(0, token_1.isVersionId)(keyId))
        throw new KeyNotFoundDecryptException('invalid key id');
    const sharedSecret = await (async () => {
        try {
            return (0, shared_secret_1.getSharedSecret)({
                database,
                transaction,
                familyId,
                deviceId,
                keyId,
                otherPublicKey
            });
        }
        catch (ex) {
            if (ex instanceof shared_secret_1.SharedSecretException)
                throw new SharedSecretDecryptException(ex);
            throw ex;
        }
    })();
    try {
        const decipher = (0, crypto_1.createDecipheriv)('aes-128-gcm', sharedSecret.sharedSecret.slice(0, 16), ivAndEncrypted.slice(0, 12), {
            authTagLength: 16
        });
        decipher.setAuthTag(ivAndEncrypted.slice(ivAndEncrypted.length - 16, ivAndEncrypted.length));
        decipher.setAAD(authData);
        const decryptedData = Buffer.concat([
            decipher.update(ivAndEncrypted.slice(12, ivAndEncrypted.length - 16)),
            decipher.final()
        ]);
        return decryptedData;
    }
    catch (ex) {
        throw new MalformedAuthenticationException();
    }
}
class DecryptException extends Error {
}
exports.DecryptException = DecryptException;
class SharedSecretDecryptException extends DecryptException {
    constructor(cause) { super(cause.message); }
}
class MalformedDataDecryptException extends DecryptException {
    constructor(message) { super('malformed data: ' + message); }
}
class MalformedAuthenticationException extends DecryptException {
    constructor() { super('authentication data'); }
}
class KeyNotFoundDecryptException extends DecryptException {
    constructor(message) { super('key not found: ' + message); }
}
//# sourceMappingURL=decrypt.js.map