diff --git a/docs/schema/ClientPullChangesRequest.schema.json b/docs/schema/ClientPullChangesRequest.schema.json
index c01277c..108e7f0 100644
--- a/docs/schema/ClientPullChangesRequest.schema.json
+++ b/docs/schema/ClientPullChangesRequest.schema.json
@@ -49,6 +49,9 @@
},
"kr": {
"type": "number"
+ },
+ "dh": {
+ "type": "string"
}
},
"additionalProperties": false,
diff --git a/docs/schema/README.md b/docs/schema/README.md
index 9708c1c..fdadbc4 100644
--- a/docs/schema/README.md
+++ b/docs/schema/README.md
@@ -216,6 +216,8 @@
* [ServerDeviceList](./serverdatastatus-definitions-serverdevicelist.md) – `https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceList`
+* [ServerDhKey](./serverdatastatus-definitions-serverdhkey.md) – `https://timelimit.io/ServerDataStatus#/definitions/ServerDhKey`
+
* [ServerExtendedDeviceData](./serverdatastatus-definitions-serverextendeddevicedata.md) – `https://timelimit.io/ServerDataStatus#/definitions/ServerExtendedDeviceData`
* [ServerInstalledAppsData](./serverdatastatus-definitions-serverinstalledappsdata.md) – `https://timelimit.io/ServerDataStatus#/definitions/ServerInstalledAppsData`
diff --git a/docs/schema/ServerDataStatus.schema.json b/docs/schema/ServerDataStatus.schema.json
index ffafea1..9166134 100644
--- a/docs/schema/ServerDataStatus.schema.json
+++ b/docs/schema/ServerDataStatus.schema.json
@@ -67,6 +67,9 @@
"$ref": "#/definitions/ServerKeyResponse"
}
},
+ "dh": {
+ "$ref": "#/definitions/ServerDhKey"
+ },
"fullVersion": {
"type": "number"
},
@@ -913,6 +916,23 @@
"tempKey"
],
"title": "ServerKeyResponse"
+ },
+ "ServerDhKey": {
+ "type": "object",
+ "properties": {
+ "v": {
+ "type": "string"
+ },
+ "k": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "k",
+ "v"
+ ],
+ "title": "ServerDhKey"
}
},
"$schema": "http://json-schema.org/draft-07/schema#",
diff --git a/docs/schema/clientpullchangesrequest-definitions-clientdatastatus-properties-dh.md b/docs/schema/clientpullchangesrequest-definitions-clientdatastatus-properties-dh.md
new file mode 100644
index 0000000..695202c
--- /dev/null
+++ b/docs/schema/clientpullchangesrequest-definitions-clientdatastatus-properties-dh.md
@@ -0,0 +1,15 @@
+# Untitled string in ClientPullChangesRequest Schema
+
+```txt
+https://timelimit.io/ClientPullChangesRequest#/definitions/ClientDataStatus/properties/dh
+```
+
+
+
+| Abstract | Extensible | Status | Identifiable | Custom Properties | Additional Properties | Access Restrictions | Defined In |
+| :------------------ | :--------- | :------------- | :---------------------- | :---------------- | :-------------------- | :------------------ | :---------------------------------------------------------------------------------------------------- |
+| Can be instantiated | No | Unknown status | Unknown identifiability | Forbidden | Allowed | none | [ClientPullChangesRequest.schema.json\*](ClientPullChangesRequest.schema.json "open original schema") |
+
+## dh Type
+
+`string`
diff --git a/docs/schema/clientpullchangesrequest-definitions-clientdatastatus.md b/docs/schema/clientpullchangesrequest-definitions-clientdatastatus.md
index fb9f4d1..d3bcd1c 100644
--- a/docs/schema/clientpullchangesrequest-definitions-clientdatastatus.md
+++ b/docs/schema/clientpullchangesrequest-definitions-clientdatastatus.md
@@ -26,6 +26,7 @@ https://timelimit.io/ClientPullChangesRequest#/definitions/ClientDataStatus
| [devicesDetail](#devicesdetail) | `object` | Optional | cannot be null | [ClientPullChangesRequest](clientpullchangesrequest-definitions-clientdatastatus-properties-devicesdetail.md "https://timelimit.io/ClientPullChangesRequest#/definitions/ClientDataStatus/properties/devicesDetail") |
| [kri](#kri) | `number` | Optional | cannot be null | [ClientPullChangesRequest](clientpullchangesrequest-definitions-clientdatastatus-properties-kri.md "https://timelimit.io/ClientPullChangesRequest#/definitions/ClientDataStatus/properties/kri") |
| [kr](#kr) | `number` | Optional | cannot be null | [ClientPullChangesRequest](clientpullchangesrequest-definitions-clientdatastatus-properties-kr.md "https://timelimit.io/ClientPullChangesRequest#/definitions/ClientDataStatus/properties/kr") |
+| [dh](#dh) | `string` | Optional | cannot be null | [ClientPullChangesRequest](clientpullchangesrequest-definitions-clientdatastatus-properties-dh.md "https://timelimit.io/ClientPullChangesRequest#/definitions/ClientDataStatus/properties/dh") |
## devices
@@ -170,3 +171,21 @@ https://timelimit.io/ClientPullChangesRequest#/definitions/ClientDataStatus
### kr Type
`number`
+
+## dh
+
+
+
+`dh`
+
+* is optional
+
+* Type: `string`
+
+* cannot be null
+
+* defined in: [ClientPullChangesRequest](clientpullchangesrequest-definitions-clientdatastatus-properties-dh.md "https://timelimit.io/ClientPullChangesRequest#/definitions/ClientDataStatus/properties/dh")
+
+### dh Type
+
+`string`
diff --git a/docs/schema/clientpullchangesrequest.md b/docs/schema/clientpullchangesrequest.md
index 33020ac..17231f5 100644
--- a/docs/schema/clientpullchangesrequest.md
+++ b/docs/schema/clientpullchangesrequest.md
@@ -77,6 +77,7 @@ Reference this group by using
| [devicesDetail](#devicesdetail) | `object` | Optional | cannot be null | [ClientPullChangesRequest](clientpullchangesrequest-definitions-clientdatastatus-properties-devicesdetail.md "https://timelimit.io/ClientPullChangesRequest#/definitions/ClientDataStatus/properties/devicesDetail") |
| [kri](#kri) | `number` | Optional | cannot be null | [ClientPullChangesRequest](clientpullchangesrequest-definitions-clientdatastatus-properties-kri.md "https://timelimit.io/ClientPullChangesRequest#/definitions/ClientDataStatus/properties/kri") |
| [kr](#kr) | `number` | Optional | cannot be null | [ClientPullChangesRequest](clientpullchangesrequest-definitions-clientdatastatus-properties-kr.md "https://timelimit.io/ClientPullChangesRequest#/definitions/ClientDataStatus/properties/kr") |
+| [dh](#dh) | `string` | Optional | cannot be null | [ClientPullChangesRequest](clientpullchangesrequest-definitions-clientdatastatus-properties-dh.md "https://timelimit.io/ClientPullChangesRequest#/definitions/ClientDataStatus/properties/dh") |
### devices
@@ -222,6 +223,24 @@ Reference this group by using
`number`
+### dh
+
+
+
+`dh`
+
+* is optional
+
+* Type: `string`
+
+* cannot be null
+
+* defined in: [ClientPullChangesRequest](clientpullchangesrequest-definitions-clientdatastatus-properties-dh.md "https://timelimit.io/ClientPullChangesRequest#/definitions/ClientDataStatus/properties/dh")
+
+#### dh Type
+
+`string`
+
## Definitions group CategoryDataStatus
Reference this group by using
diff --git a/docs/schema/serverdatastatus-definitions-serverdhkey-properties-k.md b/docs/schema/serverdatastatus-definitions-serverdhkey-properties-k.md
new file mode 100644
index 0000000..81d59f0
--- /dev/null
+++ b/docs/schema/serverdatastatus-definitions-serverdhkey-properties-k.md
@@ -0,0 +1,15 @@
+# Untitled string in ServerDataStatus Schema
+
+```txt
+https://timelimit.io/ServerDataStatus#/definitions/ServerDhKey/properties/k
+```
+
+
+
+| Abstract | Extensible | Status | Identifiable | Custom Properties | Additional Properties | Access Restrictions | Defined In |
+| :------------------ | :--------- | :------------- | :---------------------- | :---------------- | :-------------------- | :------------------ | :------------------------------------------------------------------------------------ |
+| Can be instantiated | No | Unknown status | Unknown identifiability | Forbidden | Allowed | none | [ServerDataStatus.schema.json\*](ServerDataStatus.schema.json "open original schema") |
+
+## k Type
+
+`string`
diff --git a/docs/schema/serverdatastatus-definitions-serverdhkey-properties-v.md b/docs/schema/serverdatastatus-definitions-serverdhkey-properties-v.md
new file mode 100644
index 0000000..f6942f9
--- /dev/null
+++ b/docs/schema/serverdatastatus-definitions-serverdhkey-properties-v.md
@@ -0,0 +1,15 @@
+# Untitled string in ServerDataStatus Schema
+
+```txt
+https://timelimit.io/ServerDataStatus#/definitions/ServerDhKey/properties/v
+```
+
+
+
+| Abstract | Extensible | Status | Identifiable | Custom Properties | Additional Properties | Access Restrictions | Defined In |
+| :------------------ | :--------- | :------------- | :---------------------- | :---------------- | :-------------------- | :------------------ | :------------------------------------------------------------------------------------ |
+| Can be instantiated | No | Unknown status | Unknown identifiability | Forbidden | Allowed | none | [ServerDataStatus.schema.json\*](ServerDataStatus.schema.json "open original schema") |
+
+## v Type
+
+`string`
diff --git a/docs/schema/serverdatastatus-definitions-serverdhkey.md b/docs/schema/serverdatastatus-definitions-serverdhkey.md
new file mode 100644
index 0000000..9ec7cae
--- /dev/null
+++ b/docs/schema/serverdatastatus-definitions-serverdhkey.md
@@ -0,0 +1,58 @@
+# ServerDhKey Schema
+
+```txt
+https://timelimit.io/ServerDataStatus#/definitions/ServerDhKey
+```
+
+
+
+| Abstract | Extensible | Status | Identifiable | Custom Properties | Additional Properties | Access Restrictions | Defined In |
+| :------------------ | :--------- | :------------- | :----------- | :---------------- | :-------------------- | :------------------ | :------------------------------------------------------------------------------------ |
+| Can be instantiated | No | Unknown status | No | Forbidden | Forbidden | none | [ServerDataStatus.schema.json\*](ServerDataStatus.schema.json "open original schema") |
+
+## ServerDhKey Type
+
+`object` ([ServerDhKey](serverdatastatus-definitions-serverdhkey.md))
+
+# ServerDhKey Properties
+
+| Property | Type | Required | Nullable | Defined by |
+| :------- | :------- | :------- | :------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [v](#v) | `string` | Required | cannot be null | [ServerDataStatus](serverdatastatus-definitions-serverdhkey-properties-v.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDhKey/properties/v") |
+| [k](#k) | `string` | Required | cannot be null | [ServerDataStatus](serverdatastatus-definitions-serverdhkey-properties-k.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDhKey/properties/k") |
+
+## v
+
+
+
+`v`
+
+* is required
+
+* Type: `string`
+
+* cannot be null
+
+* defined in: [ServerDataStatus](serverdatastatus-definitions-serverdhkey-properties-v.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDhKey/properties/v")
+
+### v Type
+
+`string`
+
+## k
+
+
+
+`k`
+
+* is required
+
+* Type: `string`
+
+* cannot be null
+
+* defined in: [ServerDataStatus](serverdatastatus-definitions-serverdhkey-properties-k.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDhKey/properties/k")
+
+### k Type
+
+`string`
diff --git a/docs/schema/serverdatastatus-properties-dh-properties-k.md b/docs/schema/serverdatastatus-properties-dh-properties-k.md
new file mode 100644
index 0000000..a242982
--- /dev/null
+++ b/docs/schema/serverdatastatus-properties-dh-properties-k.md
@@ -0,0 +1,15 @@
+# Untitled string in ServerDataStatus Schema
+
+```txt
+https://timelimit.io/ServerDataStatus#/properties/dh/properties/k
+```
+
+
+
+| Abstract | Extensible | Status | Identifiable | Custom Properties | Additional Properties | Access Restrictions | Defined In |
+| :------------------ | :--------- | :------------- | :---------------------- | :---------------- | :-------------------- | :------------------ | :------------------------------------------------------------------------------------ |
+| Can be instantiated | No | Unknown status | Unknown identifiability | Forbidden | Allowed | none | [ServerDataStatus.schema.json\*](ServerDataStatus.schema.json "open original schema") |
+
+## k Type
+
+`string`
diff --git a/docs/schema/serverdatastatus-properties-dh-properties-v.md b/docs/schema/serverdatastatus-properties-dh-properties-v.md
new file mode 100644
index 0000000..d51ed91
--- /dev/null
+++ b/docs/schema/serverdatastatus-properties-dh-properties-v.md
@@ -0,0 +1,15 @@
+# Untitled string in ServerDataStatus Schema
+
+```txt
+https://timelimit.io/ServerDataStatus#/properties/dh/properties/v
+```
+
+
+
+| Abstract | Extensible | Status | Identifiable | Custom Properties | Additional Properties | Access Restrictions | Defined In |
+| :------------------ | :--------- | :------------- | :---------------------- | :---------------- | :-------------------- | :------------------ | :------------------------------------------------------------------------------------ |
+| Can be instantiated | No | Unknown status | Unknown identifiability | Forbidden | Allowed | none | [ServerDataStatus.schema.json\*](ServerDataStatus.schema.json "open original schema") |
+
+## v Type
+
+`string`
diff --git a/docs/schema/serverdatastatus-properties-dh.md b/docs/schema/serverdatastatus-properties-dh.md
new file mode 100644
index 0000000..c2cca49
--- /dev/null
+++ b/docs/schema/serverdatastatus-properties-dh.md
@@ -0,0 +1,58 @@
+# Untitled object in ServerDataStatus Schema
+
+```txt
+https://timelimit.io/ServerDataStatus#/properties/dh
+```
+
+
+
+| Abstract | Extensible | Status | Identifiable | Custom Properties | Additional Properties | Access Restrictions | Defined In |
+| :------------------ | :--------- | :------------- | :----------- | :---------------- | :-------------------- | :------------------ | :------------------------------------------------------------------------------------ |
+| Can be instantiated | No | Unknown status | No | Forbidden | Forbidden | none | [ServerDataStatus.schema.json\*](ServerDataStatus.schema.json "open original schema") |
+
+## dh Type
+
+`object` ([Details](serverdatastatus-properties-dh.md))
+
+# dh Properties
+
+| Property | Type | Required | Nullable | Defined by |
+| :------- | :------- | :------- | :------------- | :------------------------------------------------------------------------------------------------------------------------------------- |
+| [v](#v) | `string` | Required | cannot be null | [ServerDataStatus](serverdatastatus-properties-dh-properties-v.md "https://timelimit.io/ServerDataStatus#/properties/dh/properties/v") |
+| [k](#k) | `string` | Required | cannot be null | [ServerDataStatus](serverdatastatus-properties-dh-properties-k.md "https://timelimit.io/ServerDataStatus#/properties/dh/properties/k") |
+
+## v
+
+
+
+`v`
+
+* is required
+
+* Type: `string`
+
+* cannot be null
+
+* defined in: [ServerDataStatus](serverdatastatus-properties-dh-properties-v.md "https://timelimit.io/ServerDataStatus#/properties/dh/properties/v")
+
+### v Type
+
+`string`
+
+## k
+
+
+
+`k`
+
+* is required
+
+* Type: `string`
+
+* cannot be null
+
+* defined in: [ServerDataStatus](serverdatastatus-properties-dh-properties-k.md "https://timelimit.io/ServerDataStatus#/properties/dh/properties/k")
+
+### k Type
+
+`string`
diff --git a/docs/schema/serverdatastatus.md b/docs/schema/serverdatastatus.md
index 3b2ede4..4ead767 100644
--- a/docs/schema/serverdatastatus.md
+++ b/docs/schema/serverdatastatus.md
@@ -30,6 +30,7 @@ https://timelimit.io/ServerDataStatus
| [users](#users) | `object` | Optional | cannot be null | [ServerDataStatus](serverdatastatus-definitions-serveruserlist.md "https://timelimit.io/ServerDataStatus#/properties/users") |
| [krq](#krq) | `array` | Optional | cannot be null | [ServerDataStatus](serverdatastatus-properties-krq.md "https://timelimit.io/ServerDataStatus#/properties/krq") |
| [kr](#kr) | `array` | Optional | cannot be null | [ServerDataStatus](serverdatastatus-properties-kr.md "https://timelimit.io/ServerDataStatus#/properties/kr") |
+| [dh](#dh) | `object` | Optional | cannot be null | [ServerDataStatus](serverdatastatus-definitions-serverdhkey.md "https://timelimit.io/ServerDataStatus#/properties/dh") |
| [fullVersion](#fullversion) | `number` | Required | cannot be null | [ServerDataStatus](serverdatastatus-properties-fullversion.md "https://timelimit.io/ServerDataStatus#/properties/fullVersion") |
| [message](#message) | `string` | Optional | cannot be null | [ServerDataStatus](serverdatastatus-properties-message.md "https://timelimit.io/ServerDataStatus#/properties/message") |
| [apiLevel](#apilevel) | `number` | Required | cannot be null | [ServerDataStatus](serverdatastatus-properties-apilevel.md "https://timelimit.io/ServerDataStatus#/properties/apiLevel") |
@@ -250,6 +251,24 @@ https://timelimit.io/ServerDataStatus
`object[]` ([ServerKeyResponse](serverdatastatus-definitions-serverkeyresponse.md))
+## dh
+
+
+
+`dh`
+
+* is optional
+
+* Type: `object` ([ServerDhKey](serverdatastatus-definitions-serverdhkey.md))
+
+* cannot be null
+
+* defined in: [ServerDataStatus](serverdatastatus-definitions-serverdhkey.md "https://timelimit.io/ServerDataStatus#/properties/dh")
+
+### dh Type
+
+`object` ([ServerDhKey](serverdatastatus-definitions-serverdhkey.md))
+
## fullVersion
@@ -3372,3 +3391,52 @@ Reference this group by using
#### signature Type
`string`
+
+## Definitions group ServerDhKey
+
+Reference this group by using
+
+```json
+{"$ref":"https://timelimit.io/ServerDataStatus#/definitions/ServerDhKey"}
+```
+
+| Property | Type | Required | Nullable | Defined by |
+| :------- | :------- | :------- | :------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [v](#v) | `string` | Required | cannot be null | [ServerDataStatus](serverdatastatus-definitions-serverdhkey-properties-v.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDhKey/properties/v") |
+| [k](#k) | `string` | Required | cannot be null | [ServerDataStatus](serverdatastatus-definitions-serverdhkey-properties-k.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDhKey/properties/k") |
+
+### v
+
+
+
+`v`
+
+* is required
+
+* Type: `string`
+
+* cannot be null
+
+* defined in: [ServerDataStatus](serverdatastatus-definitions-serverdhkey-properties-v.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDhKey/properties/v")
+
+#### v Type
+
+`string`
+
+### k
+
+
+
+`k`
+
+* is required
+
+* Type: `string`
+
+* cannot be null
+
+* defined in: [ServerDataStatus](serverdatastatus-definitions-serverdhkey-properties-k.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDhKey/properties/k")
+
+#### k Type
+
+`string`
diff --git a/src/api/sync.ts b/src/api/sync.ts
index 224d892..6fee2b5 100644
--- a/src/api/sync.ts
+++ b/src/api/sync.ts
@@ -117,7 +117,8 @@ export const createSyncRouter = ({ database, websocket, connectedDevicesManager,
familyId,
deviceId,
clientStatus: body.status,
- transaction
+ transaction,
+ eventHandler
})
})
@@ -128,6 +129,9 @@ export const createSyncRouter = ({ database, websocket, connectedDevicesManager,
if (serverStatus.usedTimes) { eventHandler.countEvent('pullStatusRequest usedTimes') }
if (serverStatus.rules) { eventHandler.countEvent('pullStatusRequest rules') }
if (serverStatus.users) { eventHandler.countEvent('pullStatusRequest users') }
+ if (serverStatus.krq) { eventHandler.countEvent('pullStatusRequest pendingKeyRequests') }
+ if (serverStatus.kr) { eventHandler.countEvent('pullStatusRequest keyResponses') }
+ if (serverStatus.dh) { eventHandler.countEvent('pullStatusRequest dh') }
res.json(serverStatus)
} catch (ex) {
diff --git a/src/api/validator.ts b/src/api/validator.ts
index da15c92..4698c35 100644
--- a/src/api/validator.ts
+++ b/src/api/validator.ts
@@ -72,6 +72,9 @@ const definitions = {
},
"kr": {
"type": "number"
+ },
+ "dh": {
+ "type": "string"
}
},
"additionalProperties": false,
@@ -2705,6 +2708,22 @@ const definitions = {
"srvSeq",
"tempKey"
]
+ },
+ "ServerDhKey": {
+ "type": "object",
+ "properties": {
+ "v": {
+ "type": "string"
+ },
+ "k": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "k",
+ "v"
+ ]
}
}
diff --git a/src/database/devicedhkey.ts b/src/database/devicedhkey.ts
new file mode 100644
index 0000000..c81b15c
--- /dev/null
+++ b/src/database/devicedhkey.ts
@@ -0,0 +1,81 @@
+/*
+ * 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 * as Sequelize from 'sequelize'
+import { familyIdColumn, idWithinFamilyColumn, versionColumn, timestampColumn } from './columns'
+import { SequelizeAttributes } from './types'
+
+export const config = {
+ generateNewKeyAfterAge: 1000 * 60 * 60 * 24,
+ generationTimeRounding: 1000 * 60 * 60,
+ expireDelay: 1000 * 60 * 60 * 2,
+ expireTimeRounding: 1000 * 60 * 15
+}
+
+export function calculateExpireTime(now: bigint): BigInt {
+ const expireBaseTime = now + BigInt(config.expireDelay)
+ const expireTime = expireBaseTime - expireBaseTime % BigInt(config.expireTimeRounding) + BigInt(config.expireTimeRounding)
+
+ return expireTime
+}
+
+export interface DeviceDhKeyAttributes {
+ familyId: string
+ deviceId: string
+ version: string
+ createdAt: string
+ expireAt: string | null
+ publicKey: Buffer
+ privateKey: Buffer
+}
+
+export type DeviceDhKeyModel = Sequelize.Model & DeviceDhKeyAttributes
+export type DeviceDhKeyModelStatic = typeof Sequelize.Model & {
+ new (values?: object, options?: Sequelize.BuildOptions): DeviceDhKeyModel;
+}
+
+export const attributes: SequelizeAttributes = {
+ familyId: {
+ ...familyIdColumn,
+ primaryKey: true
+ },
+ deviceId: {
+ ...idWithinFamilyColumn,
+ primaryKey: true
+ },
+ version: {
+ ...versionColumn,
+ primaryKey: true
+ },
+ createdAt: {
+ ...timestampColumn
+ },
+ expireAt: {
+ ...timestampColumn,
+ allowNull: true
+ },
+ publicKey: {
+ type: Sequelize.BLOB,
+ allowNull: false
+ },
+ privateKey: {
+ type: Sequelize.BLOB,
+ allowNull: false
+ }
+}
+
+export const createDeviceDhKey = (sequelize: Sequelize.Sequelize): DeviceDhKeyModelStatic => sequelize.define('DeviceDhKey', attributes) as DeviceDhKeyModelStatic
diff --git a/src/database/main.ts b/src/database/main.ts
index 95ed3b8..379cde8 100644
--- a/src/database/main.ts
+++ b/src/database/main.ts
@@ -27,6 +27,7 @@ import { CategoryTimeWarningModelStatic, createCategoryTimeWarningModel } from '
import { ChildTaskModelStatic, createChildTaskModel } from './childtask'
import { ConfigModelStatic, createConfigModel } from './config'
import { createDeviceModel, DeviceModelStatic } from './device'
+import { createDeviceDhKey, DeviceDhKeyModelStatic } from './devicedhkey'
import { createEncryptedAppListModel, EncryptedAppListModelStatic } from './encryptedapplist'
import { createFamilyModel, FamilyModelStatic } from './family'
import { createKeyRequestModel, KeyRequestModelStatic } from './keyrequest'
@@ -55,6 +56,7 @@ export interface Database {
childTask: ChildTaskModelStatic
config: ConfigModelStatic
device: DeviceModelStatic
+ deviceDhKey: DeviceDhKeyModelStatic
encryptedAppList: EncryptedAppListModelStatic
family: FamilyModelStatic
keyRequest: KeyRequestModelStatic
@@ -83,6 +85,7 @@ const createDatabase = (sequelize: Sequelize.Sequelize): Database => ({
categoryTimeWarning: createCategoryTimeWarningModel(sequelize),
config: createConfigModel(sequelize),
device: createDeviceModel(sequelize),
+ deviceDhKey: createDeviceDhKey(sequelize),
encryptedAppList: createEncryptedAppListModel(sequelize),
family: createFamilyModel(sequelize),
keyRequest: createKeyRequestModel(sequelize),
diff --git a/src/database/migration/migrations/20220913-create-device-dh-keys.ts b/src/database/migration/migrations/20220913-create-device-dh-keys.ts
new file mode 100644
index 0000000..0929d3d
--- /dev/null
+++ b/src/database/migration/migrations/20220913-create-device-dh-keys.ts
@@ -0,0 +1,70 @@
+/*
+ * 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'
+
+export async function up (queryInterface: QueryInterface, sequelize: Sequelize) {
+ await sequelize.transaction({
+ type: Transaction.TYPES.EXCLUSIVE
+ }, async (transaction) => {
+ const dialect = sequelize.getDialect()
+ const isMysql = dialect === 'mysql' || dialect === 'mariadb'
+ const isPosgresql = dialect === 'postgres'
+
+ if (isMysql) {
+ await sequelize.query(
+ 'CREATE TABLE `DeviceDhKeys` ' +
+ '(`familyId` VARCHAR(10) NOT NULL,' +
+ '`deviceId` VARCHAR(6) NOT NULL,' +
+ '`version` VARCHAR(4) NOT NULL,' +
+ '`createdAt` BIGINT NOT NULL, ' +
+ '`expireAt` BIGINT NULL, ' +
+ '`publicKey` BLOB NOT NULL, ' +
+ '`privateKey` BLOB NOT NULL, ' +
+ 'PRIMARY KEY (`familyId`, `deviceId`, `version`),' +
+ 'FOREIGN KEY (`familyId`, `deviceId`) REFERENCES `Devices` (`familyId`, `deviceId`) ON UPDATE CASCADE ON DELETE CASCADE' +
+ ')',
+ { transaction }
+ )
+ } else {
+ await sequelize.query(
+ 'CREATE TABLE "DeviceDhKeys" ' +
+ '("familyId" VARCHAR(10) NOT NULL,' +
+ '"deviceId" VARCHAR(6) NOT NULL,' +
+ '"version" VARCHAR(4) NOT NULL,' +
+ '"createdAt" ' + (isPosgresql ? 'BIGINT' : 'LONG') + ' NOT NULL, ' +
+ '"expireAt" ' + (isPosgresql ? 'BIGINT' : 'LONG') + ' NULL, ' +
+ '"publicKey" ' + (isPosgresql ? 'BYTEA' : 'BLOB') + ' NOT NULL, ' +
+ '"privateKey" ' + (isPosgresql ? 'BYTEA' : 'BLOB') + ' NOT NULL, ' +
+ 'PRIMARY KEY ("familyId", "deviceId", "version"),' +
+ 'FOREIGN KEY ("familyId", "deviceId") REFERENCES "Devices" ("familyId", "deviceId") ON UPDATE CASCADE ON DELETE CASCADE' +
+ ')',
+ { transaction }
+ )
+ }
+
+ await queryInterface.addIndex('DeviceDhKeys', ['expireAt'], { transaction })
+ })
+}
+
+export async function down (queryInterface: QueryInterface, sequelize: Sequelize) {
+ await sequelize.transaction({
+ type: Transaction.TYPES.EXCLUSIVE
+ }, async (transaction) => {
+ await queryInterface.dropTable('DeviceDhKeys', { 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
new file mode 100644
index 0000000..b1a87dd
--- /dev/null
+++ b/src/function/sync/get-server-data-status/dh-keys.ts
@@ -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 .
+ */
+
+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 {
+ 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
+ }
+ }
+}
diff --git a/src/function/sync/get-server-data-status/index.ts b/src/function/sync/get-server-data-status/index.ts
index 95ffe67..6e3dfd2 100644
--- a/src/function/sync/get-server-data-status/index.ts
+++ b/src/function/sync/get-server-data-status/index.ts
@@ -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 => {
+ 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
}
diff --git a/src/object/clientdatastatus.ts b/src/object/clientdatastatus.ts
index 531f17e..fdc7ea3 100644
--- a/src/object/clientdatastatus.ts
+++ b/src/object/clientdatastatus.ts
@@ -24,6 +24,7 @@ export interface ClientDataStatus {
devicesDetail?: ClientDataStatusDevicesExtended
kri?: number // last key request index
kr?: number // last key response index
+ dh?: string // last Diffie Hellman key version
}
export type ClientDataStatusApps = {[key: string]: string} // installedAppsVersionsByDeviceId
diff --git a/src/object/serverdatastatus.ts b/src/object/serverdatastatus.ts
index 78a1437..b880eeb 100644
--- a/src/object/serverdatastatus.ts
+++ b/src/object/serverdatastatus.ts
@@ -34,6 +34,7 @@ export interface ServerDataStatus {
users?: ServerUserList // newUserList
krq?: Array // pendingKeyRequests
kr?: Array // keyResponses
+ dh?: ServerDhKey // Diffie Hellman
fullVersion: number // fullVersionUntil
message?: string
apiLevel: number
@@ -252,3 +253,8 @@ export interface ServerKeyResponse {
cryptKey: string,
signature: string
}
+
+export interface ServerDhKey {
+ v: string // version
+ k: string // key, base64
+}
diff --git a/src/worker/delete-old-tokens.ts b/src/worker/delete-old-tokens.ts
index 66d30df..ac2f901 100644
--- a/src/worker/delete-old-tokens.ts
+++ b/src/worker/delete-old-tokens.ts
@@ -1,6 +1,6 @@
/*
* server component for the TimeLimit App
- * Copyright (C) 2019 - 2021 Jonas Lochmann
+ * 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
@@ -71,4 +71,12 @@ async function deleteOldTokens ({ database }: {
transaction
})
})
+
+ await database.deviceDhKey.destroy({
+ where: {
+ expireAt: {
+ [Sequelize.Op.lt]: Date.now().toString()
+ }
+ }
+ })
}