"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.getDeviceDhKeys = getDeviceDhKeys;
const devicedhkey_1 = require("../../../database/devicedhkey");
const token_1 = require("../../../util/token");
const dh_1 = require("../../../function/dh");
async function getDeviceDhKeys({ database, transaction, familyEntry, deviceId, lastVersionId, eventHandler }) {
    const savedData = await database.deviceDhKey.findAll({
        where: {
            familyId: familyEntry.familyId,
            deviceId
        },
        transaction
    });
    const now = BigInt(Date.now());
    const oldCurrentKey = savedData.find((item) => item.expireAt === null);
    const needsNewKey = oldCurrentKey === undefined ||
        BigInt(oldCurrentKey.createdAt) + BigInt(devicedhkey_1.config.generateNewKeyAfterAge) <= now ||
        BigInt(oldCurrentKey.createdAt) > now;
    if (needsNewKey) {
        eventHandler.countEvent('getDeviceDhKeys:needsNewKey');
        const newVersion = (0, token_1.generateVersionId)();
        const newKeypair = await (0, dh_1.generateDhKeypair)();
        if (savedData.length >= 8) {
            eventHandler.countEvent('getDeviceDhKeys:gc');
            const elementToRemove = savedData.reduce((a, currentItem, currentIndex) => {
                const b = { item: currentItem, index: currentIndex };
                const createdA = BigInt(a.item.createdAt);
                const createdB = BigInt(b.item.createdAt);
                if (createdA > createdB)
                    return b;
                else if (createdA < createdB)
                    return a;
                else {
                    if (a.item.createdAtSubsequence > b.item.createdAtSubsequence)
                        return b;
                    else
                        return a;
                }
            }, { index: 0, item: savedData[0] });
            await database.deviceDhKey.destroy({
                where: {
                    familyId: familyEntry.familyId,
                    deviceId,
                    version: elementToRemove.item.version
                },
                transaction
            });
            savedData.splice(elementToRemove.index, 1);
        }
        await database.deviceDhKey.update({
            expireAt: (0, devicedhkey_1.calculateExpireTime)(now).toString(10)
        }, {
            where: {
                familyId: familyEntry.familyId,
                deviceId,
                expireAt: null
            },
            transaction
        });
        const newItemCreatedAt = (now - now % BigInt(devicedhkey_1.config.generationTimeRounding));
        const newItemExistingSubsequenceValues = savedData
            .filter((item) => BigInt(item.createdAt) === newItemCreatedAt)
            .map((item) => item.createdAtSubsequence);
        const newItemCreatedAtSubsequence = newItemExistingSubsequenceValues.reduce((max, item) => Math.max(max, item + 1), 0);
        await database.deviceDhKey.create({
            familyId: familyEntry.familyId,
            deviceId,
            version: newVersion,
            createdAt: newItemCreatedAt.toString(10),
            createdAtSubsequence: Math.min(newItemCreatedAtSubsequence, 1 << 30),
            expireAt: null,
            publicKey: newKeypair.publicKey,
            privateKey: newKeypair.privateKey
        }, { transaction });
        return {
            k: newKeypair.publicKey.toString('base64'),
            v: newVersion
        };
    }
    else {
        if (lastVersionId === oldCurrentKey.version)
            return null;
        else
            return {
                k: oldCurrentKey.publicKey.toString('base64'),
                v: oldCurrentKey.version
            };
    }
}
//# sourceMappingURL=dh-keys.js.map