mirror of
https://codeberg.org/timelimit/timelimit-server.git
synced 2026-08-31 19:03:45 +02:00
Add generating dh keys
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
import * as Sequelize from 'sequelize'
|
||||
import { Database } from '../../../database'
|
||||
import { config, calculateExpireTime } from '../../../database/devicedhkey'
|
||||
import { ServerDhKey } from '../../../object/serverdatastatus'
|
||||
import { generateVersionId } from '../../../util/token'
|
||||
import { EventHandler } from '../../../monitoring/eventhandler'
|
||||
import { FamilyEntry } from './family-entry'
|
||||
import { generateKeyPair } from 'crypto'
|
||||
import { promisify } from 'util'
|
||||
|
||||
const generateKeyPairAsync = promisify(generateKeyPair)
|
||||
|
||||
export async function getDeviceDhKeys ({
|
||||
database, transaction, familyEntry, deviceId, lastVersionId, eventHandler
|
||||
}: {
|
||||
database: Database
|
||||
transaction: Sequelize.Transaction
|
||||
familyEntry: FamilyEntry
|
||||
deviceId: string
|
||||
lastVersionId: string | null
|
||||
eventHandler: EventHandler
|
||||
}): Promise<ServerDhKey | null> {
|
||||
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(config.generateNewKeyAfterAge) <= now ||
|
||||
BigInt(oldCurrentKey.createdAt) > now
|
||||
|
||||
if (needsNewKey) {
|
||||
eventHandler.countEvent('getDeviceDhKeys:needsNewKey')
|
||||
|
||||
const newVersion = generateVersionId()
|
||||
const newKeypair = await generateKeyPairAsync(
|
||||
'ec',
|
||||
{
|
||||
namedCurve: 'prime256v1',
|
||||
publicKeyEncoding: {
|
||||
type: 'spki',
|
||||
format: 'der'
|
||||
},
|
||||
privateKeyEncoding: {
|
||||
type: 'pkcs8',
|
||||
format: 'der'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (savedData.length >= 8) {
|
||||
eventHandler.countEvent('getDeviceDhKeys:gc')
|
||||
|
||||
const minCreatedAtValue = savedData.map((item) => BigInt(item.createdAt)).sort()[0]
|
||||
|
||||
await database.deviceDhKey.destroy({
|
||||
where: {
|
||||
familyId: familyEntry.familyId,
|
||||
deviceId,
|
||||
createdAt: {
|
||||
[Sequelize.Op.lte]: minCreatedAtValue.toString(10)
|
||||
}
|
||||
},
|
||||
transaction
|
||||
})
|
||||
}
|
||||
|
||||
await database.deviceDhKey.update({
|
||||
expireAt: calculateExpireTime(now).toString(10)
|
||||
}, {
|
||||
where: {
|
||||
familyId: familyEntry.familyId,
|
||||
deviceId,
|
||||
expireAt: null
|
||||
},
|
||||
transaction
|
||||
})
|
||||
|
||||
await database.deviceDhKey.create({
|
||||
familyId: familyEntry.familyId,
|
||||
deviceId,
|
||||
version: newVersion,
|
||||
createdAt: (now - now % BigInt(config.generationTimeRounding)).toString(10),
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import { Database } from '../../../database'
|
||||
import { getStatusMessage } from '../../../function/statusmessage'
|
||||
import { ClientDataStatus } from '../../../object/clientdatastatus'
|
||||
import { ServerDataStatus } from '../../../object/serverdatastatus'
|
||||
import { EventHandler } from '../../../monitoring/eventhandler'
|
||||
import { getAppList } from './app-list'
|
||||
import {
|
||||
getCategoryAssignedApps, getCategoryBaseDatas, getCategoryDataToSync,
|
||||
@@ -28,23 +29,28 @@ import {
|
||||
} from './category'
|
||||
import { getDeviceDetailList } from './device-detail'
|
||||
import { getDeviceList } from './device-list'
|
||||
import { getDeviceDhKeys } from './dh-keys'
|
||||
import { getFamilyEntry } from './family-entry'
|
||||
import { getUserList } from './user-list'
|
||||
import { getKeyRequests } from './key-requests'
|
||||
import { getKeyResponses } from './key-responses'
|
||||
|
||||
export const generateServerDataStatus = async ({
|
||||
database, clientStatus, familyId, deviceId, transaction
|
||||
database, clientStatus, familyId, deviceId, transaction, eventHandler
|
||||
}: {
|
||||
database: Database
|
||||
clientStatus: ClientDataStatus
|
||||
familyId: string
|
||||
deviceId: string
|
||||
transaction: Sequelize.Transaction
|
||||
eventHandler: EventHandler
|
||||
}): Promise<ServerDataStatus> => {
|
||||
const clientLevel = clientStatus.clientLevel || 0
|
||||
|
||||
const familyEntry = await getFamilyEntry({ database, familyId, transaction })
|
||||
const doesClientSupportTasks = clientStatus.clientLevel !== undefined && clientStatus.clientLevel >= 3
|
||||
const doesClientSupportCryptoApps = clientStatus.clientLevel !== undefined && clientStatus.clientLevel >= 4
|
||||
const doesClientSupportTasks = clientLevel >= 3
|
||||
const doesClientSupportCryptoApps = clientLevel >= 4
|
||||
const doesClientSupportDh = clientLevel >= 5
|
||||
|
||||
const result: ServerDataStatus = {
|
||||
fullVersion: config.alwaysPro ? 1 : (
|
||||
@@ -135,5 +141,16 @@ export const generateServerDataStatus = async ({
|
||||
}) || undefined
|
||||
}
|
||||
|
||||
if (doesClientSupportDh) {
|
||||
result.dh = await getDeviceDhKeys({
|
||||
database,
|
||||
transaction,
|
||||
familyEntry,
|
||||
deviceId,
|
||||
lastVersionId: clientStatus.dh || null,
|
||||
eventHandler
|
||||
}) || undefined
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user