Add U2F support

This commit is contained in:
Jonas Lochmann
2022-09-22 08:47:06 +02:00
parent 04aa2ce517
commit 613776cbf9
64 changed files with 2501 additions and 134 deletions
+52
View File
@@ -16,6 +16,7 @@
*/
import { json } from 'body-parser'
import { createHmac } from 'crypto'
import { Router } from 'express'
import { BadRequest, Forbidden, Unauthorized } from 'http-errors'
import { config } from '../config'
@@ -27,6 +28,7 @@ import { getStatusByMailToken } from '../function/parent/get-status-by-mail-addr
import { linkMailAddress } from '../function/parent/link-mail-address'
import { recoverParentPassword } from '../function/parent/recover-parent-password'
import { signInIntoFamily } from '../function/parent/sign-in-into-family'
import { validateU2fIntegrity, U2fValidationError } from '../function/u2f'
import { createIdentityToken, MissingSignSecretException } from '../util/identity-token'
import { WebsocketApi } from '../websocket'
import {
@@ -168,6 +170,56 @@ export const createParentRouter = ({ database, websocket }: {database: Database,
}
return { deviceEntry, parentEntry }
} else if (secondPasswordHash.startsWith('u2f:')) {
try {
const familyEntryUnsafe = await database.family.findOne({
where: {
familyId: deviceEntry.familyId
},
transaction,
attributes: ['hasFullVersion']
})
if (!familyEntryUnsafe) {
throw new Unauthorized()
}
const familyEntry = { hasFullVersion: familyEntryUnsafe.hasFullVersion }
const hasFullVersion = familyEntry.hasFullVersion || config.alwaysPro
const u2fResult = await validateU2fIntegrity({
integrity: secondPasswordHash,
hasFullVersion,
familyId: deviceEntry.familyId,
deviceId: deviceEntry.deviceId,
database,
transaction,
calculateHmac: (secret) => createHmac('sha256', secret)
.update('direct action')
.digest()
})
if (u2fResult.userId !== parentId) throw new Unauthorized()
const parentEntry = await database.user.findOne({
where: {
familyId: deviceEntry.familyId,
type: 'parent',
userId: u2fResult.userId
},
transaction
})
if (!parentEntry) {
throw new Unauthorized()
}
return { deviceEntry, parentEntry }
} catch (ex) {
if (ex instanceof U2fValidationError) throw new Unauthorized()
else throw ex
}
} else {
const parentEntry = await database.user.findOne({
where: {
+1
View File
@@ -132,6 +132,7 @@ export const createSyncRouter = ({ database, websocket, connectedDevicesManager,
if (serverStatus.krq) { eventHandler.countEvent('pullStatusRequest pendingKeyRequests') }
if (serverStatus.kr) { eventHandler.countEvent('pullStatusRequest keyResponses') }
if (serverStatus.dh) { eventHandler.countEvent('pullStatusRequest dh') }
if (serverStatus.u2f) { eventHandler.countEvent('pullStatusRequest u2f') }
res.json(serverStatus)
} catch (ex) {
+116
View File
@@ -75,6 +75,9 @@ const definitions = {
},
"dh": {
"type": "string"
},
"u2f": {
"type": "string"
}
},
"additionalProperties": false,
@@ -209,6 +212,29 @@ const definitions = {
"type"
]
},
"SerializedAddParentU2fKeyAction": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"ADD_PARENT_U2F"
]
},
"keyHandle": {
"type": "string"
},
"publicKey": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"keyHandle",
"publicKey",
"type"
]
},
"SerializedAddUserAction": {
"type": "object",
"properties": {
@@ -535,6 +561,21 @@ const definitions = {
"type"
]
},
"SerializedReportU2fLoginAction": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"REPORT_U2F_LOGIN"
]
}
},
"additionalProperties": false,
"required": [
"type"
]
},
"SerializedRemoveCategoryAppsAction": {
"type": "object",
"properties": {
@@ -561,6 +602,29 @@ const definitions = {
"type"
]
},
"SerializedRemoveParentU2fKeyAction": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"REMOVE_PARENT_U2F"
]
},
"keyHandle": {
"type": "string"
},
"publicKey": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"keyHandle",
"publicKey",
"type"
]
},
"SerializedRemoveUserAction": {
"type": "object",
"properties": {
@@ -2747,6 +2811,49 @@ const definitions = {
"k",
"v"
]
},
"U2fData": {
"type": "object",
"properties": {
"v": {
"type": "string"
},
"d": {
"type": "array",
"items": {
"$ref": "#/definitions/U2fItem"
}
}
},
"additionalProperties": false,
"required": [
"d",
"v"
]
},
"U2fItem": {
"type": "object",
"properties": {
"u": {
"type": "string"
},
"a": {
"type": "number"
},
"h": {
"type": "string"
},
"p": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"a",
"h",
"p",
"u"
]
}
}
@@ -2907,6 +3014,9 @@ export const isSerializedParentAction: (value: unknown) => value is SerializedPa
{
"$ref": "#/definitions/SerializedAddCategoryNetworkIdAction"
},
{
"$ref": "#/definitions/SerializedAddParentU2fKeyAction"
},
{
"$ref": "#/definitions/SerializedAddUserAction"
},
@@ -2934,9 +3044,15 @@ export const isSerializedParentAction: (value: unknown) => value is SerializedPa
{
"$ref": "#/definitions/SerializedIncrementCategoryExtraTimeAction"
},
{
"$ref": "#/definitions/SerializedReportU2fLoginAction"
},
{
"$ref": "#/definitions/SerializedRemoveCategoryAppsAction"
},
{
"$ref": "#/definitions/SerializedRemoveParentU2fKeyAction"
},
{
"$ref": "#/definitions/SerializedRemoveUserAction"
},