Skip to content

Commit

Permalink
675: Add code type to card verification
Browse files Browse the repository at this point in the history
  • Loading branch information
sarahsporck committed Jan 26, 2023
1 parent e322b38 commit fc6bb19
Show file tree
Hide file tree
Showing 8 changed files with 54 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -13,6 +14,13 @@ class CardQueryService {
fun verifyCardInProject(project: String, card: CardVerificationModel, dfe: DataFetchingEnvironment): Boolean {
val context = dfe.getContext<GraphQLContext>()
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
}
}
Original file line number Diff line number Diff line change
@@ -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
)
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ Future<bool> 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<bool> queryStaticServerVerification(
Expand All @@ -18,15 +24,30 @@ Future<bool> 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<bool> _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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<CardInfo?> 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<CardInfo?> processDynamicVerifyCode(GraphQLClient client, String projectId, DynamicVerifyCode code) async {
Future<CardInfo?> verifyDynamicVerifyCode(GraphQLClient client, String projectId, DynamicVerifyCode code) async {
assertConsistentCardInfo(code.info);
_assertConsistentDynamicVerifyCode(code);
if (!(await queryDynamicServerVerification(client, projectId, code))) {
Expand All @@ -35,7 +33,7 @@ Future<CardInfo?> processDynamicVerifyCode(GraphQLClient client, String projectI
return code.info;
}

Future<CardInfo?> processStaticVerifyCode(GraphQLClient client, String projectId, StaticVerifyCode code) async {
Future<CardInfo?> verifyStaticVerifyCode(GraphQLClient client, String projectId, StaticVerifyCode code) async {
assertConsistentCardInfo(code.info);
_assertConsistentStaticVerifyCode(code);
if (!(await queryStaticServerVerification(client, projectId, code))) {
Expand Down Expand Up @@ -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");
Expand All @@ -72,7 +70,7 @@ void _assertConsistentDynamicVerifyCode(DynamicVerifyCode verifyCode) {

void _assertConsistentStaticVerifyCode(StaticVerifyCode verifyCode) {
if (!verifyCode.hasPepper()) {
throw QrCodeFieldMissingException("hashSecretBase64");
throw QrCodeFieldMissingException("pepper");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions specs/backend-api.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ input CardGenerationModelInput {

input CardVerificationModelInput {
cardInfoHashBase64: String!
codeType: CodeType!
totp: Int
}

Expand Down

0 comments on commit fc6bb19

Please sign in to comment.