From fc6bb19db135bbe4610d2f5b423d0f986a25f1a8 Mon Sep 17 00:00:00 2001 From: Sarah Sporck Date: Tue, 24 Jan 2023 17:06:28 +0100 Subject: [PATCH] 675: Add code type to card verification --- .../verification/service/CardVerifier.kt | 10 ++----- .../webservice/schema/CardQueryService.kt | 10 ++++++- .../schema/types/CardVerificationModel.kt | 5 +++- .../activation_code_parser.dart | 2 +- .../query_server_verification.dart | 29 ++++++++++++++++--- .../verification_qr_code_processor.dart | 16 +++++----- .../verification_qr_scanner_page.dart | 5 +++- specs/backend-api.graphql | 1 + 8 files changed, 54 insertions(+), 24 deletions(-) diff --git a/backend/src/main/kotlin/app/ehrenamtskarte/backend/verification/service/CardVerifier.kt b/backend/src/main/kotlin/app/ehrenamtskarte/backend/verification/service/CardVerifier.kt index d30b4d487..f4925e259 100644 --- a/backend/src/main/kotlin/app/ehrenamtskarte/backend/verification/service/CardVerifier.kt +++ b/backend/src/main/kotlin/app/ehrenamtskarte/backend/verification/service/CardVerifier.kt @@ -2,7 +2,6 @@ package app.ehrenamtskarte.backend.verification.service import app.ehrenamtskarte.backend.verification.ValidityPeriodUtil.Companion.daysSinceEpochToDate import app.ehrenamtskarte.backend.verification.ValidityPeriodUtil.Companion.isOnOrBeforeToday -import app.ehrenamtskarte.backend.verification.database.CardEntity import app.ehrenamtskarte.backend.verification.database.repos.CardRepository import com.eatthepath.otp.TimeBasedOneTimePasswordGenerator import org.jetbrains.exposed.sql.transactions.transaction @@ -15,17 +14,14 @@ val TIME_STEP: Duration = Duration.ofSeconds(30) const val TOTP_LENGTH = 6 object CardVerifier { - fun verifyCardHash(project: String, cardHash: ByteArray, totp: Int?, timezone: ZoneId): Boolean { + public fun verifyStaticCard(project: String, cardHash: ByteArray, timezone: ZoneId): Boolean { val card = transaction { CardRepository.findByHashModel(project, cardHash) } ?: return false - return if (totp != null) verifyDynamicCard(card, totp, timezone) else verifyStaticCard(card, timezone) - } - - private fun verifyStaticCard(card: CardEntity, timezone: ZoneId): Boolean { return !isExpired(card.expirationDay, timezone) && !card.revoked } - private fun verifyDynamicCard(card: CardEntity, totp: Int, timezone: ZoneId): Boolean { + public fun verifyDynamicCard(project: String, cardHash: ByteArray, totp: Int, timezone: ZoneId): Boolean { + val card = transaction { CardRepository.findByHashModel(project, cardHash) } ?: return false return !isExpired(card.expirationDay, timezone) && !card.revoked && isTotpValid(totp, card.totpSecret) diff --git a/backend/src/main/kotlin/app/ehrenamtskarte/backend/verification/webservice/schema/CardQueryService.kt b/backend/src/main/kotlin/app/ehrenamtskarte/backend/verification/webservice/schema/CardQueryService.kt index fdb1cbee0..b2979edfe 100644 --- a/backend/src/main/kotlin/app/ehrenamtskarte/backend/verification/webservice/schema/CardQueryService.kt +++ b/backend/src/main/kotlin/app/ehrenamtskarte/backend/verification/webservice/schema/CardQueryService.kt @@ -1,6 +1,7 @@ package app.ehrenamtskarte.backend.verification.webservice.schema import app.ehrenamtskarte.backend.common.webservice.GraphQLContext +import app.ehrenamtskarte.backend.verification.database.CodeType import app.ehrenamtskarte.backend.verification.service.CardVerifier import app.ehrenamtskarte.backend.verification.webservice.schema.types.CardVerificationModel import com.expediagroup.graphql.generator.annotations.GraphQLDescription @@ -13,6 +14,13 @@ class CardQueryService { fun verifyCardInProject(project: String, card: CardVerificationModel, dfe: DataFetchingEnvironment): Boolean { val context = dfe.getContext() val projectConfig = context.backendConfiguration.projects.find { it.id == project } ?: throw NullPointerException("Project not found") - return CardVerifier.verifyCardHash(project, Base64.getDecoder().decode(card.cardInfoHashBase64), card.totp, projectConfig.timezone) + val cardHash = Base64.getDecoder().decode(card.cardInfoHashBase64) + + if (card.codeType == CodeType.static) { + return card.totp == null && CardVerifier.verifyStaticCard(project, cardHash, projectConfig.timezone) + } else if (card.codeType == CodeType.dynamic) { + return card.totp != null && CardVerifier.verifyDynamicCard(project, cardHash, card.totp, projectConfig.timezone) + } + return false } } diff --git a/backend/src/main/kotlin/app/ehrenamtskarte/backend/verification/webservice/schema/types/CardVerificationModel.kt b/backend/src/main/kotlin/app/ehrenamtskarte/backend/verification/webservice/schema/types/CardVerificationModel.kt index 5fe2469bd..2ae42dd08 100644 --- a/backend/src/main/kotlin/app/ehrenamtskarte/backend/verification/webservice/schema/types/CardVerificationModel.kt +++ b/backend/src/main/kotlin/app/ehrenamtskarte/backend/verification/webservice/schema/types/CardVerificationModel.kt @@ -1,6 +1,9 @@ package app.ehrenamtskarte.backend.verification.webservice.schema.types +import app.ehrenamtskarte.backend.verification.database.CodeType + data class CardVerificationModel( val cardInfoHashBase64: String, - val totp: Int? + val totp: Int?, + val codeType: CodeType ) diff --git a/frontend/lib/identification/activation_workflow/activation_code_parser.dart b/frontend/lib/identification/activation_workflow/activation_code_parser.dart index 6b917353c..2ef1ae6a2 100644 --- a/frontend/lib/identification/activation_workflow/activation_code_parser.dart +++ b/frontend/lib/identification/activation_workflow/activation_code_parser.dart @@ -39,7 +39,7 @@ class ActivationCodeParser { void _assertConsistentDynamicActivationCode(DynamicActivationCode code) { if (!code.hasPepper()) { - throw QrCodeFieldMissingException("hashSecretBase64"); + throw QrCodeFieldMissingException("pepper"); } if (!code.hasTotpSecret()) { throw QrCodeFieldMissingException("totpSecret"); diff --git a/frontend/lib/identification/verification_workflow/query_server_verification.dart b/frontend/lib/identification/verification_workflow/query_server_verification.dart index d3fd2c1b4..48fbb59b8 100644 --- a/frontend/lib/identification/verification_workflow/query_server_verification.dart +++ b/frontend/lib/identification/verification_workflow/query_server_verification.dart @@ -9,7 +9,13 @@ Future queryDynamicServerVerification( DynamicVerifyCode verifyCode, ) async { final hash = const QrCodeUtils().hashCardInfo(verifyCode.info, verifyCode.pepper); - return _queryServerVerification(client, projectId, hash, verifyCode.otp); + return _queryServerVerification( + client, + projectId, + hash, + verifyCode.otp, + CodeType.kw$dynamic, + ); } Future queryStaticServerVerification( @@ -18,15 +24,30 @@ Future queryStaticServerVerification( StaticVerifyCode verifyCode, ) async { final hash = const QrCodeUtils().hashCardInfo(verifyCode.info, verifyCode.pepper); - return _queryServerVerification(client, projectId, hash, null); + return _queryServerVerification( + client, + projectId, + hash, + null, + CodeType.kw$static, + ); } Future _queryServerVerification( - GraphQLClient client, String projectId, String verificationHash, int? totp) async { + GraphQLClient client, + String projectId, + String verificationHash, + int? totp, + CodeType codeType, +) async { final byCardDetailsHash = CardVerificationByHashQuery( variables: CardVerificationByHashArguments( project: projectId, - card: CardVerificationModelInput(cardInfoHashBase64: verificationHash, totp: totp), + card: CardVerificationModelInput( + cardInfoHashBase64: verificationHash, + totp: totp, + codeType: codeType, + ), ), ); final queryOptions = QueryOptions( diff --git a/frontend/lib/identification/verification_workflow/verification_qr_code_processor.dart b/frontend/lib/identification/verification_workflow/verification_qr_code_processor.dart index be5cad4f7..8a55e7ae2 100644 --- a/frontend/lib/identification/verification_workflow/verification_qr_code_processor.dart +++ b/frontend/lib/identification/verification_workflow/verification_qr_code_processor.dart @@ -2,31 +2,29 @@ import 'package:ehrenamtskarte/configuration/configuration.dart'; import 'package:ehrenamtskarte/identification/activation_workflow/activation_code_parser.dart'; import 'package:ehrenamtskarte/identification/qr_code_scanner/qr_code_processor.dart'; import 'package:ehrenamtskarte/identification/verification_workflow/query_server_verification.dart'; -import 'package:ehrenamtskarte/identification/verification_workflow/verification_qr_content_parser.dart'; import 'package:ehrenamtskarte/proto/card.pb.dart'; import 'package:flutter/cupertino.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; Future verifyQrCodeContent( BuildContext context, - String rawBase64Content, + QrCode qrcode, ) async { final client = GraphQLProvider.of(context).value; final projectId = Configuration.of(context).projectId; - final qrcode = rawBase64Content.parseQRCodeContent(); if (qrcode.hasDynamicVerifyCode()) { final verifyCode = qrcode.dynamicVerifyCode; - return processDynamicVerifyCode(client, projectId, verifyCode); + return verifyDynamicVerifyCode(client, projectId, verifyCode); } else if (qrcode.hasStaticVerifyCode()) { final verifyCode = qrcode.staticVerifyCode; - return processStaticVerifyCode(client, projectId, verifyCode); + return verifyStaticVerifyCode(client, projectId, verifyCode); } else { throw QrCodeWrongTypeException(); } } -Future processDynamicVerifyCode(GraphQLClient client, String projectId, DynamicVerifyCode code) async { +Future verifyDynamicVerifyCode(GraphQLClient client, String projectId, DynamicVerifyCode code) async { assertConsistentCardInfo(code.info); _assertConsistentDynamicVerifyCode(code); if (!(await queryDynamicServerVerification(client, projectId, code))) { @@ -35,7 +33,7 @@ Future processDynamicVerifyCode(GraphQLClient client, String projectI return code.info; } -Future processStaticVerifyCode(GraphQLClient client, String projectId, StaticVerifyCode code) async { +Future verifyStaticVerifyCode(GraphQLClient client, String projectId, StaticVerifyCode code) async { assertConsistentCardInfo(code.info); _assertConsistentStaticVerifyCode(code); if (!(await queryStaticServerVerification(client, projectId, code))) { @@ -63,7 +61,7 @@ void assertConsistentCardInfo(CardInfo cardInfo) { void _assertConsistentDynamicVerifyCode(DynamicVerifyCode verifyCode) { if (!verifyCode.hasPepper()) { - throw QrCodeFieldMissingException("hashSecretBase64"); + throw QrCodeFieldMissingException("pepper"); } if (verifyCode.otp <= 0) { throw QrCodeFieldMissingException("otp"); @@ -72,7 +70,7 @@ void _assertConsistentDynamicVerifyCode(DynamicVerifyCode verifyCode) { void _assertConsistentStaticVerifyCode(StaticVerifyCode verifyCode) { if (!verifyCode.hasPepper()) { - throw QrCodeFieldMissingException("hashSecretBase64"); + throw QrCodeFieldMissingException("pepper"); } } diff --git a/frontend/lib/identification/verification_workflow/verification_qr_scanner_page.dart b/frontend/lib/identification/verification_workflow/verification_qr_scanner_page.dart index 220ff5149..738d049c0 100644 --- a/frontend/lib/identification/verification_workflow/verification_qr_scanner_page.dart +++ b/frontend/lib/identification/verification_workflow/verification_qr_scanner_page.dart @@ -13,6 +13,7 @@ import 'package:ehrenamtskarte/identification/verification_workflow/dialogs/posi import 'package:ehrenamtskarte/identification/verification_workflow/dialogs/verification_info_dialog.dart'; import 'package:ehrenamtskarte/identification/verification_workflow/query_server_verification.dart'; import 'package:ehrenamtskarte/identification/verification_workflow/verification_qr_code_processor.dart'; +import 'package:ehrenamtskarte/identification/verification_workflow/verification_qr_content_parser.dart'; import 'package:ehrenamtskarte/proto/card.pb.dart'; import 'package:ehrenamtskarte/widgets/app_bars.dart' show CustomAppBar; import 'package:flutter/material.dart'; @@ -71,7 +72,9 @@ class VerificationQrScannerPage extends StatelessWidget { _openWaitingDialog(context); try { - final cardInfo = await verifyQrCodeContent(context, rawQrContent); + final qrcode = rawQrContent.parseQRCodeContent(); + + final cardInfo = await verifyQrCodeContent(context, qrcode); if (cardInfo == null) { await _onError( context, diff --git a/specs/backend-api.graphql b/specs/backend-api.graphql index e664e9d3d..545535960 100644 --- a/specs/backend-api.graphql +++ b/specs/backend-api.graphql @@ -271,6 +271,7 @@ input CardGenerationModelInput { input CardVerificationModelInput { cardInfoHashBase64: String! + codeType: CodeType! totp: Int }