From c82d3a46579ff9e2016f8be841d252b1d4f27eaf Mon Sep 17 00:00:00 2001 From: Jonas Lochmann Date: Mon, 19 Sep 2022 02:00:00 +0200 Subject: [PATCH] Add dh subsequence numbers --- src/database/devicedhkey.ts | 23 ++++++++++- .../migrations/20220921-add-dh-subsequence.ts | 39 +++++++++++++++++++ .../sync/get-server-data-status/dh-keys.ts | 33 +++++++++++++--- 3 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 src/database/migration/migrations/20220921-add-dh-subsequence.ts diff --git a/src/database/devicedhkey.ts b/src/database/devicedhkey.ts index c81b15c..449174d 100644 --- a/src/database/devicedhkey.ts +++ b/src/database/devicedhkey.ts @@ -33,7 +33,7 @@ export function calculateExpireTime(now: bigint): BigInt { return expireTime } -export interface DeviceDhKeyAttributes { +interface DeviceDhKeyAttributesVersion1 { familyId: string deviceId: string version: string @@ -43,12 +43,18 @@ export interface DeviceDhKeyAttributes { privateKey: Buffer } +interface DeviceDhKeyAttributesVersion2 { + createdAtSubsequence: number +} + +export type DeviceDhKeyAttributes = DeviceDhKeyAttributesVersion1 & DeviceDhKeyAttributesVersion2 + export type DeviceDhKeyModel = Sequelize.Model & DeviceDhKeyAttributes export type DeviceDhKeyModelStatic = typeof Sequelize.Model & { new (values?: object, options?: Sequelize.BuildOptions): DeviceDhKeyModel; } -export const attributes: SequelizeAttributes = { +export const attributesVersion1: SequelizeAttributes = { familyId: { ...familyIdColumn, primaryKey: true @@ -78,4 +84,17 @@ export const attributes: SequelizeAttributes = { } } +export const attributesVersion2: SequelizeAttributes = { + createdAtSubsequence: { + type: Sequelize.INTEGER, + allowNull: false, + defaultValue: 0 + } +} + +export const attributes: SequelizeAttributes = { + ...attributesVersion1, + ...attributesVersion2 +} + export const createDeviceDhKey = (sequelize: Sequelize.Sequelize): DeviceDhKeyModelStatic => sequelize.define('DeviceDhKey', attributes) as DeviceDhKeyModelStatic diff --git a/src/database/migration/migrations/20220921-add-dh-subsequence.ts b/src/database/migration/migrations/20220921-add-dh-subsequence.ts new file mode 100644 index 0000000..7157cd5 --- /dev/null +++ b/src/database/migration/migrations/20220921-add-dh-subsequence.ts @@ -0,0 +1,39 @@ +/* + * 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 . + */ + +import { QueryInterface, Sequelize, Transaction } from 'sequelize' +import { attributesVersion2 as dhKeyAttributes } from '../../devicedhkey' + +export async function up (queryInterface: QueryInterface, sequelize: Sequelize) { + await sequelize.transaction({ + type: Transaction.TYPES.EXCLUSIVE + }, async (transaction) => { + await queryInterface.addColumn('DeviceDhKeys', 'createdAtSubsequence', { + ...dhKeyAttributes.createdAtSubsequence + }, { + transaction + }) + }) +} + +export async function down (queryInterface: QueryInterface, sequelize: Sequelize) { + await sequelize.transaction({ + type: Transaction.TYPES.EXCLUSIVE + }, async (transaction) => { + await queryInterface.removeColumn('DeviceDhKeys', 'createdAtSubsequence', { transaction }) + }) +} diff --git a/src/function/sync/get-server-data-status/dh-keys.ts b/src/function/sync/get-server-data-status/dh-keys.ts index 71cd16c..7ff3f15 100644 --- a/src/function/sync/get-server-data-status/dh-keys.ts +++ b/src/function/sync/get-server-data-status/dh-keys.ts @@ -58,18 +58,30 @@ export async function getDeviceDhKeys ({ if (savedData.length >= 8) { eventHandler.countEvent('getDeviceDhKeys:gc') - const minCreatedAtValue = savedData.map((item) => BigInt(item.createdAt)).sort()[0] + 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, - createdAt: { - [Sequelize.Op.lte]: minCreatedAtValue.toString(10) - } + version: elementToRemove.item.version }, transaction }) + + savedData.splice(elementToRemove.index, 1) } await database.deviceDhKey.update({ @@ -83,11 +95,22 @@ export async function getDeviceDhKeys ({ transaction }) + const newItemCreatedAt = (now - now % BigInt(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: (now - now % BigInt(config.generationTimeRounding)).toString(10), + createdAt: newItemCreatedAt.toString(10), + createdAtSubsequence: Math.min(newItemCreatedAtSubsequence, 1 << 30), expireAt: null, publicKey: newKeypair.publicKey, privateKey: newKeypair.privateKey