From 9b0850a5f17279d355a30e384e3b47ce75c7abcd Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Sat, 3 Apr 2021 11:27:32 +0200 Subject: [PATCH 01/43] Use config regex --- .../appconfig/PresenceTracingConfig.kt | 2 + .../PresenceTracingConfigContainer.kt | 4 +- .../mapping/PresenceTracingConfigMapper.kt | 15 ++-- .../checkins/qrcode/QRCodeUriParser.kt | 69 ++++++++++--------- .../checkins/qrcode/QRCodeUriParserTest.kt | 28 +++++++- 5 files changed, 74 insertions(+), 44 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/PresenceTracingConfig.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/PresenceTracingConfig.kt index 7b73dbd495e..699e95b6fd7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/PresenceTracingConfig.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/PresenceTracingConfig.kt @@ -2,6 +2,7 @@ package de.rki.coronawarnapp.appconfig import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel import de.rki.coronawarnapp.appconfig.mapping.ConfigMapper +import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptorOrBuilder interface PresenceTracingConfig { val qrCodeErrorCorrectionLevel: ErrorCorrectionLevel @@ -9,6 +10,7 @@ interface PresenceTracingConfig { val riskCalculationParameters: PresenceTracingRiskCalculationParamContainer val submissionParameters: PresenceTracingSubmissionParamContainer val plausibleDeniabilityParameters: PlausibleDeniabilityParametersContainer + val qrCodeDescriptors: List interface Mapper : ConfigMapper } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/PresenceTracingConfigContainer.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/PresenceTracingConfigContainer.kt index 91e17852764..5957fcdd910 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/PresenceTracingConfigContainer.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/PresenceTracingConfigContainer.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.appconfig import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel +import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptorOrBuilder data class PresenceTracingConfigContainer( override val qrCodeErrorCorrectionLevel: ErrorCorrectionLevel = ErrorCorrectionLevel.H, @@ -10,5 +11,6 @@ data class PresenceTracingConfigContainer( override val submissionParameters: PresenceTracingSubmissionParamContainer = PresenceTracingSubmissionParamContainer(), override val plausibleDeniabilityParameters: PlausibleDeniabilityParametersContainer = - PlausibleDeniabilityParametersContainer() + PlausibleDeniabilityParametersContainer(), + override val qrCodeDescriptors: List = emptyList() ) : PresenceTracingConfig diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/PresenceTracingConfigMapper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/PresenceTracingConfigMapper.kt index bdba0734eca..739a0e6fd00 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/PresenceTracingConfigMapper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/PresenceTracingConfigMapper.kt @@ -8,14 +8,10 @@ import de.rki.coronawarnapp.appconfig.PresenceTracingConfigContainer import de.rki.coronawarnapp.appconfig.PresenceTracingRiskCalculationParamContainer import de.rki.coronawarnapp.appconfig.PresenceTracingSubmissionParamContainer import de.rki.coronawarnapp.server.protocols.internal.v2.AppConfigAndroid -import de.rki.coronawarnapp.server.protocols.internal.v2 - .PresenceTracingParametersOuterClass.PresenceTracingSubmissionParameters -import de.rki.coronawarnapp.server.protocols.internal.v2 - .PresenceTracingParametersOuterClass.PresenceTracingRiskCalculationParameters -import de.rki.coronawarnapp.server.protocols.internal.v2 - .PresenceTracingParametersOuterClass.PresenceTracingParameters.QRCodeErrorCorrectionLevel -import de.rki.coronawarnapp.server.protocols.internal.v2 - .PresenceTracingParametersOuterClass.PresenceTracingPlausibleDeniabilityParameters +import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingSubmissionParameters +import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingRiskCalculationParameters +import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingParameters.QRCodeErrorCorrectionLevel +import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingPlausibleDeniabilityParameters import timber.log.Timber import javax.inject.Inject @@ -95,7 +91,8 @@ class PresenceTracingConfigMapper @Inject constructor() : PresenceTracingConfig. revokedTraceLocationVersions = revokedTraceLocationVersionsList.orEmpty(), riskCalculationParameters = riskCalculationParameters, submissionParameters = submissionParameters, - plausibleDeniabilityParameters = plausibleDeniabilityParameters + plausibleDeniabilityParameters = plausibleDeniabilityParameters, + qrCodeDescriptors = qrCodeDescriptorsOrBuilderList ) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt index 7d622c3425e..48f54ac988d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt @@ -1,46 +1,53 @@ package de.rki.coronawarnapp.eventregistration.checkins.qrcode import dagger.Reusable +import de.rki.coronawarnapp.appconfig.AppConfigProvider +import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptor.PayloadEncoding import de.rki.coronawarnapp.util.decodeBase32 import okio.ByteString +import okio.ByteString.Companion.decodeBase64 import timber.log.Timber import java.net.URI import javax.inject.Inject @Reusable -class QRCodeUriParser @Inject constructor() { - - /** - * Validate that QRCode scanned uri matches the following formulas: - * https://e.coronawarn.app/c1/SIGNED_TRACE_LOCATION_BASE32 - * HTTPS://E.CORONAWARN.APP/C1/SIGNED_TRACE_LOCATION_BASE32 - */ - fun getQrCodePayload(maybeUri: String): ByteString? = URI.create(maybeUri).run { - if (!scheme.equals(SCHEME, true)) return@run null - if (!authority.equals(AUTHORITY, true)) return@run null - - if (!path.substringBeforeLast("/").equals(PATH_PREFIX, true)) return@run null - - val rawData = path.substringAfterLast("/") - val paddingDiff = 8 - (rawData.length % 8) - val maybeBase32 = rawData + createPadding(paddingDiff) - - if (!maybeBase32.matches(BASE32_REGEX)) return@run null - - return@run try { - maybeBase32.decodeBase32() - } catch (e: Exception) { - Timber.w(e, "Data wasn't base32: %s", maybeBase32) - null +class QRCodeUriParser @Inject constructor( + private val appConfigProvider: AppConfigProvider +) { + + suspend fun getQrCodePayload(input: String): ByteString? { + Timber.d("input=$input") + URI.create(input) // Just to verify it is a valid url + + val descriptors = appConfigProvider.getAppConfig().presenceTracing.qrCodeDescriptors + val descriptor = descriptors.find { it.regexPattern.toRegex(RegexOption.IGNORE_CASE).matches(input) } + if (descriptor == null) { + Timber.d("Invalid URI - no matchedDescriptor") + throw IllegalArgumentException("Invalid URI - no matchedDescriptor") } - } + Timber.d("descriptor=$descriptor") + + val payloadGroupIndex = descriptor.encodedPayloadGroupIndex + val groups = descriptor.regexPattern + .toRegex(RegexOption.IGNORE_CASE).find(input) // Find matched result [MatchResult] + ?.destructured?.toList().orEmpty() // Destructured groups - excluding the zeroth group (Whole String) + Timber.d("groups=$groups") + + if (payloadGroupIndex !in groups.indices) { + Timber.d("Invalid payload - group index is out of bounds") + throw IllegalArgumentException("Invalid payload - group index is out of bounds") + } + + val payload = groups[payloadGroupIndex] + Timber.d("payload=$payload") - companion object { - private fun createPadding(length: Int) = (0 until length).joinToString(separator = "") { "=" } + val encoding = PayloadEncoding.forNumber(descriptor.payloadEncoding.number) + Timber.d("encoding=$encoding") - private const val SCHEME = "https" - private const val AUTHORITY = "e.coronawarn.app" - private const val PATH_PREFIX = "/c1" - private val BASE32_REGEX = "^([A-Z2-7=]{8})+$".toRegex(RegexOption.IGNORE_CASE) + return when (encoding) { + PayloadEncoding.BASE32 -> payload.decodeBase32() + PayloadEncoding.BASE64 -> payload.decodeBase64() + else -> null + } } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt index 71a1c8722df..aab3a7215f7 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt @@ -1,24 +1,46 @@ package de.rki.coronawarnapp.eventregistration.checkins.qrcode +import de.rki.coronawarnapp.appconfig.AppConfigProvider +import de.rki.coronawarnapp.appconfig.ConfigData +import de.rki.coronawarnapp.appconfig.PresenceTracingConfigContainer import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe +import io.mockk.MockKAnnotations +import io.mockk.coEvery +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.test.runBlockingTest +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ArgumentsSource import testhelpers.BaseTest class QRCodeUriParserTest : BaseTest() { - fun createInstance() = QRCodeUriParser() + @MockK lateinit var appConfigProvider: AppConfigProvider + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + coEvery { appConfigProvider.getAppConfig() } returns mockk().apply { + every { presenceTracing } returns PresenceTracingConfigContainer( + qrCodeDescriptors = listOf() + ) + } + } + + fun createInstance() = QRCodeUriParser(appConfigProvider) @ParameterizedTest @ArgumentsSource(ValidUrlProvider::class) - fun `Valid URLs`(input: String) { + fun `Valid URLs`(input: String) = runBlockingTest { createInstance().getQrCodePayload(input) shouldNotBe null } @ParameterizedTest @ArgumentsSource(InvalidUrlProvider::class) - fun `Invalid URLs`(input: String) { + fun `Invalid URLs`(input: String) = runBlockingTest { createInstance().getQrCodePayload(input) shouldBe null } } From a0ee98d9f18b25627693ed42f5bf4829f72e0b46 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Sat, 3 Apr 2021 11:43:41 +0200 Subject: [PATCH 02/43] Refactoring --- .../checkins/qrcode/QRCodeUriParser.kt | 50 ++++++++++++------- .../attendee/checkins/CheckInsViewModel.kt | 12 +---- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt index 48f54ac988d..9c0c8386211 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt @@ -2,9 +2,10 @@ package de.rki.coronawarnapp.eventregistration.checkins.qrcode import dagger.Reusable import de.rki.coronawarnapp.appconfig.AppConfigProvider +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.QRCodePayload +import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptorOrBuilder import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptor.PayloadEncoding import de.rki.coronawarnapp.util.decodeBase32 -import okio.ByteString import okio.ByteString.Companion.decodeBase64 import timber.log.Timber import java.net.URI @@ -15,10 +16,30 @@ class QRCodeUriParser @Inject constructor( private val appConfigProvider: AppConfigProvider ) { - suspend fun getQrCodePayload(input: String): ByteString? { + @Suppress("BlockingMethodInNonBlockingContext") + suspend fun getQrCodePayload(input: String): QRCodePayload { Timber.d("input=$input") - URI.create(input) // Just to verify it is a valid url + URI.create(input) // Verify it is a valid uri + val descriptor = descriptor(input) + val groups = descriptor.matchedGroups(input) + + val payload = groups[descriptor.encodedPayloadGroupIndex] + Timber.d("payload=$payload") + + val encoding = PayloadEncoding.forNumber(descriptor.payloadEncoding.number) + Timber.d("encoding=$encoding") + + val rawPayload = when (encoding) { + PayloadEncoding.BASE32 -> payload.decodeBase32() + PayloadEncoding.BASE64 -> payload.decodeBase64() + else -> null + } ?: throw IllegalArgumentException("Payload decoding failed") + + return QRCodePayload.parseFrom(rawPayload.toByteArray()) + } + + private suspend fun descriptor(input: String): PresenceTracingQRCodeDescriptorOrBuilder { val descriptors = appConfigProvider.getAppConfig().presenceTracing.qrCodeDescriptors val descriptor = descriptors.find { it.regexPattern.toRegex(RegexOption.IGNORE_CASE).matches(input) } if (descriptor == null) { @@ -26,28 +47,21 @@ class QRCodeUriParser @Inject constructor( throw IllegalArgumentException("Invalid URI - no matchedDescriptor") } Timber.d("descriptor=$descriptor") + return descriptor + } - val payloadGroupIndex = descriptor.encodedPayloadGroupIndex - val groups = descriptor.regexPattern + private fun PresenceTracingQRCodeDescriptorOrBuilder.matchedGroups( + input: String + ): List { + val groups = regexPattern .toRegex(RegexOption.IGNORE_CASE).find(input) // Find matched result [MatchResult] ?.destructured?.toList().orEmpty() // Destructured groups - excluding the zeroth group (Whole String) Timber.d("groups=$groups") - if (payloadGroupIndex !in groups.indices) { + if (encodedPayloadGroupIndex !in groups.indices) { Timber.d("Invalid payload - group index is out of bounds") throw IllegalArgumentException("Invalid payload - group index is out of bounds") } - - val payload = groups[payloadGroupIndex] - Timber.d("payload=$payload") - - val encoding = PayloadEncoding.forNumber(descriptor.payloadEncoding.number) - Timber.d("encoding=$encoding") - - return when (encoding) { - PayloadEncoding.BASE32 -> payload.decodeBase32() - PayloadEncoding.BASE64 -> payload.decodeBase64() - else -> null - } + return groups } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsViewModel.kt index dbfafa23aac..e78ae163bec 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsViewModel.kt @@ -8,13 +8,11 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import de.rki.coronawarnapp.eventregistration.checkins.CheckIn import de.rki.coronawarnapp.eventregistration.checkins.CheckInRepository -import de.rki.coronawarnapp.eventregistration.checkins.qrcode.InvalidQRCodeDataException import de.rki.coronawarnapp.eventregistration.checkins.qrcode.QRCodeUriParser import de.rki.coronawarnapp.eventregistration.checkins.qrcode.VerifiedTraceLocation import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.presencetracing.checkins.checkout.CheckOutHandler -import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass import de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.items.ActiveCheckInVH import de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.items.CameraPermissionVH import de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.items.CheckInsItem @@ -138,15 +136,7 @@ class CheckInsViewModel @AssistedInject constructor( private fun verifyUri(uri: String) = launch { try { Timber.i("uri: $uri") - val qrCodePayloadRaw = qrCodeUriParser.getQrCodePayload(uri)?.toByteArray() - ?: throw IllegalArgumentException("Invalid uri: $uri") - - val qrCodePayload = try { - TraceLocationOuterClass.QRCodePayload.parseFrom(qrCodePayloadRaw) - } catch (e: Exception) { - throw InvalidQRCodeDataException(cause = e, message = "QR-code data could not be parsed.") - } - + val qrCodePayload = qrCodeUriParser.getQrCodePayload(uri) val verifiedTraceLocation = VerifiedTraceLocation(qrCodePayload) events.postValue(CheckInEvent.ConfirmCheckIn(verifiedTraceLocation)) } catch (e: Exception) { From 7a7c61d3da61b35f5b81ec10063769e09e656ff9 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Sat, 3 Apr 2021 12:56:16 +0200 Subject: [PATCH 03/43] Verify functionality --- .../mapping/PresenceTracingConfigMapper.kt | 10 +++++- .../checkins/qrcode/QRCodeUriParser.kt | 1 + .../attendee/checkins/CheckInsFragment.kt | 35 +++++++------------ .../checkins/qrcode/QRCodeUriParserTest.kt | 17 +++++++-- .../checkins/qrcode/ValidUrlProvider.kt | 33 ++++++----------- .../checkins/CheckInsViewModelTest.kt | 6 ++-- 6 files changed, 51 insertions(+), 51 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/PresenceTracingConfigMapper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/PresenceTracingConfigMapper.kt index 739a0e6fd00..be8bc073886 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/PresenceTracingConfigMapper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/PresenceTracingConfigMapper.kt @@ -8,6 +8,7 @@ import de.rki.coronawarnapp.appconfig.PresenceTracingConfigContainer import de.rki.coronawarnapp.appconfig.PresenceTracingRiskCalculationParamContainer import de.rki.coronawarnapp.appconfig.PresenceTracingSubmissionParamContainer import de.rki.coronawarnapp.server.protocols.internal.v2.AppConfigAndroid +import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingSubmissionParameters import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingRiskCalculationParameters import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingParameters.QRCodeErrorCorrectionLevel @@ -92,7 +93,14 @@ class PresenceTracingConfigMapper @Inject constructor() : PresenceTracingConfig. riskCalculationParameters = riskCalculationParameters, submissionParameters = submissionParameters, plausibleDeniabilityParameters = plausibleDeniabilityParameters, - qrCodeDescriptors = qrCodeDescriptorsOrBuilderList + qrCodeDescriptors = qrCodeDescriptorsOrBuilderList + + // TODO remove + PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptor.newBuilder() + .setEncodedPayloadGroupIndex(1) + .setPayloadEncoding(PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptor.PayloadEncoding.BASE64) + .setRegexPattern("https://e\\.coronawarn\\.app\\?v=(\\d+)\\#(.+)") + .setVersionGroupIndex(0) + .build() ) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt index 9c0c8386211..a5faf64f24c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt @@ -41,6 +41,7 @@ class QRCodeUriParser @Inject constructor( private suspend fun descriptor(input: String): PresenceTracingQRCodeDescriptorOrBuilder { val descriptors = appConfigProvider.getAppConfig().presenceTracing.qrCodeDescriptors + Timber.d("descriptors=$descriptors") val descriptor = descriptors.find { it.regexPattern.toRegex(RegexOption.IGNORE_CASE).matches(input) } if (descriptor == null) { Timber.d("Invalid URI - no matchedDescriptor") diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsFragment.kt index eaa18007395..825f9717e60 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsFragment.kt @@ -12,7 +12,6 @@ import androidx.appcompat.widget.Toolbar import androidx.core.net.toUri import androidx.core.view.isGone import androidx.fragment.app.Fragment -import androidx.navigation.NavOptions import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs @@ -38,6 +37,9 @@ import de.rki.coronawarnapp.util.ui.viewBindingLazy import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider import de.rki.coronawarnapp.util.viewmodel.cwaViewModelsAssisted import timber.log.Timber +import java.lang.Exception +import java.lang.reflect.Executable +import java.net.URLEncoder import javax.inject.Inject class CheckInsFragment : Fragment(R.layout.trace_location_attendee_checkins_fragment), AutoInject { @@ -149,18 +151,6 @@ class CheckInsFragment : Fragment(R.layout.trace_location_attendee_checkins_frag FragmentNavigatorExtras(this to transitionName) ) } - // TODO Remove once feature is done - if (CWADebug.isDeviceForTestersBuild) { - setOnLongClickListener { - findNavController().navigate( - createCheckInUri(DEBUG_CHECKINS.random()), - NavOptions.Builder().apply { - setLaunchSingleTop(true) - }.build() - ) - true - } - } } } @@ -224,15 +214,14 @@ class CheckInsFragment : Fragment(R.layout.trace_location_attendee_checkins_frag } companion object { - fun createCheckInUri(rootUri: String): Uri = "coronawarnapp://check-ins/$rootUri".toUri() - - @Suppress("MaxLineLength") - private val DEBUG_CHECKINS = listOf( - "HTTPS://E.CORONAWARN.APP/C1/BJHAUJDFMNSTKMJYGY3S2NJYHA4S2NBRG5QS2YLGMM3C2ZDDHFRTSNRSGZTGIYZWCAARQAJCBVEWGZLDOJSWC3JAKNUG64BKBVGWC2LOEBJXI4TFMV2CAMJQAA4AAQAKCJDTARICEBFRIDICXSP4QTNMBRDF7EOJ3EIJD6AWT24YDOWWXQI22KCUD7R7WARBAC7ONBRPJDB2KK6QKZLF4RE3PXU7PMON4IOZVIHCYPJGBZ27FF5S4", - "HTTPS://E.CORONAWARN.APP/C1/BJHAUJDEMVRDGZTGMU2C2MZUGQ2C2NBWGZQS2YLCHEYC2NJQHBRDCMBRMVTDIZBTCAARQAJCBVEWGZLDOJSWC3JAKNUG64BKBVGWC2LOEBJXI4TFMV2CAMJQAA4AAQAKCJDTARICEEAJRWAYJARF3V4AS5OVBODPLPX2V3IJFMFU4O2CAKRH6HGHHWCDMJYCEBH7BO2IU2EEGRKEXBZT2DAOFIMXES5ETUT45QIWDCX64APY7C2ME", - "HTTPS://E.CORONAWARN.APP/C1/BJHAUJDBMNQWIMLFHA3S2NZQGVTC2NDDGY3C2ODGGBTC2ZBWGQYDCZJUMRTDEN3FCAARQAJCBVEWGZLDOJSWC3JAKNUG64BKBVGWC2LOEBJXI4TFMV2CAMJQAA4AAQAKCJDTARICEEAKJM3RPYMM2VVCE2GLVK6OKY36F64FRNSI6DWYV7WW6MGESFCDNNQCEA44UHS2GEWHJYHTIJ3AJYM6BC3HEIYHY2HRMPIP7ZF62YBAUKOIY", - "HTTPS://E.CORONAWARN.APP/C1/BJHAUJBQGZRDOMJXHEYC2NBRG44C2NBWGZRC2YRQGA4S2MJTGRRDQOJZMU4DMMRQCAARQAJCBVEWGZLDOJSWC3JAKNUG64BKBVGWC2LOEBJXI4TFMV2CAMJQAA4AAQAKCJDTARICEB365TX5SEWICC3JUOAZCQX5YUK2LZZA7RGRTNBXTSEBXTD2766CAARBADXEYUJHQSE7QRQOIPEMSSPLCVC5D4I3FOBDRX64NASE47XKKK5EY", - "HTTPS://E.CORONAWARN.APP/C1/BJHAUJDGGE4WKMTEMQZC2OJRMUYC2NBQGNTC2OJZMZRC2MTEG4ZWGMJTGA3GEOBTCAARQAJCBVEWGZLDOJSWC3JAKNUG64BKBVGWC2LOEBJXI4TFMV2CAMJQAA4AAQAKCJDTARICEEANT4HDNB7V5DWCKKUV22YQ7NYOBCTOZ2QUFBOUDZS6V5J2VRVLVSICEBU2YHAEBPQSLWTR75VFC6OEFIS22V6KU4NRDYZHTIBMHS4FDADG6", - ) + fun createCheckInUri(rootUri: String): Uri { + val encodedUrl = try { + URLEncoder.encode(rootUri, Charsets.UTF_8.name()) + } catch (e: Exception) { + Timber.d(e, "URL Encoding failed url($rootUri)") + rootUri // Pass original + } + return "coronawarnapp://check-ins/$encodedUrl".toUri() + } } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt index aab3a7215f7..0738967d5be 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt @@ -3,6 +3,8 @@ package de.rki.coronawarnapp.eventregistration.checkins.qrcode import de.rki.coronawarnapp.appconfig.AppConfigProvider import de.rki.coronawarnapp.appconfig.ConfigData import de.rki.coronawarnapp.appconfig.PresenceTracingConfigContainer +import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptor +import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptor.PayloadEncoding import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import io.mockk.MockKAnnotations @@ -11,10 +13,12 @@ import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.mockk import kotlinx.coroutines.test.runBlockingTest +import org.junit.Ignore import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ArgumentsSource import testhelpers.BaseTest +import timber.log.Timber class QRCodeUriParserTest : BaseTest() { @@ -25,7 +29,14 @@ class QRCodeUriParserTest : BaseTest() { MockKAnnotations.init(this) coEvery { appConfigProvider.getAppConfig() } returns mockk().apply { every { presenceTracing } returns PresenceTracingConfigContainer( - qrCodeDescriptors = listOf() + qrCodeDescriptors = listOf( + PresenceTracingQRCodeDescriptor.newBuilder() + .setEncodedPayloadGroupIndex(1) + .setPayloadEncoding(PayloadEncoding.BASE64) + .setRegexPattern("https://e\\.coronawarn\\.app\\?v=(\\d+)\\#(.+)") + .setVersionGroupIndex(0) + .build() + ) ) } } @@ -35,7 +46,9 @@ class QRCodeUriParserTest : BaseTest() { @ParameterizedTest @ArgumentsSource(ValidUrlProvider::class) fun `Valid URLs`(input: String) = runBlockingTest { - createInstance().getQrCodePayload(input) shouldNotBe null + val qrCodePayload = createInstance().getQrCodePayload(input) + qrCodePayload shouldNotBe null + Timber.d("qrCodePayload=$qrCodePayload") } @ParameterizedTest diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/ValidUrlProvider.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/ValidUrlProvider.kt index f7e0bf42fae..f8d65fe2bcf 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/ValidUrlProvider.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/ValidUrlProvider.kt @@ -9,30 +9,17 @@ class ValidUrlProvider : ArgumentsProvider { override fun provideArguments(context: ExtensionContext?): Stream { return Stream.of( Arguments.of( - "HTTPS://E.CORONAWARN.APP/C1/BIYAUEDBZY6EIWF7QX6JOKSRPAGEB3H7CIIEGV2BEBGGC5LOMNUCAUDBO" + - "J2HSGGTQ6SACIHXQ6SACKA6CJEDARQCEEAPHGEZ5JI2K2T422L5U3SMZY5DGCPUZ2RQACAYEJ3HQYMAFFBU2" + - "SQCEEAJAUCJSQJ7WDM675MCMOD3L2UL7ECJU7TYERH23B746RQTABO3CTI=" + "https://e.coronawarn.app?v=1#CAESLAgBEhFNeSBCaXJ0aGRheSBQYXJ0eRoLYXQgbXkgcGxhY2Uo04ekATD3h6QBGmoIARJgOMTa6eYSiaDv8lW13xdYEvGHOZ1EYTiFSxt51HEoPCD7CNnvCUiIYPhax1MpkN0UfNClCm9ZWYy0JH01CDVD9eq-voxQ1EcFJQkEIujVwoCNK0MNGuDK1ayjGxeDc4UDGgQxMjM0IgQIARAC" ), - Arguments.of( - "https://e.coronawarn.app/c1/BIYAUEDBZY6EIWF7QX6JOKSRPAGEB3H7CIIEGV2BEBGGC5LOMNUCAUDBO" + - "J2HSGGTQ6SACIHXQ6SACKA6CJEDARQCEEAPHGEZ5JI2K2T422L5U3SMZY5DGCPUZ2RQACAYEJ3HQYMAFFBU2" + - "SQCEEAJAUCJSQJ7WDM675MCMOD3L2UL7ECJU7TYERH23B746RQTABO3CTI=" - ), - Arguments.of( - "https://e.coronawarn.app/c1/BJLAUJBTGA2TKMZTGFRS2MRTGA3C2NBTMYZS2OJXGQZC2NTEHBTGCYRVG" + - "RSTQNBYCAARQARCCFGXSICCNFZHI2DEMF4SAUDBOJ2HSKQLMF2CA3LZEBYGYYLDMUYNHB5EAE4PPB5EAF" + - "AAAESIGBDAEIIARVENF6QT6XZATJ5GSDHL77BCAGR6QKDEUJRP2RDCTKTS7QECWMFAEIIA47MT2EA7MQK" + - "GNQU2XCY3Y2ZOZXCILDPC65PBUO4JJHT5LQQWDQSA" - ), - Arguments.of( - "https://e.coronawarn.app/c1/BJHAUJDGMNQTQNDCGM3S2NRRMMYC2NDBG5RS2YRSMY4C2OBSGVRWCZDEG" + - "UYDMY3GCAARQAJCBVEWGZLDOJSWC3JAKNUG64BKBVGWC2LOEBJXI4TFMV2CAMJQAA4AAQAKCJDTARICEB" + - "NEPPKKTAAIH5BSV45EPOINHOASARJLYYSHNTUUHLNGVYUZXZEBWARBACD53WYEGYXYQS3STOFLSOVM3XX" + - "D5A5HKMFQR7WYYARKKVOFGYGHO" - ), - Arguments.of("https://e.coronawarn.app/c1/JBSWY3DPEBLW64TMMQQQ===="), - Arguments.of("https://e.coronawarn.app/c1/JBSWY3DPEBLW64TMMQQQ"), - Arguments.of("https://e.coronawarn.app/c1/JBSWY3DPEBLW64TMMQQQ========"), +// Arguments.of( +// "https://e.coronawarn.app?v=1#" +// ), +// Arguments.of( +// "https://e.coronawarn.app?v=1#" +// ), +// Arguments.of( +// "https://e.coronawarn.app?v=1#" +// ) ) } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsViewModelTest.kt index 77d37b6285b..466def41d46 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsViewModelTest.kt @@ -5,6 +5,7 @@ import de.rki.coronawarnapp.eventregistration.checkins.CheckIn import de.rki.coronawarnapp.eventregistration.checkins.CheckInRepository import de.rki.coronawarnapp.eventregistration.checkins.qrcode.QRCodeUriParser import de.rki.coronawarnapp.presencetracing.checkins.checkout.CheckOutHandler +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass import de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.items.ActiveCheckInVH import de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.items.CameraPermissionVH import de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.items.PastCheckInVH @@ -80,11 +81,12 @@ class CheckInsViewModelTest : BaseTest() { @Test fun `DeepLink verification`() = runBlockingTest { every { savedState.get(any()) } returns null - every { qrCodeUriParser.getQrCodePayload(any()) } returns ByteString.EMPTY + coEvery { qrCodeUriParser.getQrCodePayload(any()) } returns + TraceLocationOuterClass.QRCodePayload.newBuilder().build() createInstance(deepLink = DEEP_LINK, scope = this).apply { events.getOrAwaitValue().shouldBeInstanceOf() - verify { + coVerify { savedState.get(any()) qrCodeUriParser.getQrCodePayload(any()) savedState.set(any(), any()) From b7598d4136c9c36b4168fe9d99436ae9fa66813e Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Sat, 3 Apr 2021 15:58:26 +0200 Subject: [PATCH 04/43] Specific exceptions --- .../qrcode/InvalidQRCodeDataException.kt | 6 ------ .../qrcode/InvalidQRCodeSignatureException.kt | 6 ------ .../checkins/qrcode/QRCodeException.kt | 9 +++++---- .../checkins/qrcode/QRCodeUriParser.kt | 17 ++++++++++++----- 4 files changed, 17 insertions(+), 21 deletions(-) delete mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/InvalidQRCodeDataException.kt delete mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/InvalidQRCodeSignatureException.kt diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/InvalidQRCodeDataException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/InvalidQRCodeDataException.kt deleted file mode 100644 index 935e86b319f..00000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/InvalidQRCodeDataException.kt +++ /dev/null @@ -1,6 +0,0 @@ -package de.rki.coronawarnapp.eventregistration.checkins.qrcode - -class InvalidQRCodeDataException constructor( - message: String? = null, - cause: Throwable? = null -) : QRCodeException(message, cause) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/InvalidQRCodeSignatureException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/InvalidQRCodeSignatureException.kt deleted file mode 100644 index eae77689d0a..00000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/InvalidQRCodeSignatureException.kt +++ /dev/null @@ -1,6 +0,0 @@ -package de.rki.coronawarnapp.eventregistration.checkins.qrcode - -class InvalidQRCodeSignatureException constructor( - message: String? = null, - cause: Throwable? = null -) : QRCodeException(message, cause) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeException.kt index 1fb4c881a81..47683b8b605 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeException.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeException.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.eventregistration.checkins.qrcode -open class QRCodeException constructor( - message: String? = null, - cause: Throwable? = null -) : Exception(message, cause) +sealed class QRCodeException constructor(message: String? = null) : Exception(message) + +class InvalidQrCodeUriException constructor(message: String? = null) : QRCodeException(message) +class InvalidQrCodeDataException constructor(message: String? = null) : QRCodeException(message) +class InvalidQrCodePayloadException constructor(message: String? = null) : QRCodeException(message) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt index a5faf64f24c..6c5dcbe1406 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt @@ -13,9 +13,16 @@ import javax.inject.Inject @Reusable class QRCodeUriParser @Inject constructor( - private val appConfigProvider: AppConfigProvider + private val configProvider: AppConfigProvider ) { + /** + * Parse [QRCodePayload] from [input] + * + * @throws [Exception] such as [QRCodeException], + * exceptions from [URI.create] + * and possible decoding exceptions + */ @Suppress("BlockingMethodInNonBlockingContext") suspend fun getQrCodePayload(input: String): QRCodePayload { Timber.d("input=$input") @@ -34,18 +41,18 @@ class QRCodeUriParser @Inject constructor( PayloadEncoding.BASE32 -> payload.decodeBase32() PayloadEncoding.BASE64 -> payload.decodeBase64() else -> null - } ?: throw IllegalArgumentException("Payload decoding failed") + } ?: throw InvalidQrCodeDataException("Payload decoding failed") return QRCodePayload.parseFrom(rawPayload.toByteArray()) } private suspend fun descriptor(input: String): PresenceTracingQRCodeDescriptorOrBuilder { - val descriptors = appConfigProvider.getAppConfig().presenceTracing.qrCodeDescriptors + val descriptors = configProvider.getAppConfig().presenceTracing.qrCodeDescriptors Timber.d("descriptors=$descriptors") val descriptor = descriptors.find { it.regexPattern.toRegex(RegexOption.IGNORE_CASE).matches(input) } if (descriptor == null) { Timber.d("Invalid URI - no matchedDescriptor") - throw IllegalArgumentException("Invalid URI - no matchedDescriptor") + throw InvalidQrCodeUriException("Invalid URI - no matchedDescriptor") } Timber.d("descriptor=$descriptor") return descriptor @@ -61,7 +68,7 @@ class QRCodeUriParser @Inject constructor( if (encodedPayloadGroupIndex !in groups.indices) { Timber.d("Invalid payload - group index is out of bounds") - throw IllegalArgumentException("Invalid payload - group index is out of bounds") + throw InvalidQrCodePayloadException("Invalid payload - group index is out of bounds") } return groups } From d550367f514b351f0136557465726306bc403013 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Sat, 3 Apr 2021 15:58:47 +0200 Subject: [PATCH 05/43] Tests --- .../checkins/CheckInsTransformer.kt | 2 - .../de/rki/coronawarnapp/util/ProtoBuff.kt | 7 ++ .../checkins/qrcode/QRCodeUriParserTest.kt | 54 +++++++++---- .../checkins/qrcode/ValidUrlProvider.kt | 77 ++++++++++++++++--- 4 files changed, 115 insertions(+), 25 deletions(-) create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ProtoBuff.kt diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/CheckInsTransformer.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/CheckInsTransformer.kt index 6f76b0b04c1..265a835224f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/CheckInsTransformer.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/CheckInsTransformer.kt @@ -107,5 +107,3 @@ fun CheckIn.determineRiskTransmission(now: Instant, transmissionVector: Transmis val ageInDays = Days.daysBetween(startMidnight, nowMidnight).days return transmissionVector.raw.getOrElse(ageInDays) { 1 } // Default value } - -private fun okio.ByteString.toProtoByteString() = ByteString.copyFrom(toByteArray()) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ProtoBuff.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ProtoBuff.kt new file mode 100644 index 00000000000..4066988731c --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ProtoBuff.kt @@ -0,0 +1,7 @@ +package de.rki.coronawarnapp.util + +import com.google.protobuf.ByteString +import okio.ByteString.Companion.toByteString + +fun okio.ByteString.toProtoByteString() = ByteString.copyFrom(toByteArray()) +fun ByteString.toOkioByteString() = toByteArray().toByteString() diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt index 0738967d5be..ad476969253 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt @@ -3,57 +3,85 @@ package de.rki.coronawarnapp.eventregistration.checkins.qrcode import de.rki.coronawarnapp.appconfig.AppConfigProvider import de.rki.coronawarnapp.appconfig.ConfigData import de.rki.coronawarnapp.appconfig.PresenceTracingConfigContainer +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.CWALocationData +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.QRCodePayload import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptor import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptor.PayloadEncoding +import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.shouldBe -import io.kotest.matchers.shouldNotBe import io.mockk.MockKAnnotations import io.mockk.coEvery import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.mockk import kotlinx.coroutines.test.runBlockingTest -import org.junit.Ignore import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ArgumentsSource import testhelpers.BaseTest -import timber.log.Timber +@Suppress("BlockingMethodInNonBlockingContext") class QRCodeUriParserTest : BaseTest() { - @MockK lateinit var appConfigProvider: AppConfigProvider + @MockK lateinit var configProvider: AppConfigProvider @BeforeEach fun setup() { MockKAnnotations.init(this) - coEvery { appConfigProvider.getAppConfig() } returns mockk().apply { + coEvery { configProvider.getAppConfig() } returns mockk().apply { every { presenceTracing } returns PresenceTracingConfigContainer( qrCodeDescriptors = listOf( PresenceTracingQRCodeDescriptor.newBuilder() + .setVersionGroupIndex(0) .setEncodedPayloadGroupIndex(1) .setPayloadEncoding(PayloadEncoding.BASE64) .setRegexPattern("https://e\\.coronawarn\\.app\\?v=(\\d+)\\#(.+)") - .setVersionGroupIndex(0) .build() ) ) } } - fun createInstance() = QRCodeUriParser(appConfigProvider) + fun createInstance() = QRCodeUriParser(configProvider) @ParameterizedTest @ArgumentsSource(ValidUrlProvider::class) - fun `Valid URLs`(input: String) = runBlockingTest { - val qrCodePayload = createInstance().getQrCodePayload(input) - qrCodePayload shouldNotBe null - Timber.d("qrCodePayload=$qrCodePayload") - } + fun `Valid URLs`( + input: String, + expectedPayload: QRCodePayload, + expectedVendorData: CWALocationData + ) = + runBlockingTest { + val qrCodePayload = createInstance().getQrCodePayload(input) + qrCodePayload shouldBe expectedPayload + CWALocationData.parseFrom(qrCodePayload.vendorData) shouldBe expectedVendorData + + /* qrCodePayload.apply { + version shouldBe expected.version + crowdNotifierData.apply { + cryptographicSeed shouldBe expected.crowdNotifierData.cryptographicSeed + version shouldBe expected.crowdNotifierData.version + publicKey shouldBe expected.crowdNotifierData.publicKey + + } + + locationData.apply { + description shouldBe expected.locationData.description + address shouldBe expected.locationData.address + version shouldBe expected.locationData.version + startTimestamp shouldBe expected.locationData.startTimestamp + endTimestamp shouldBe expected.locationData.endTimestamp + } + vendorData shouldBe expected.vendorData + }*/ + + } @ParameterizedTest @ArgumentsSource(InvalidUrlProvider::class) fun `Invalid URLs`(input: String) = runBlockingTest { - createInstance().getQrCodePayload(input) shouldBe null + shouldThrow { + createInstance().getQrCodePayload(input) + } } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/ValidUrlProvider.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/ValidUrlProvider.kt index f8d65fe2bcf..afea1d0c0aa 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/ValidUrlProvider.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/ValidUrlProvider.kt @@ -1,5 +1,12 @@ package de.rki.coronawarnapp.eventregistration.checkins.qrcode +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.CWALocationData +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.TraceLocation +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.QRCodePayload +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.CrowdNotifierData +import de.rki.coronawarnapp.util.toProtoByteString +import okio.ByteString.Companion.decodeBase64 import org.junit.jupiter.api.extension.ExtensionContext import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.ArgumentsProvider @@ -9,17 +16,67 @@ class ValidUrlProvider : ArgumentsProvider { override fun provideArguments(context: ExtensionContext?): Stream { return Stream.of( Arguments.of( - "https://e.coronawarn.app?v=1#CAESLAgBEhFNeSBCaXJ0aGRheSBQYXJ0eRoLYXQgbXkgcGxhY2Uo04ekATD3h6QBGmoIARJgOMTa6eYSiaDv8lW13xdYEvGHOZ1EYTiFSxt51HEoPCD7CNnvCUiIYPhax1MpkN0UfNClCm9ZWYy0JH01CDVD9eq-voxQ1EcFJQkEIujVwoCNK0MNGuDK1ayjGxeDc4UDGgQxMjM0IgQIARAC" + "https://e.coronawarn.app?v=1#CAESLAgBEhFNeSBCaXJ0aGRheSBQYXJ0eRoLYXQgbXkgcGxhY2Uo04ekATD3h6QBGmoIARJgO" + + "MTa6eYSiaDv8lW13xdYEvGHOZ1EYTiFSxt51HEoPCD7CNnvCUiIYPhax1MpkN0UfNClCm9ZWYy0JH01CDVD9eq-vox" + + "Q1EcFJQkEIujVwoCNK0MNGuDK1ayjGxeDc4UDGgQxMjM0IgQIARAC", + QRCodePayload.newBuilder() + .setVersion(1) + .setCrowdNotifierData( + CrowdNotifierData.newBuilder() + .setCryptographicSeed(CRYPTOGRAPHIC_SEED.decodeBase64()!!.toProtoByteString()) + .setPublicKey(PUB_KEY.decodeBase64()!!.toProtoByteString()) + .setVersion(1) + ) + .setVendorData("CAEQAg==".decodeBase64()!!.toProtoByteString()) + .setLocationData( + TraceLocation.newBuilder() + .setDescription("My Birthday Party") + .setAddress("at my place") + .setStartTimestamp(2687955) + .setEndTimestamp(2687991) + .setVersion(1) + .build() + ) + .build(), + CWALocationData.newBuilder() + .setType(TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_OTHER) + .setVersion(1) + .build() ), -// Arguments.of( -// "https://e.coronawarn.app?v=1#" -// ), -// Arguments.of( -// "https://e.coronawarn.app?v=1#" -// ), -// Arguments.of( -// "https://e.coronawarn.app?v=1#" -// ) + Arguments.of( + "https://e.coronawarn.app?v=1#CAESIAgBEg1JY2VjcmVhbSBTaG9wGg1NYWluIFN0cmVldCAxGmoIARJgOMTa6eYSiaDv8lW1" + + "3xdYEvGHOZ1EYTiFSxt51HEoPCD7CNnvCUiIYPhax1MpkN0UfNClCm9ZWYy0JH01CDVD9eq-voxQ" + + "1EcFJQkEIujVwoCNK0MNGuDK1ayjGxeDc4UDGgQxMjM0IgYIARABGAo", + QRCodePayload.newBuilder() + .setVersion(1) + .setCrowdNotifierData( + CrowdNotifierData.newBuilder() + .setCryptographicSeed(CRYPTOGRAPHIC_SEED.decodeBase64()!!.toProtoByteString()) + .setPublicKey(PUB_KEY.decodeBase64()!!.toProtoByteString()) + .setVersion(1) + ) + .setVendorData("CAEQARgK".decodeBase64()!!.toProtoByteString()) + .setLocationData( + TraceLocation.newBuilder() + .setDescription("Icecream Shop") + .setAddress("Main Street 1") + .setVersion(1) + .build() + ) + .build(), + CWALocationData.newBuilder() + .setType(TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_PERMANENT_OTHER) + .setVersion(1) + .setDefaultCheckInLengthInMinutes(10) + .build() + ) ) } + + companion object { + const val CRYPTOGRAPHIC_SEED = "MTIzNA==" + const val PUB_KEY = + "OMTa6eYSiaDv8lW13xdYEvGHOZ1EYTiFSxt51HEoPCD7CNnvCUiIYPhax1MpkN0UfNClCm9ZWYy0JH01CDVD9" + + "eq+voxQ1EcFJQkEIujVwoCNK0MNGuDK1ayjGxeDc4UD" + } } From 5d697e7baf1d2c0f16295f1b9fdd90365e7a140a Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Sat, 3 Apr 2021 16:03:14 +0200 Subject: [PATCH 06/43] lint --- .../appconfig/mapping/PresenceTracingConfigMapper.kt | 7 ++++--- .../eventregistration/checkins/CheckInsTransformer.kt | 1 - .../attendee/checkins/CheckInsFragment.kt | 2 -- .../checkins/qrcode/QRCodeUriParserTest.kt | 1 - .../attendee/checkins/CheckInsViewModelTest.kt | 1 - 5 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/PresenceTracingConfigMapper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/PresenceTracingConfigMapper.kt index be8bc073886..e5a96d4d136 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/PresenceTracingConfigMapper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/PresenceTracingConfigMapper.kt @@ -8,7 +8,8 @@ import de.rki.coronawarnapp.appconfig.PresenceTracingConfigContainer import de.rki.coronawarnapp.appconfig.PresenceTracingRiskCalculationParamContainer import de.rki.coronawarnapp.appconfig.PresenceTracingSubmissionParamContainer import de.rki.coronawarnapp.server.protocols.internal.v2.AppConfigAndroid -import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass +import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptor.PayloadEncoding +import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptor import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingSubmissionParameters import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingRiskCalculationParameters import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingParameters.QRCodeErrorCorrectionLevel @@ -95,9 +96,9 @@ class PresenceTracingConfigMapper @Inject constructor() : PresenceTracingConfig. plausibleDeniabilityParameters = plausibleDeniabilityParameters, qrCodeDescriptors = qrCodeDescriptorsOrBuilderList + // TODO remove - PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptor.newBuilder() + PresenceTracingQRCodeDescriptor.newBuilder() .setEncodedPayloadGroupIndex(1) - .setPayloadEncoding(PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptor.PayloadEncoding.BASE64) + .setPayloadEncoding(PayloadEncoding.BASE64) .setRegexPattern("https://e\\.coronawarn\\.app\\?v=(\\d+)\\#(.+)") .setVersionGroupIndex(0) .build() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/CheckInsTransformer.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/CheckInsTransformer.kt index 265a835224f..68c52400c7b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/CheckInsTransformer.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/CheckInsTransformer.kt @@ -1,6 +1,5 @@ package de.rki.coronawarnapp.eventregistration.checkins -import com.google.protobuf.ByteString import de.rki.coronawarnapp.appconfig.AppConfigProvider import de.rki.coronawarnapp.eventregistration.checkins.derivetime.deriveTime import de.rki.coronawarnapp.eventregistration.checkins.split.splitByMidnightUTC diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsFragment.kt index 825f9717e60..b39c06703c9 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsFragment.kt @@ -23,7 +23,6 @@ import de.rki.coronawarnapp.databinding.TraceLocationAttendeeCheckinsFragmentBin import de.rki.coronawarnapp.eventregistration.checkins.CheckIn import de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.items.CameraPermissionVH import de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.items.CheckInsItem -import de.rki.coronawarnapp.util.CWADebug import de.rki.coronawarnapp.util.di.AutoInject import de.rki.coronawarnapp.util.list.isSwipeable import de.rki.coronawarnapp.util.list.onSwipeItem @@ -38,7 +37,6 @@ import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider import de.rki.coronawarnapp.util.viewmodel.cwaViewModelsAssisted import timber.log.Timber import java.lang.Exception -import java.lang.reflect.Executable import java.net.URLEncoder import javax.inject.Inject diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt index ad476969253..351133d26ba 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt @@ -74,7 +74,6 @@ class QRCodeUriParserTest : BaseTest() { } vendorData shouldBe expected.vendorData }*/ - } @ParameterizedTest diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsViewModelTest.kt index 466def41d46..bfbd0747006 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/CheckInsViewModelTest.kt @@ -24,7 +24,6 @@ import io.mockk.verify import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runBlockingTest -import okio.ByteString import org.joda.time.Instant import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test From 2c2a9ec384fe4a72aa7ac90e713ba0bc05a30e67 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Sat, 3 Apr 2021 19:47:58 +0200 Subject: [PATCH 07/43] Generate QR Code url --- .../events/TraceLocationUrlGenerator.kt | 55 ++++++++++++++++ .../de/rki/coronawarnapp/util/ProtoBuff.kt | 2 +- .../checkins/qrcode/QRCodeUriParserTest.kt | 30 ++------- .../events/TraceLocationUrlGeneratorTest.kt | 63 +++++++++++++++++++ 4 files changed, 124 insertions(+), 26 deletions(-) create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlGenerator.kt create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlGeneratorTest.kt diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlGenerator.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlGenerator.kt new file mode 100644 index 00000000000..2e98d6e3c02 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlGenerator.kt @@ -0,0 +1,55 @@ +package de.rki.coronawarnapp.eventregistration.events + +import com.google.common.io.BaseEncoding +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TRACE_LOCATION_VERSION +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.QRCodePayload +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.CWALocationData +import de.rki.coronawarnapp.util.TimeAndDateExtensions.seconds +import de.rki.coronawarnapp.util.toProtoByteString +import okio.ByteString.Companion.decodeBase64 +import javax.inject.Inject + +class TraceLocationUrlGenerator @Inject constructor() { + + fun traceLocationUrl(traceLocation: TraceLocation): String { + val vendorData = CWALocationData.newBuilder() + .setType(traceLocation.type) + .setDefaultCheckInLengthInMinutes(traceLocation.defaultCheckInLengthInMinutes ?: 0) + .setVersion(TRACE_LOCATION_VERSION) + .build() + + val crowdNotifierData = TraceLocationOuterClass.CrowdNotifierData.newBuilder() + .setCryptographicSeed(traceLocation.cryptographicSeed.toProtoByteString()) + .setPublicKey(traceLocation.cnPublicKey.decodeBase64()!!.toProtoByteString()) + .setVersion(TRACE_LOCATION_VERSION) + + val locationData = TraceLocationOuterClass.TraceLocation.newBuilder() + .setDescription(traceLocation.description) + .setAddress(traceLocation.address) + .setStartTimestamp(traceLocation.startDate?.seconds ?: 0) + .setEndTimestamp(traceLocation.endDate?.seconds ?: 0) + .setVersion(TRACE_LOCATION_VERSION) + .build() + + val qrCodePayload = QRCodePayload.newBuilder() + .setVendorData(vendorData.toByteString()) + .setCrowdNotifierData(crowdNotifierData) + .setLocationData(locationData) + .setVersion(TRACE_LOCATION_VERSION) + .build() + + val base64Url = BaseEncoding + .base64Url() + .omitPadding() + .encode( + qrCodePayload.toByteArray() + ) + return AUTHORITY.plus(base64Url) + } + + companion object { + private const val AUTHORITY = "https://e.coronawarn.app?v=$TRACE_LOCATION_VERSION#" + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ProtoBuff.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ProtoBuff.kt index 4066988731c..c0a185307df 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ProtoBuff.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ProtoBuff.kt @@ -3,5 +3,5 @@ package de.rki.coronawarnapp.util import com.google.protobuf.ByteString import okio.ByteString.Companion.toByteString -fun okio.ByteString.toProtoByteString() = ByteString.copyFrom(toByteArray()) +fun okio.ByteString.toProtoByteString(): ByteString = ByteString.copyFrom(toByteArray()) fun ByteString.toOkioByteString() = toByteArray().toByteString() diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt index 351133d26ba..1b96cc2ae68 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt @@ -50,31 +50,11 @@ class QRCodeUriParserTest : BaseTest() { input: String, expectedPayload: QRCodePayload, expectedVendorData: CWALocationData - ) = - runBlockingTest { - val qrCodePayload = createInstance().getQrCodePayload(input) - qrCodePayload shouldBe expectedPayload - CWALocationData.parseFrom(qrCodePayload.vendorData) shouldBe expectedVendorData - - /* qrCodePayload.apply { - version shouldBe expected.version - crowdNotifierData.apply { - cryptographicSeed shouldBe expected.crowdNotifierData.cryptographicSeed - version shouldBe expected.crowdNotifierData.version - publicKey shouldBe expected.crowdNotifierData.publicKey - - } - - locationData.apply { - description shouldBe expected.locationData.description - address shouldBe expected.locationData.address - version shouldBe expected.locationData.version - startTimestamp shouldBe expected.locationData.startTimestamp - endTimestamp shouldBe expected.locationData.endTimestamp - } - vendorData shouldBe expected.vendorData - }*/ - } + ) = runBlockingTest { + val qrCodePayload = createInstance().getQrCodePayload(input) + qrCodePayload shouldBe expectedPayload + CWALocationData.parseFrom(qrCodePayload.vendorData) shouldBe expectedVendorData + } @ParameterizedTest @ArgumentsSource(InvalidUrlProvider::class) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlGeneratorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlGeneratorTest.kt new file mode 100644 index 00000000000..97bf6471fc8 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlGeneratorTest.kt @@ -0,0 +1,63 @@ +package de.rki.coronawarnapp.eventregistration.events + +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TRACE_LOCATION_VERSION +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass +import de.rki.coronawarnapp.util.TimeAndDateExtensions.secondsToInstant +import io.kotest.matchers.shouldBe +import kotlinx.coroutines.test.runBlockingTest +import okio.ByteString.Companion.decodeBase64 +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +class TraceLocationUrlGeneratorTest : BaseTest() { + + @Test + fun `traceLocationUrl 1`() = runBlockingTest { + val traceLocation = TraceLocation( + id = 1, + type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_OTHER, + description = "My Birthday Party", + address = "at my place", + startDate = 2687955L.secondsToInstant(), + endDate = 2687991L.secondsToInstant(), + defaultCheckInLengthInMinutes = null, + cryptographicSeed = CRYPTOGRAPHIC_SEED.decodeBase64()!!, + cnPublicKey = PUB_KEY, + version = TRACE_LOCATION_VERSION + ) + createInstance().traceLocationUrl(traceLocation) shouldBe + "https://e.coronawarn.app?v=1#CAESLAgBEhFNeSBCaXJ0aGRheSBQYXJ0eRoLYXQgbXkgcGxhY2Uo04ekATD3h6QBGmoIAR" + + "JgOMTa6eYSiaDv8lW13xdYEvGHOZ1EYTiFSxt51HEoPCD7CNnvCUiIYPhax1MpkN0UfNClCm9ZWYy0JH01CDVD9eq-voxQ1EcFJ" + + "QkEIujVwoCNK0MNGuDK1ayjGxeDc4UDGgQxMjM0IgQIARAC" + } + + @Test + fun `traceLocationUrl 2`() = runBlockingTest { + val traceLocation = TraceLocation( + id = 2, + type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_PERMANENT_OTHER, + description = "Icecream Shop", + address = "Main Street 1", + startDate = null, + endDate = null, + defaultCheckInLengthInMinutes = 10, + cryptographicSeed = CRYPTOGRAPHIC_SEED.decodeBase64()!!, + cnPublicKey = PUB_KEY, + version = TRACE_LOCATION_VERSION + ) + createInstance().traceLocationUrl(traceLocation) shouldBe + "https://e.coronawarn.app?v=1#CAESIAgBEg1JY2VjcmVhbSBTaG9wGg1NYWluIFN0cmVldCAxGmoIARJgOMTa6eYSiaDv8l" + + "W13xdYEvGHOZ1EYTiFSxt51HEoPCD7CNnvCUiIYPhax1MpkN0UfNClCm9ZWYy0JH01CDVD9eq-voxQ1EcFJQkEIujVwoCNK0MNG" + + "uDK1ayjGxeDc4UDGgQxMjM0IgYIARABGAo" + } + + private fun createInstance() = TraceLocationUrlGenerator() + + companion object { + const val CRYPTOGRAPHIC_SEED = "MTIzNA==" + const val PUB_KEY = + "OMTa6eYSiaDv8lW13xdYEvGHOZ1EYTiFSxt51HEoPCD7CNnvCUiIYPhax1MpkN0UfNClCm9ZWYy0JH01CDVD9" + + "eq+voxQ1EcFJQkEIujVwoCNK0MNGuDK1ayjGxeDc4UD" + } +} From 894cf353793347b2d297bf8eeee21b6889000292 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Sat, 3 Apr 2021 21:00:34 +0200 Subject: [PATCH 08/43] Test base32 --- .../checkins/qrcode/Base32UrlProvider.kt | 81 +++++++++++++++++++ ...lidUrlProvider.kt => Base64UrlProvider.kt} | 2 +- .../checkins/qrcode/QRCodeUriParserTest.kt | 29 ++++++- 3 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/Base32UrlProvider.kt rename Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/{ValidUrlProvider.kt => Base64UrlProvider.kt} (98%) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/Base32UrlProvider.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/Base32UrlProvider.kt new file mode 100644 index 00000000000..036343e6dcf --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/Base32UrlProvider.kt @@ -0,0 +1,81 @@ +package de.rki.coronawarnapp.eventregistration.checkins.qrcode + +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.CWALocationData +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.TraceLocation +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.QRCodePayload +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.CrowdNotifierData +import de.rki.coronawarnapp.util.toProtoByteString +import okio.ByteString.Companion.decodeBase64 +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.ArgumentsProvider +import java.util.stream.Stream + +class Base32UrlProvider : ArgumentsProvider { + override fun provideArguments(context: ExtensionContext?): Stream { + return Stream.of( + Arguments.of( + "https://e.coronawarn.app?v=1#BAARELAIAEJBCTLZEBBGS4TUNBSGC6JAKBQXE5DZDIFWC5BANV4SA4DMMFRWKKGTQ6SA" + + "CMHXQ6SACGTFBAAREWZQLEYBGBQHFKDERTR5AIAQMCBKQZEM4PIDAEDQGQQAARZ3BRFS24KCCFZSSN7E4YBSPXT7QU4DO" + + "UKYNRUDLSSPJTFOZWCHHV5DFTRKISOOU5Y3ENLQ2WS2HYW5YAPUE3C6XBJRSAEF6352AOK6DICDCMRTGQRAICABCABA", + QRCodePayload.newBuilder() + .setVersion(1) + .setCrowdNotifierData( + CrowdNotifierData.newBuilder() + .setCryptographicSeed(CRYPTOGRAPHIC_SEED.decodeBase64()!!.toProtoByteString()) + .setPublicKey(PUB_KEY.decodeBase64()!!.toProtoByteString()) + .setVersion(1) + ) + .setVendorData("CAEQAg==".decodeBase64()!!.toProtoByteString()) + .setLocationData( + TraceLocation.newBuilder() + .setDescription("My Birthday Party") + .setAddress("at my place") + .setStartTimestamp(2687955) + .setEndTimestamp(2687991) + .setVersion(1) + .build() + ) + .build(), + CWALocationData.newBuilder() + .setType(TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_OTHER) + .setVersion(1) + .build() + ), + Arguments.of( + "https://e.coronawarn.app?v=1#BAAREIAIAEJA2SLDMVRXEZLBNUQFG2DPOANA2TLBNFXCAU3UOJSWK5BAGENGKCABCJNTA" + + "WJQCMDAOKUGJDHD2AQBAYECVBSIZY6QGAIHANBAABDTWDCLFVYUEELTFE36JZQDE7PH7BJYG5IVQ3DIGXFE6TGK5TMEOPL" + + "2GLHCURE45J3RWI2XBVNFUPRN3QA7IJWF5OCTDEAIL5X3UA4V4GQEGEZDGNBCAYEACEABDAFA", + QRCodePayload.newBuilder() + .setVersion(1) + .setCrowdNotifierData( + CrowdNotifierData.newBuilder() + .setCryptographicSeed(CRYPTOGRAPHIC_SEED.decodeBase64()!!.toProtoByteString()) + .setPublicKey(PUB_KEY.decodeBase64()!!.toProtoByteString()) + .setVersion(1) + ) + .setVendorData("CAEQARgK".decodeBase64()!!.toProtoByteString()) + .setLocationData( + TraceLocation.newBuilder() + .setDescription("Icecream Shop") + .setAddress("Main Street 1") + .setVersion(1) + .build() + ) + .build(), + CWALocationData.newBuilder() + .setType(TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_PERMANENT_OTHER) + .setVersion(1) + .setDefaultCheckInLengthInMinutes(10) + .build() + ) + ) + } + + companion object { + const val CRYPTOGRAPHIC_SEED = "MTIzNA==" + const val PUB_KEY = + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7DEstcUIRcyk35OYDJ95/hTg3UVhsaDXKT0zK7NhHPXoyzipEnOp3GyNXDVpaPi3cAfQmxeuFMZAIX2+6A5Xg==" + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/ValidUrlProvider.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/Base64UrlProvider.kt similarity index 98% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/ValidUrlProvider.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/Base64UrlProvider.kt index afea1d0c0aa..2d92ae6bc96 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/ValidUrlProvider.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/Base64UrlProvider.kt @@ -12,7 +12,7 @@ import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.ArgumentsProvider import java.util.stream.Stream -class ValidUrlProvider : ArgumentsProvider { +class Base64UrlProvider : ArgumentsProvider { override fun provideArguments(context: ExtensionContext?): Stream { return Stream.of( Arguments.of( diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt index 1b96cc2ae68..c01a15b2b53 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParserTest.kt @@ -45,8 +45,8 @@ class QRCodeUriParserTest : BaseTest() { fun createInstance() = QRCodeUriParser(configProvider) @ParameterizedTest - @ArgumentsSource(ValidUrlProvider::class) - fun `Valid URLs`( + @ArgumentsSource(Base64UrlProvider::class) + fun `Base64 Valid URLs`( input: String, expectedPayload: QRCodePayload, expectedVendorData: CWALocationData @@ -56,6 +56,31 @@ class QRCodeUriParserTest : BaseTest() { CWALocationData.parseFrom(qrCodePayload.vendorData) shouldBe expectedVendorData } + @ParameterizedTest + @ArgumentsSource(Base32UrlProvider::class) + fun `Base32 Valid URLs`( + input: String, + expectedPayload: QRCodePayload, + expectedVendorData: CWALocationData + ) = runBlockingTest { + coEvery { configProvider.getAppConfig() } returns mockk().apply { + every { presenceTracing } returns PresenceTracingConfigContainer( + qrCodeDescriptors = listOf( + PresenceTracingQRCodeDescriptor.newBuilder() + .setVersionGroupIndex(0) + .setEncodedPayloadGroupIndex(1) + .setPayloadEncoding(PayloadEncoding.BASE32) + .setRegexPattern("https://e\\.coronawarn\\.app\\?v=(\\d+)\\#(.+)") + .build() + ) + ) + } + + val qrCodePayload = createInstance().getQrCodePayload(input) + qrCodePayload shouldBe expectedPayload + CWALocationData.parseFrom(qrCodePayload.vendorData) shouldBe expectedVendorData + } + @ParameterizedTest @ArgumentsSource(InvalidUrlProvider::class) fun `Invalid URLs`(input: String) = runBlockingTest { From ec0c317e5998937b3d7ebe77bb6fb003eb0cb12c Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Sat, 3 Apr 2021 21:01:04 +0200 Subject: [PATCH 09/43] Generate QrCode from url data --- .../organizer/details/QrCodeDetailViewModel.kt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt index d9a58e3871a..878cad6bc80 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt @@ -6,6 +6,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation +import de.rki.coronawarnapp.eventregistration.events.TraceLocationUrlGenerator import de.rki.coronawarnapp.eventregistration.storage.repo.DefaultTraceLocationRepository import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.ui.SingleLiveEvent @@ -21,6 +22,7 @@ class QrCodeDetailViewModel @AssistedInject constructor( @Assisted private val traceLocationId: Long?, private val dispatcher: DispatcherProvider, private val qrCodeGenerator: QrCodeGenerator, + private val traceLocationUrlGenerator: TraceLocationUrlGenerator, private val traceLocationRepository: DefaultTraceLocationRepository ) : CWAViewModel() { @@ -53,11 +55,7 @@ class QrCodeDetailViewModel @AssistedInject constructor( traceLocationFlow.value = traceLocation - createQrCode( - "HTTPS://E.CORONAWARN.APP/C1/BIYAUEDBZY6EIWF7QX6JOKSRPAGEB3H7CIIEGV2BEBGGC5LOMNUCAUD" + - "BOJ2HSGGTQ6SACIHXQ6SACKA6CJEDARQCEEAPHGEZ5JI2K2T422L5U3SMZY5DGCPUZ2RQACAYEJ3HQYMAFF" + - "BU2SQCEEAJAUCJSQJ7WDM675MCMOD3L2UL7ECJU7TYERH23B746RQTABO3CTI=" - ) + createQrCode(traceLocation) } } @@ -91,9 +89,10 @@ class QrCodeDetailViewModel @AssistedInject constructor( /** * Creates a QR Code [Bitmap] ,result is delivered by [qrCodeBitmap] */ - private fun createQrCode(input: String) = launch(context = dispatcher.IO) { - + private fun createQrCode(traceLocation: TraceLocation) = launch(context = dispatcher.IO) { try { + val input = traceLocationUrlGenerator.traceLocationUrl(traceLocation) + Timber.d("input=$input") qrCodeBitmap.postValue(qrCodeGenerator.createQrCode(input)) } catch (e: Exception) { Timber.d(e, "Qr code creation failed") From 7c04b470b72ca42c870510296c9968be50e7027d Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Sat, 3 Apr 2021 21:39:40 +0200 Subject: [PATCH 10/43] Remove / added by the system --- .../src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt index 87224434596..7cdf4b0cda7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt @@ -181,7 +181,8 @@ class MainActivity : AppCompatActivity(), HasAndroidInjector { private fun navigateByIntentUri(intent: Intent?) { val uri = intent?.data ?: return Timber.i("Uri:$uri") - navController.navigate(CheckInsFragment.createCheckInUri(uri.toString())) + val rootUri = uri.toString().replace(".app/?", ".app?") + navController.navigate(CheckInsFragment.createCheckInUri(rootUri)) } /** From 2c0a6c32ebc25ad14deb76c4762a9799e54a5f41 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Sat, 3 Apr 2021 22:47:00 +0200 Subject: [PATCH 11/43] Remove prefix --- Corona-Warn-App/src/main/AndroidManifest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/Corona-Warn-App/src/main/AndroidManifest.xml b/Corona-Warn-App/src/main/AndroidManifest.xml index d0ebbbe7886..2cefbd599e3 100644 --- a/Corona-Warn-App/src/main/AndroidManifest.xml +++ b/Corona-Warn-App/src/main/AndroidManifest.xml @@ -85,7 +85,6 @@ From 5286ef6129a903a9a528429c8f8b9a42880418f2 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Sun, 4 Apr 2021 12:04:40 +0200 Subject: [PATCH 12/43] Prepare for location id calculation --- .../checkins/qrcode/TraceLocation.kt | 11 ++++-- ...ator.kt => TraceLocationUrlIdGenerator.kt} | 36 ++++++++++--------- .../details/QrCodeDetailViewModel.kt | 6 ++-- ....kt => TraceLocationUrlIdGeneratorTest.kt} | 13 ++++--- 4 files changed, 36 insertions(+), 30 deletions(-) rename Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/{TraceLocationUrlGenerator.kt => TraceLocationUrlIdGenerator.kt} (66%) rename Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/{TraceLocationUrlGeneratorTest.kt => TraceLocationUrlIdGeneratorTest.kt} (85%) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/TraceLocation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/TraceLocation.kt index 92409f9bf5c..3e8463cb334 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/TraceLocation.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/TraceLocation.kt @@ -8,8 +8,6 @@ import okio.ByteString import okio.ByteString.Companion.decodeBase64 import org.joda.time.Instant -const val TRACE_LOCATION_VERSION = 1 - @Parcelize data class TraceLocation( val id: Long = 0L, @@ -21,12 +19,19 @@ data class TraceLocation( val defaultCheckInLengthInMinutes: Int?, val cryptographicSeed: ByteString, val cnPublicKey: String, - val version: Int = TRACE_LOCATION_VERSION, + val version: Int = VERSION, ) : Parcelable { fun isBeforeStartTime(now: Instant): Boolean = startDate?.isAfter(now) ?: false fun isAfterEndTime(now: Instant): Boolean = endDate?.isBefore(now) ?: false + + companion object { + /** + * Trace location version. This is a static data and not calculated from [TraceLocation] + */ + const val VERSION = 1 + } } fun List.toTraceLocations() = this.map { it.toTraceLocation() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlGenerator.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlIdGenerator.kt similarity index 66% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlGenerator.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlIdGenerator.kt index 2e98d6e3c02..aaf6bf4518b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlGenerator.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlIdGenerator.kt @@ -1,55 +1,57 @@ package de.rki.coronawarnapp.eventregistration.events import com.google.common.io.BaseEncoding -import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TRACE_LOCATION_VERSION import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.QRCodePayload import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.CWALocationData +import de.rki.coronawarnapp.ui.eventregistration.organizer.details.QrCodeGenerator import de.rki.coronawarnapp.util.TimeAndDateExtensions.seconds import de.rki.coronawarnapp.util.toProtoByteString import okio.ByteString.Companion.decodeBase64 import javax.inject.Inject -class TraceLocationUrlGenerator @Inject constructor() { +class TraceLocationUrlIdGenerator @Inject constructor() { - fun traceLocationUrl(traceLocation: TraceLocation): String { + /** + * Return a url for [TraceLocation] to be used as an input for [QrCodeGenerator] + * URL format https://e.coronawarn.app?v=1#QR_CODE_PAYLOAD_BASE64URL + */ + fun urlForQrCode(traceLocation: TraceLocation): String { + val payloadBytes = qrCodePayload(traceLocation).toByteArray() + val base64Url = BaseEncoding.base64Url().omitPadding().encode(payloadBytes) + return AUTHORITY.plus(base64Url) + } + + private fun qrCodePayload(traceLocation: TraceLocation): QRCodePayload { val vendorData = CWALocationData.newBuilder() .setType(traceLocation.type) .setDefaultCheckInLengthInMinutes(traceLocation.defaultCheckInLengthInMinutes ?: 0) - .setVersion(TRACE_LOCATION_VERSION) + .setVersion(TraceLocation.VERSION) .build() val crowdNotifierData = TraceLocationOuterClass.CrowdNotifierData.newBuilder() .setCryptographicSeed(traceLocation.cryptographicSeed.toProtoByteString()) .setPublicKey(traceLocation.cnPublicKey.decodeBase64()!!.toProtoByteString()) - .setVersion(TRACE_LOCATION_VERSION) + .setVersion(TraceLocation.VERSION) val locationData = TraceLocationOuterClass.TraceLocation.newBuilder() .setDescription(traceLocation.description) .setAddress(traceLocation.address) .setStartTimestamp(traceLocation.startDate?.seconds ?: 0) .setEndTimestamp(traceLocation.endDate?.seconds ?: 0) - .setVersion(TRACE_LOCATION_VERSION) + .setVersion(TraceLocation.VERSION) .build() - val qrCodePayload = QRCodePayload.newBuilder() + return QRCodePayload.newBuilder() .setVendorData(vendorData.toByteString()) .setCrowdNotifierData(crowdNotifierData) .setLocationData(locationData) - .setVersion(TRACE_LOCATION_VERSION) + .setVersion(TraceLocation.VERSION) .build() - - val base64Url = BaseEncoding - .base64Url() - .omitPadding() - .encode( - qrCodePayload.toByteArray() - ) - return AUTHORITY.plus(base64Url) } companion object { - private const val AUTHORITY = "https://e.coronawarn.app?v=$TRACE_LOCATION_VERSION#" + private const val AUTHORITY = "https://e.coronawarn.app?v=${TraceLocation.VERSION}#" } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt index 878cad6bc80..f8dcfb0c150 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt @@ -6,7 +6,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation -import de.rki.coronawarnapp.eventregistration.events.TraceLocationUrlGenerator +import de.rki.coronawarnapp.eventregistration.events.TraceLocationUrlIdGenerator import de.rki.coronawarnapp.eventregistration.storage.repo.DefaultTraceLocationRepository import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.ui.SingleLiveEvent @@ -22,7 +22,7 @@ class QrCodeDetailViewModel @AssistedInject constructor( @Assisted private val traceLocationId: Long?, private val dispatcher: DispatcherProvider, private val qrCodeGenerator: QrCodeGenerator, - private val traceLocationUrlGenerator: TraceLocationUrlGenerator, + private val traceLocationUrlIdGenerator: TraceLocationUrlIdGenerator, private val traceLocationRepository: DefaultTraceLocationRepository ) : CWAViewModel() { @@ -91,7 +91,7 @@ class QrCodeDetailViewModel @AssistedInject constructor( */ private fun createQrCode(traceLocation: TraceLocation) = launch(context = dispatcher.IO) { try { - val input = traceLocationUrlGenerator.traceLocationUrl(traceLocation) + val input = traceLocationUrlIdGenerator.urlForQrCode(traceLocation) Timber.d("input=$input") qrCodeBitmap.postValue(qrCodeGenerator.createQrCode(input)) } catch (e: Exception) { diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlGeneratorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlIdGeneratorTest.kt similarity index 85% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlGeneratorTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlIdGeneratorTest.kt index 97bf6471fc8..08060b4f8ef 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlGeneratorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlIdGeneratorTest.kt @@ -1,6 +1,5 @@ package de.rki.coronawarnapp.eventregistration.events -import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TRACE_LOCATION_VERSION import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass import de.rki.coronawarnapp.util.TimeAndDateExtensions.secondsToInstant @@ -10,7 +9,7 @@ import okio.ByteString.Companion.decodeBase64 import org.junit.jupiter.api.Test import testhelpers.BaseTest -class TraceLocationUrlGeneratorTest : BaseTest() { +class TraceLocationUrlIdGeneratorTest : BaseTest() { @Test fun `traceLocationUrl 1`() = runBlockingTest { @@ -24,9 +23,9 @@ class TraceLocationUrlGeneratorTest : BaseTest() { defaultCheckInLengthInMinutes = null, cryptographicSeed = CRYPTOGRAPHIC_SEED.decodeBase64()!!, cnPublicKey = PUB_KEY, - version = TRACE_LOCATION_VERSION + version = TraceLocation.VERSION ) - createInstance().traceLocationUrl(traceLocation) shouldBe + createInstance().urlForQrCode(traceLocation) shouldBe "https://e.coronawarn.app?v=1#CAESLAgBEhFNeSBCaXJ0aGRheSBQYXJ0eRoLYXQgbXkgcGxhY2Uo04ekATD3h6QBGmoIAR" + "JgOMTa6eYSiaDv8lW13xdYEvGHOZ1EYTiFSxt51HEoPCD7CNnvCUiIYPhax1MpkN0UfNClCm9ZWYy0JH01CDVD9eq-voxQ1EcFJ" + "QkEIujVwoCNK0MNGuDK1ayjGxeDc4UDGgQxMjM0IgQIARAC" @@ -44,15 +43,15 @@ class TraceLocationUrlGeneratorTest : BaseTest() { defaultCheckInLengthInMinutes = 10, cryptographicSeed = CRYPTOGRAPHIC_SEED.decodeBase64()!!, cnPublicKey = PUB_KEY, - version = TRACE_LOCATION_VERSION + version = TraceLocation.VERSION ) - createInstance().traceLocationUrl(traceLocation) shouldBe + createInstance().urlForQrCode(traceLocation) shouldBe "https://e.coronawarn.app?v=1#CAESIAgBEg1JY2VjcmVhbSBTaG9wGg1NYWluIFN0cmVldCAxGmoIARJgOMTa6eYSiaDv8l" + "W13xdYEvGHOZ1EYTiFSxt51HEoPCD7CNnvCUiIYPhax1MpkN0UfNClCm9ZWYy0JH01CDVD9eq-voxQ1EcFJQkEIujVwoCNK0MNG" + "uDK1ayjGxeDc4UDGgQxMjM0IgYIARABGAo" } - private fun createInstance() = TraceLocationUrlGenerator() + private fun createInstance() = TraceLocationUrlIdGenerator() companion object { const val CRYPTOGRAPHIC_SEED = "MTIzNA==" From bd22fde45d554f7c567ae34864ce342df5d54c7b Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Sun, 4 Apr 2021 13:52:08 +0200 Subject: [PATCH 13/43] Calculate location id --- .../events/TraceLocationId.kt | 25 +++++++++++++ ...nUrlIdGenerator.kt => TraceLocationUrl.kt} | 24 ++++++------ .../details/QrCodeDetailViewModel.kt | 6 +-- .../events/TraceLocationIdTest.kt | 37 +++++++++++++++++++ ...neratorTest.kt => TraceLocationUrlTest.kt} | 14 ++++--- 5 files changed, 85 insertions(+), 21 deletions(-) create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationId.kt rename Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/{TraceLocationUrlIdGenerator.kt => TraceLocationUrl.kt} (68%) create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationIdTest.kt rename Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/{TraceLocationUrlIdGeneratorTest.kt => TraceLocationUrlTest.kt} (87%) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationId.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationId.kt new file mode 100644 index 00000000000..d7b69506090 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationId.kt @@ -0,0 +1,25 @@ +package de.rki.coronawarnapp.eventregistration.events + +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.QRCodePayload +import okio.ByteString.Companion.toByteString +import javax.inject.Inject + +class TraceLocationId @Inject constructor() { + + /** + * Returns a byte sequence that serves as an identifier for the trace location. + * The ID is the byte representation of SHA-256 hash. + * + * @param qrCodePayload [QRCodePayload] + */ + fun locationId(qrCodePayload: QRCodePayload): ByteArray { + val cwaDomain = CWA_GUID.toByteArray() + val payloadBytes = qrCodePayload.toByteArray() + val totalByteSequence = cwaDomain + payloadBytes + return totalByteSequence.toByteString().sha256().toByteArray() + } + + companion object { + private const val CWA_GUID = "CWA-GUID" + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlIdGenerator.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrl.kt similarity index 68% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlIdGenerator.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrl.kt index aaf6bf4518b..60b79b3f533 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlIdGenerator.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrl.kt @@ -11,35 +11,35 @@ import de.rki.coronawarnapp.util.toProtoByteString import okio.ByteString.Companion.decodeBase64 import javax.inject.Inject -class TraceLocationUrlIdGenerator @Inject constructor() { +class TraceLocationUrl @Inject constructor() { /** * Return a url for [TraceLocation] to be used as an input for [QrCodeGenerator] * URL format https://e.coronawarn.app?v=1#QR_CODE_PAYLOAD_BASE64URL */ - fun urlForQrCode(traceLocation: TraceLocation): String { - val payloadBytes = qrCodePayload(traceLocation).toByteArray() + fun locationUrl(traceLocation: TraceLocation): String { + val payloadBytes = traceLocation.qrCodePayload().toByteArray() val base64Url = BaseEncoding.base64Url().omitPadding().encode(payloadBytes) return AUTHORITY.plus(base64Url) } - private fun qrCodePayload(traceLocation: TraceLocation): QRCodePayload { + private fun TraceLocation.qrCodePayload(): QRCodePayload { val vendorData = CWALocationData.newBuilder() - .setType(traceLocation.type) - .setDefaultCheckInLengthInMinutes(traceLocation.defaultCheckInLengthInMinutes ?: 0) + .setType(type) + .setDefaultCheckInLengthInMinutes(defaultCheckInLengthInMinutes ?: 0) .setVersion(TraceLocation.VERSION) .build() val crowdNotifierData = TraceLocationOuterClass.CrowdNotifierData.newBuilder() - .setCryptographicSeed(traceLocation.cryptographicSeed.toProtoByteString()) - .setPublicKey(traceLocation.cnPublicKey.decodeBase64()!!.toProtoByteString()) + .setCryptographicSeed(cryptographicSeed.toProtoByteString()) + .setPublicKey(cnPublicKey.decodeBase64()!!.toProtoByteString()) .setVersion(TraceLocation.VERSION) val locationData = TraceLocationOuterClass.TraceLocation.newBuilder() - .setDescription(traceLocation.description) - .setAddress(traceLocation.address) - .setStartTimestamp(traceLocation.startDate?.seconds ?: 0) - .setEndTimestamp(traceLocation.endDate?.seconds ?: 0) + .setDescription(description) + .setAddress(address) + .setStartTimestamp(startDate?.seconds ?: 0) + .setEndTimestamp(endDate?.seconds ?: 0) .setVersion(TraceLocation.VERSION) .build() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt index f8dcfb0c150..072e083e3ab 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt @@ -6,7 +6,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation -import de.rki.coronawarnapp.eventregistration.events.TraceLocationUrlIdGenerator +import de.rki.coronawarnapp.eventregistration.events.TraceLocationUrl import de.rki.coronawarnapp.eventregistration.storage.repo.DefaultTraceLocationRepository import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.ui.SingleLiveEvent @@ -22,7 +22,7 @@ class QrCodeDetailViewModel @AssistedInject constructor( @Assisted private val traceLocationId: Long?, private val dispatcher: DispatcherProvider, private val qrCodeGenerator: QrCodeGenerator, - private val traceLocationUrlIdGenerator: TraceLocationUrlIdGenerator, + private val traceLocationUrl: TraceLocationUrl, private val traceLocationRepository: DefaultTraceLocationRepository ) : CWAViewModel() { @@ -91,7 +91,7 @@ class QrCodeDetailViewModel @AssistedInject constructor( */ private fun createQrCode(traceLocation: TraceLocation) = launch(context = dispatcher.IO) { try { - val input = traceLocationUrlIdGenerator.urlForQrCode(traceLocation) + val input = traceLocationUrl.locationUrl(traceLocation) Timber.d("input=$input") qrCodeBitmap.postValue(qrCodeGenerator.createQrCode(input)) } catch (e: Exception) { diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationIdTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationIdTest.kt new file mode 100644 index 00000000000..9533520c6cb --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationIdTest.kt @@ -0,0 +1,37 @@ +package de.rki.coronawarnapp.eventregistration.events + +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass +import io.kotest.matchers.shouldBe +import okio.ByteString.Companion.decodeBase64 +import okio.ByteString.Companion.toByteString +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +class TraceLocationIdTest : BaseTest() { + @Test + fun `locationId 1`() { + val qrCodePayloadBase64 = + "CAESLAgBEhFNeSBCaXJ0aGRheSBQYXJ0eRoLYXQgbXkgcGxhY2Uo04ekATD3h6QBGmUIARJbMFkwEwYHKoZIzj0CAQYIKo" + + "ZIzj0DAQcDQgAEc7DEstcUIRcyk35OYDJ95/hTg3UVhsaDXKT0zK7NhHPXoyzipEnOp3GyNXDVpaPi3cAfQmxe" + + "uFMZAIX2+6A5XhoEMTIzNCIECAEQAg==" + val qrCodePayload = TraceLocationOuterClass.QRCodePayload.parseFrom( + qrCodePayloadBase64.decodeBase64()!!.toByteArray() + ) + createInstance().locationId(qrCodePayload).toByteString().base64() shouldBe + "jNcJTCajd9Sen6Tbexl2Yb7O3J7ps47b6k4+QMT4xS0=" + } + + @Test + fun `locationId 2`() { + val qrCodePayloadBase64 = + "CAESIAgBEg1JY2VjcmVhbSBTaG9wGg1NYWluIFN0cmVldCAxGmUIARJbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7DEstcUIR" + + "cyk35OYDJ95/hTg3UVhsaDXKT0zK7NhHPXoyzipEnOp3GyNXDVpaPi3cAfQmxeuFMZAIX2+6A5XhoEMTIzNCIGCAEQARgK" + val qrCodePayload = TraceLocationOuterClass.QRCodePayload.parseFrom( + qrCodePayloadBase64.decodeBase64()!!.toByteArray() + ) + createInstance().locationId(qrCodePayload).toByteString().base64() shouldBe + "GMuCjqNmOdYyrFhyvFNTVEeLaZh+uShgUoY0LYJo4YQ=" + } + + private fun createInstance() = TraceLocationId() +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlIdGeneratorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlTest.kt similarity index 87% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlIdGeneratorTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlTest.kt index 08060b4f8ef..70f5361089e 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlIdGeneratorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlTest.kt @@ -9,10 +9,10 @@ import okio.ByteString.Companion.decodeBase64 import org.junit.jupiter.api.Test import testhelpers.BaseTest -class TraceLocationUrlIdGeneratorTest : BaseTest() { +class TraceLocationUrlTest : BaseTest() { @Test - fun `traceLocationUrl 1`() = runBlockingTest { + fun `locationUrl 1`() = runBlockingTest { val traceLocation = TraceLocation( id = 1, type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_OTHER, @@ -25,14 +25,15 @@ class TraceLocationUrlIdGeneratorTest : BaseTest() { cnPublicKey = PUB_KEY, version = TraceLocation.VERSION ) - createInstance().urlForQrCode(traceLocation) shouldBe + + createInstance().locationUrl(traceLocation) shouldBe "https://e.coronawarn.app?v=1#CAESLAgBEhFNeSBCaXJ0aGRheSBQYXJ0eRoLYXQgbXkgcGxhY2Uo04ekATD3h6QBGmoIAR" + "JgOMTa6eYSiaDv8lW13xdYEvGHOZ1EYTiFSxt51HEoPCD7CNnvCUiIYPhax1MpkN0UfNClCm9ZWYy0JH01CDVD9eq-voxQ1EcFJ" + "QkEIujVwoCNK0MNGuDK1ayjGxeDc4UDGgQxMjM0IgQIARAC" } @Test - fun `traceLocationUrl 2`() = runBlockingTest { + fun `locationUrl 2`() = runBlockingTest { val traceLocation = TraceLocation( id = 2, type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_PERMANENT_OTHER, @@ -45,13 +46,14 @@ class TraceLocationUrlIdGeneratorTest : BaseTest() { cnPublicKey = PUB_KEY, version = TraceLocation.VERSION ) - createInstance().urlForQrCode(traceLocation) shouldBe + + createInstance().locationUrl(traceLocation) shouldBe "https://e.coronawarn.app?v=1#CAESIAgBEg1JY2VjcmVhbSBTaG9wGg1NYWluIFN0cmVldCAxGmoIARJgOMTa6eYSiaDv8l" + "W13xdYEvGHOZ1EYTiFSxt51HEoPCD7CNnvCUiIYPhax1MpkN0UfNClCm9ZWYy0JH01CDVD9eq-voxQ1EcFJQkEIujVwoCNK0MNG" + "uDK1ayjGxeDc4UDGgQxMjM0IgYIARABGAo" } - private fun createInstance() = TraceLocationUrlIdGenerator() + private fun createInstance() = TraceLocationUrl() companion object { const val CRYPTOGRAPHIC_SEED = "MTIzNA==" From 28ab14c9daa31c4e2cedb287ddce4752ab945706 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Sun, 4 Apr 2021 21:04:36 +0200 Subject: [PATCH 14/43] Location Id from trace location and tests split --- .../eventregistration/events/QrCodePayload.kt | 38 +++++++++++ .../events/TraceLocationId.kt | 10 +++ .../events/TraceLocationUrl.kt | 34 ---------- .../events/QrCodePayloadTest.kt | 66 +++++++++++++++++++ .../events/TraceLocationIdTest.kt | 50 +++++++++++++- .../events/TraceLocationUrlTest.kt | 4 +- 6 files changed, 164 insertions(+), 38 deletions(-) create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/QrCodePayload.kt create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/QrCodePayloadTest.kt diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/QrCodePayload.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/QrCodePayload.kt new file mode 100644 index 00000000000..2bd51c53186 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/QrCodePayload.kt @@ -0,0 +1,38 @@ +package de.rki.coronawarnapp.eventregistration.events + +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.CWALocationData +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.CrowdNotifierData +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.QRCodePayload +import de.rki.coronawarnapp.util.TimeAndDateExtensions.seconds +import de.rki.coronawarnapp.util.toProtoByteString +import okio.ByteString.Companion.decodeBase64 + +fun TraceLocation.qrCodePayload(): QRCodePayload { + val vendorData = CWALocationData.newBuilder() + .setType(type) + .setDefaultCheckInLengthInMinutes(defaultCheckInLengthInMinutes ?: 0) + .setVersion(TraceLocation.VERSION) + .build() + + val crowdNotifierData = CrowdNotifierData.newBuilder() + .setCryptographicSeed(cryptographicSeed.toProtoByteString()) + .setPublicKey(cnPublicKey.decodeBase64()!!.toProtoByteString()) + .setVersion(TraceLocation.VERSION) + + val locationData = TraceLocationOuterClass.TraceLocation.newBuilder() + .setDescription(description) + .setAddress(address) + .setStartTimestamp(startDate?.seconds ?: 0) + .setEndTimestamp(endDate?.seconds ?: 0) + .setVersion(TraceLocation.VERSION) + .build() + + return QRCodePayload.newBuilder() + .setVendorData(vendorData.toByteString()) + .setCrowdNotifierData(crowdNotifierData) + .setLocationData(locationData) + .setVersion(TraceLocation.VERSION) + .build() +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationId.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationId.kt index d7b69506090..d6850be8b2b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationId.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationId.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.eventregistration.events +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.QRCodePayload import okio.ByteString.Companion.toByteString import javax.inject.Inject @@ -19,6 +20,15 @@ class TraceLocationId @Inject constructor() { return totalByteSequence.toByteString().sha256().toByteArray() } + /** + * Returns a byte sequence that serves as an identifier for the trace location. + * The ID is the byte representation of SHA-256 hash. + * + * @param traceLocation [TraceLocation] + */ + fun locationId(traceLocation: TraceLocation): ByteArray = + locationId(traceLocation.qrCodePayload()) + companion object { private const val CWA_GUID = "CWA-GUID" } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrl.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrl.kt index 60b79b3f533..cb87575df89 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrl.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrl.kt @@ -2,13 +2,7 @@ package de.rki.coronawarnapp.eventregistration.events import com.google.common.io.BaseEncoding import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation -import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass -import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.QRCodePayload -import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.CWALocationData import de.rki.coronawarnapp.ui.eventregistration.organizer.details.QrCodeGenerator -import de.rki.coronawarnapp.util.TimeAndDateExtensions.seconds -import de.rki.coronawarnapp.util.toProtoByteString -import okio.ByteString.Companion.decodeBase64 import javax.inject.Inject class TraceLocationUrl @Inject constructor() { @@ -23,34 +17,6 @@ class TraceLocationUrl @Inject constructor() { return AUTHORITY.plus(base64Url) } - private fun TraceLocation.qrCodePayload(): QRCodePayload { - val vendorData = CWALocationData.newBuilder() - .setType(type) - .setDefaultCheckInLengthInMinutes(defaultCheckInLengthInMinutes ?: 0) - .setVersion(TraceLocation.VERSION) - .build() - - val crowdNotifierData = TraceLocationOuterClass.CrowdNotifierData.newBuilder() - .setCryptographicSeed(cryptographicSeed.toProtoByteString()) - .setPublicKey(cnPublicKey.decodeBase64()!!.toProtoByteString()) - .setVersion(TraceLocation.VERSION) - - val locationData = TraceLocationOuterClass.TraceLocation.newBuilder() - .setDescription(description) - .setAddress(address) - .setStartTimestamp(startDate?.seconds ?: 0) - .setEndTimestamp(endDate?.seconds ?: 0) - .setVersion(TraceLocation.VERSION) - .build() - - return QRCodePayload.newBuilder() - .setVendorData(vendorData.toByteString()) - .setCrowdNotifierData(crowdNotifierData) - .setLocationData(locationData) - .setVersion(TraceLocation.VERSION) - .build() - } - companion object { private const val AUTHORITY = "https://e.coronawarn.app?v=${TraceLocation.VERSION}#" } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/QrCodePayloadTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/QrCodePayloadTest.kt new file mode 100644 index 00000000000..98881b794ea --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/QrCodePayloadTest.kt @@ -0,0 +1,66 @@ +package de.rki.coronawarnapp.eventregistration.events + +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass +import de.rki.coronawarnapp.util.TimeAndDateExtensions.secondsToInstant +import io.kotest.matchers.shouldBe +import okio.ByteString.Companion.decodeBase64 +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +class QrCodePayloadTest : BaseTest() { + + @Test + fun `Trace location to QrCodePayload 1`() { + val traceLocation = TraceLocation( + id = 1, + type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_OTHER, + description = "My Birthday Party", + address = "at my place", + startDate = 2687955L.secondsToInstant(), + endDate = 2687991L.secondsToInstant(), + defaultCheckInLengthInMinutes = null, + cryptographicSeed = CRYPTOGRAPHIC_SEED.decodeBase64()!!, + cnPublicKey = PUB_KEY, + version = TraceLocation.VERSION + ) + + traceLocation.qrCodePayload() shouldBe + TraceLocationOuterClass.QRCodePayload.parseFrom(PAYLOAD_1.decodeBase64()!!.toByteArray()) + } + + @Test + fun `Trace location to QrCodePayload 2`() { + val traceLocation = TraceLocation( + id = 2, + type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_PERMANENT_OTHER, + description = "Icecream Shop", + address = "Main Street 1", + startDate = null, + endDate = null, + defaultCheckInLengthInMinutes = 10, + cryptographicSeed = CRYPTOGRAPHIC_SEED.decodeBase64()!!, + cnPublicKey = PUB_KEY, + version = TraceLocation.VERSION + ) + + traceLocation.qrCodePayload() shouldBe + TraceLocationOuterClass.QRCodePayload.parseFrom(PAYLOAD_2.decodeBase64()!!.toByteArray()) + } + + companion object { + private const val PAYLOAD_1 = + "CAESLAgBEhFNeSBCaXJ0aGRheSBQYXJ0eRoLYXQgbXkgcGxhY2Uo04ekATD3h6QBGmUIARJbMFkwEwYHKoZIzj0CAQYIKo" + + "ZIzj0DAQcDQgAEc7DEstcUIRcyk35OYDJ95/hTg3UVhsaDXKT0zK7NhHPXoyzipEnOp3GyNXDVpaPi3cAfQmxe" + + "uFMZAIX2+6A5XhoEMTIzNCIECAEQAg==" + + private const val PAYLOAD_2 = + "CAESIAgBEg1JY2VjcmVhbSBTaG9wGg1NYWluIFN0cmVldCAxGmUIARJbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7DEstcUIR" + + "cyk35OYDJ95/hTg3UVhsaDXKT0zK7NhHPXoyzipEnOp3GyNXDVpaPi3cAfQmxeuFMZAIX2+6A5XhoEMTIzNCIGCAEQARgK" + + private const val CRYPTOGRAPHIC_SEED = "MTIzNA==" + private const val PUB_KEY = + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7DEstcUIRcyk35OYDJ95/hTg3UVhsaDXKT0z" + + "K7NhHPXoyzipEnOp3GyNXDVpaPi3cAfQmxeuFMZAIX2+6A5Xg==" + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationIdTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationIdTest.kt index 9533520c6cb..4ff7ef412d7 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationIdTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationIdTest.kt @@ -1,6 +1,8 @@ package de.rki.coronawarnapp.eventregistration.events +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass +import de.rki.coronawarnapp.util.TimeAndDateExtensions.secondsToInstant import io.kotest.matchers.shouldBe import okio.ByteString.Companion.decodeBase64 import okio.ByteString.Companion.toByteString @@ -9,7 +11,7 @@ import testhelpers.BaseTest class TraceLocationIdTest : BaseTest() { @Test - fun `locationId 1`() { + fun `locationId from qrCodePayloadBase64 - 1`() { val qrCodePayloadBase64 = "CAESLAgBEhFNeSBCaXJ0aGRheSBQYXJ0eRoLYXQgbXkgcGxhY2Uo04ekATD3h6QBGmUIARJbMFkwEwYHKoZIzj0CAQYIKo" + "ZIzj0DAQcDQgAEc7DEstcUIRcyk35OYDJ95/hTg3UVhsaDXKT0zK7NhHPXoyzipEnOp3GyNXDVpaPi3cAfQmxe" + @@ -22,7 +24,7 @@ class TraceLocationIdTest : BaseTest() { } @Test - fun `locationId 2`() { + fun `locationId from qrCodePayloadBase64 - 2`() { val qrCodePayloadBase64 = "CAESIAgBEg1JY2VjcmVhbSBTaG9wGg1NYWluIFN0cmVldCAxGmUIARJbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7DEstcUIR" + "cyk35OYDJ95/hTg3UVhsaDXKT0zK7NhHPXoyzipEnOp3GyNXDVpaPi3cAfQmxeuFMZAIX2+6A5XhoEMTIzNCIGCAEQARgK" @@ -33,5 +35,49 @@ class TraceLocationIdTest : BaseTest() { "GMuCjqNmOdYyrFhyvFNTVEeLaZh+uShgUoY0LYJo4YQ=" } + @Test + fun `locationId from traceLocation - 1`() { + val traceLocation = TraceLocation( + id = 1, + type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_OTHER, + description = "My Birthday Party", + address = "at my place", + startDate = 2687955L.secondsToInstant(), + endDate = 2687991L.secondsToInstant(), + defaultCheckInLengthInMinutes = null, + cryptographicSeed = CRYPTOGRAPHIC_SEED.decodeBase64()!!, + cnPublicKey = PUB_KEY, + version = TraceLocation.VERSION + ) + createInstance().locationId(traceLocation).toByteString().base64() shouldBe + "jNcJTCajd9Sen6Tbexl2Yb7O3J7ps47b6k4+QMT4xS0=" + } + + @Test + fun `locationId from traceLocation - 2`() { + val traceLocation = TraceLocation( + id = 2, + type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_PERMANENT_OTHER, + description = "Icecream Shop", + address = "Main Street 1", + startDate = null, + endDate = null, + defaultCheckInLengthInMinutes = 10, + cryptographicSeed = CRYPTOGRAPHIC_SEED.decodeBase64()!!, + cnPublicKey = PUB_KEY, + version = TraceLocation.VERSION + ) + + createInstance().locationId(traceLocation).toByteString().base64() shouldBe + "GMuCjqNmOdYyrFhyvFNTVEeLaZh+uShgUoY0LYJo4YQ=" + } + private fun createInstance() = TraceLocationId() + + companion object { + private const val CRYPTOGRAPHIC_SEED = "MTIzNA==" + private const val PUB_KEY = + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7DEstcUIRcyk35OYDJ95/hTg3UVhsaDXKT0z" + + "K7NhHPXoyzipEnOp3GyNXDVpaPi3cAfQmxeuFMZAIX2+6A5Xg==" + } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlTest.kt index 70f5361089e..fd2ab811c11 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlTest.kt @@ -56,8 +56,8 @@ class TraceLocationUrlTest : BaseTest() { private fun createInstance() = TraceLocationUrl() companion object { - const val CRYPTOGRAPHIC_SEED = "MTIzNA==" - const val PUB_KEY = + private const val CRYPTOGRAPHIC_SEED = "MTIzNA==" + private const val PUB_KEY = "OMTa6eYSiaDv8lW13xdYEvGHOZ1EYTiFSxt51HEoPCD7CNnvCUiIYPhax1MpkN0UfNClCm9ZWYy0JH01CDVD9" + "eq+voxQ1EcFJQkEIujVwoCNK0MNGuDK1ayjGxeDc4UD" } From 3de16aaadc88b92b88bd72993e54529b5373b793 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Sun, 4 Apr 2021 21:54:02 +0200 Subject: [PATCH 15/43] Display last added location data in test menu --- .../ui/EventRegistrationTestFragment.kt | 10 +++++ .../EventRegistrationTestFragmentViewModel.kt | 36 +++++++++++++++++- .../fragment_test_eventregistration.xml | 38 +++++++++++++++++++ 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt index beb72df52a2..3aa84171173 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt @@ -82,6 +82,16 @@ class EventRegistrationTestFragment : Fragment(R.layout.fragment_test_eventregis viewModel.riskCalculationRuntime.observe2(this) { binding.riskCalculationRuntimeText.text = "Risk calculation runtime in millis: $it" } + + viewModel.lastLocationData.observe(viewLifecycleOwner) { + it?.let { scannedData -> + with(binding) { + lastTraceLocation.text = scannedData.traceLocation.toString() + lastTraceLocationId.text = scannedData.id + lastTraceLocationUrl.text = scannedData.url + } + } + } } companion object { diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt index a87c13dc9c2..4fb1a411a4d 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt @@ -1,9 +1,13 @@ package de.rki.coronawarnapp.test.eventregistration.ui +import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.asLiveData import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation +import de.rki.coronawarnapp.eventregistration.events.TraceLocationId +import de.rki.coronawarnapp.eventregistration.events.TraceLocationUrl import de.rki.coronawarnapp.eventregistration.storage.repo.TraceLocationRepository import de.rki.coronawarnapp.presencetracing.risk.CheckInWarningMatcher import de.rki.coronawarnapp.presencetracing.risk.CheckInWarningOverlap @@ -13,17 +17,26 @@ import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.debug.measureTime import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory +import kotlinx.coroutines.flow.map import okio.ByteString.Companion.encode +import okio.ByteString.Companion.toByteString import org.joda.time.DateTime import timber.log.Timber class EventRegistrationTestFragmentViewModel @AssistedInject constructor( - private val dispatcherProvider: DispatcherProvider, + dispatcherProvider: DispatcherProvider, private val traceLocationRepository: TraceLocationRepository, private val checkInWarningMatcher: CheckInWarningMatcher, - private val presenceTracingRiskCalculator: PresenceTracingRiskCalculator + private val presenceTracingRiskCalculator: PresenceTracingRiskCalculator, + private val traceLocationId: TraceLocationId, + private val traceLocationUrl: TraceLocationUrl, ) : CWAViewModel(dispatcherProvider = dispatcherProvider) { + val lastLocationData: LiveData = + traceLocationRepository.allTraceLocations + .map { lastLocationData(it) } + .asLiveData(dispatcherProvider.Default) + private val checkInWarningOverlaps = mutableListOf() val checkInOverlapsText = MutableLiveData() val matchingRuntime = MutableLiveData() @@ -181,6 +194,25 @@ class EventRegistrationTestFragmentViewModel @AssistedInject constructor( } } + private fun lastLocationData(it: List): LastLocationData? { + val traceLocation = it.maxByOrNull { traceLocation -> traceLocation.id } + return if (traceLocation != null) { + LastLocationData( + traceLocation = traceLocation, + id = traceLocationId.locationId(traceLocation).toByteString().base64(), + url = traceLocationUrl.locationUrl(traceLocation) + ) + } else { + null + } + } + @AssistedFactory interface Factory : SimpleCWAViewModelFactory } + +data class LastLocationData( + val traceLocation: TraceLocation, + val id: String, + val url: String +) diff --git a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_eventregistration.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_eventregistration.xml index 7cde04273f1..7e0dffba9bf 100644 --- a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_eventregistration.xml +++ b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_eventregistration.xml @@ -118,6 +118,44 @@ + + + + + + + + + + + + Date: Sun, 4 Apr 2021 21:55:56 +0200 Subject: [PATCH 16/43] Update EventRegistrationTestFragmentViewModel.kt --- .../EventRegistrationTestFragmentViewModel.kt | 101 ------------------ 1 file changed, 101 deletions(-) diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt index 4fb1a411a4d..8e8a4d4b5a0 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt @@ -12,15 +12,12 @@ import de.rki.coronawarnapp.eventregistration.storage.repo.TraceLocationReposito import de.rki.coronawarnapp.presencetracing.risk.CheckInWarningMatcher import de.rki.coronawarnapp.presencetracing.risk.CheckInWarningOverlap import de.rki.coronawarnapp.presencetracing.risk.PresenceTracingRiskCalculator -import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.debug.measureTime import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory import kotlinx.coroutines.flow.map -import okio.ByteString.Companion.encode import okio.ByteString.Companion.toByteString -import org.joda.time.DateTime import timber.log.Timber class EventRegistrationTestFragmentViewModel @AssistedInject constructor( @@ -106,105 +103,7 @@ class EventRegistrationTestFragmentViewModel @AssistedInject constructor( } } - fun generateTestTraceLocations() { - launch { - val permanent = TraceLocation( - type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_PERMANENT_FOOD_SERVICE, - description = "SAP Kantine WDF20", - address = "Hauptstr. 3, 69115 Heidelberg", - startDate = null, - endDate = null, - defaultCheckInLengthInMinutes = 60, - cryptographicSeed = "".encode(), - cnPublicKey = "" - ) - traceLocationRepository.addTraceLocation(permanent) - - val oneDayEvent = TraceLocation( - type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_CULTURAL_EVENT, - description = "Jahrestreffen der deutschen SAP Anwendergruppe (one day)", - address = "Hauptstr. 3, 69115 Heidelberg", - startDate = DateTime.now().plusHours(2).toInstant(), - endDate = DateTime.now().plusHours(3).toInstant(), - defaultCheckInLengthInMinutes = 60, - cryptographicSeed = "".encode(), - cnPublicKey = "" - ) - traceLocationRepository.addTraceLocation(oneDayEvent) - - val partyHardEvent = TraceLocation( - type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_CLUB_ACTIVITY, - description = "Jahrestreffen der deutschen SAP Anwendergruppe (many days)", - address = "Hauptstr. 3, 69115 Heidelberg", - startDate = DateTime.now().plusHours(2).toInstant(), - endDate = DateTime.now().plusDays(5).plusHours(2).toInstant(), - defaultCheckInLengthInMinutes = 60, - cryptographicSeed = "".encode(), - cnPublicKey = "" - ) - traceLocationRepository.addTraceLocation(partyHardEvent) - - val oldPermanent = TraceLocation( - type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_PERMANENT_FOOD_SERVICE, - description = "SAP Kantine MOW07", - address = "Moscow, Kosmodomianskaya 52/7", - startDate = null, - endDate = null, - defaultCheckInLengthInMinutes = 60, - cryptographicSeed = "".encode(), - cnPublicKey = "" - ) - traceLocationRepository.addTraceLocation(oldPermanent) - - val oldTemporaryOne = TraceLocation( - type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_CLUB_ACTIVITY, - description = "Old temporary 1", - address = "Hauptstr. 3, 69115 Heidelberg", - startDate = DateTime.now().minusSeconds(16 * 86400).toInstant(), - endDate = DateTime.now().minusSeconds(15 * 86400 - 10).toInstant(), - defaultCheckInLengthInMinutes = 60, - cryptographicSeed = "".encode(), - cnPublicKey = "" - ) - traceLocationRepository.addTraceLocation(oldTemporaryOne) - - val oldTemporaryTwo = TraceLocation( - type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_CLUB_ACTIVITY, - description = "Old temporary 2", - address = "Hauptstr. 3, 69115 Heidelberg", - startDate = DateTime.now().minusSeconds(16 * 86400).toInstant(), - endDate = DateTime.now().minusSeconds(15 * 86400).toInstant(), - defaultCheckInLengthInMinutes = 60, - cryptographicSeed = "".encode(), - cnPublicKey = "" - ) - traceLocationRepository.addTraceLocation(oldTemporaryTwo) - - val oldTemporaryThree = TraceLocation( - type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_CLUB_ACTIVITY, - description = "Old temporary 3", - address = "Hauptstr. 3, 69115 Heidelberg", - startDate = DateTime.now().minusSeconds(16 * 86400).toInstant(), - endDate = DateTime.now().minusSeconds(15 * 86400 + 10).toInstant(), - defaultCheckInLengthInMinutes = 60, - cryptographicSeed = "".encode(), - cnPublicKey = "" - ) - traceLocationRepository.addTraceLocation(oldTemporaryThree) - } - } - private fun lastLocationData(it: List): LastLocationData? { - val traceLocation = it.maxByOrNull { traceLocation -> traceLocation.id } - return if (traceLocation != null) { - LastLocationData( - traceLocation = traceLocation, - id = traceLocationId.locationId(traceLocation).toByteString().base64(), - url = traceLocationUrl.locationUrl(traceLocation) - ) - } else { - null - } } @AssistedFactory From 44ab3aec237f0313a1768ef33f6f09f55c8553b8 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Sun, 4 Apr 2021 21:56:10 +0200 Subject: [PATCH 17/43] Simplify --- .../ui/EventRegistrationTestFragmentViewModel.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt index 8e8a4d4b5a0..e0972f37e71 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt @@ -104,6 +104,12 @@ class EventRegistrationTestFragmentViewModel @AssistedInject constructor( } private fun lastLocationData(it: List): LastLocationData? { + val traceLocation = it.maxByOrNull { traceLocation -> traceLocation.id } ?: return null + return LastLocationData( + traceLocation = traceLocation, + id = traceLocationId.locationId(traceLocation).toByteString().base64(), + url = traceLocationUrl.locationUrl(traceLocation) + ) } @AssistedFactory From fb9d3a3b342fbe2968979815f6e1451cf0cb64bd Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Sun, 4 Apr 2021 22:41:28 +0200 Subject: [PATCH 18/43] Display last locations --- .../ui/EventRegistrationTestFragment.kt | 22 ++++++---- .../EventRegistrationTestFragmentViewModel.kt | 37 +++++++++++++++- .../fragment_test_eventregistration.xml | 44 +++++++++++++++++-- 3 files changed, 89 insertions(+), 14 deletions(-) diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt index 3aa84171173..453d2d66225 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt @@ -50,10 +50,6 @@ class EventRegistrationTestFragment : Fragment(R.layout.fragment_test_eventregis showEventsButton.setOnClickListener { findNavController().navigate(R.id.showStoredEventsTestFragment) } - - generateTestTraceLocations.setOnClickListener { - viewModel.generateTestTraceLocations() - } } binding.runMatcher.setOnClickListener { viewModel.runMatcher() @@ -83,12 +79,22 @@ class EventRegistrationTestFragment : Fragment(R.layout.fragment_test_eventregis binding.riskCalculationRuntimeText.text = "Risk calculation runtime in millis: $it" } - viewModel.lastLocationData.observe(viewLifecycleOwner) { + viewModel.lastOrganiserLocation.observe(viewLifecycleOwner) { + it?.let { scannedData -> + with(binding) { + lastOrganiserLocation.text = scannedData.traceLocation.toString() + lastOrganiserLocationId.text = scannedData.id + lastOrganiserLocationUrl.text = scannedData.url + } + } + } + + viewModel.lastAttendeeLocation.observe(viewLifecycleOwner) { it?.let { scannedData -> with(binding) { - lastTraceLocation.text = scannedData.traceLocation.toString() - lastTraceLocationId.text = scannedData.id - lastTraceLocationUrl.text = scannedData.url + lastAttendeeLocation.text = scannedData.traceLocation.toString() + lastAttendeeLocationId.text = scannedData.id + lastAttendeeLocationUrl.text = scannedData.url } } } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt index e0972f37e71..b0243c632ee 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt @@ -5,6 +5,8 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.asLiveData import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import de.rki.coronawarnapp.eventregistration.checkins.CheckIn +import de.rki.coronawarnapp.eventregistration.checkins.CheckInRepository import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation import de.rki.coronawarnapp.eventregistration.events.TraceLocationId import de.rki.coronawarnapp.eventregistration.events.TraceLocationUrl @@ -12,28 +14,36 @@ import de.rki.coronawarnapp.eventregistration.storage.repo.TraceLocationReposito import de.rki.coronawarnapp.presencetracing.risk.CheckInWarningMatcher import de.rki.coronawarnapp.presencetracing.risk.CheckInWarningOverlap import de.rki.coronawarnapp.presencetracing.risk.PresenceTracingRiskCalculator +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.debug.measureTime import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory import kotlinx.coroutines.flow.map +import okio.ByteString.Companion.decodeBase64 import okio.ByteString.Companion.toByteString import timber.log.Timber class EventRegistrationTestFragmentViewModel @AssistedInject constructor( dispatcherProvider: DispatcherProvider, - private val traceLocationRepository: TraceLocationRepository, + traceLocationRepository: TraceLocationRepository, + checkInRepository: CheckInRepository, private val checkInWarningMatcher: CheckInWarningMatcher, private val presenceTracingRiskCalculator: PresenceTracingRiskCalculator, private val traceLocationId: TraceLocationId, private val traceLocationUrl: TraceLocationUrl, ) : CWAViewModel(dispatcherProvider = dispatcherProvider) { - val lastLocationData: LiveData = + val lastOrganiserLocation: LiveData = traceLocationRepository.allTraceLocations .map { lastLocationData(it) } .asLiveData(dispatcherProvider.Default) + val lastAttendeeLocation: LiveData = + checkInRepository.allCheckIns + .map { lastAttendeeLocationData(it) } + .asLiveData(dispatcherProvider.Default) + private val checkInWarningOverlaps = mutableListOf() val checkInOverlapsText = MutableLiveData() val matchingRuntime = MutableLiveData() @@ -112,6 +122,29 @@ class EventRegistrationTestFragmentViewModel @AssistedInject constructor( ) } + private fun lastAttendeeLocationData(it: List): LastLocationData? { + val checkIn = it.maxByOrNull { checkIn -> checkIn.id } ?: return null + + val traceLocation = TraceLocation( + id = 0, + type = TraceLocationOuterClass.TraceLocationType.forNumber(checkIn.type), + description = checkIn.description, + address = checkIn.address, + startDate = checkIn.traceLocationStart, + endDate = checkIn.traceLocationEnd, + defaultCheckInLengthInMinutes = checkIn.defaultCheckInLengthInMinutes, + cryptographicSeed = checkIn.cryptographicSeed, + cnPublicKey = checkIn.cnPublicKey, + version = checkIn.version + ) + + return LastLocationData( + traceLocation = traceLocation, + id = traceLocationId.locationId(traceLocation).toByteString().base64(), + url = traceLocationUrl.locationUrl(traceLocation) + ) + } + @AssistedFactory interface Factory : SimpleCWAViewModelFactory } diff --git a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_eventregistration.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_eventregistration.xml index 7e0dffba9bf..574555a1e73 100644 --- a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_eventregistration.xml +++ b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_eventregistration.xml @@ -130,17 +130,17 @@ style="@style/headline6" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="Last location (ID/URL)" /> + android:text="Last organiser location (ID/URL)" /> + + + + + + + + + + Date: Sun, 4 Apr 2021 23:03:12 +0200 Subject: [PATCH 19/43] Pass check In Id --- .../ui/EventRegistrationTestFragmentViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt index b0243c632ee..5caffea1c8f 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt @@ -126,7 +126,7 @@ class EventRegistrationTestFragmentViewModel @AssistedInject constructor( val checkIn = it.maxByOrNull { checkIn -> checkIn.id } ?: return null val traceLocation = TraceLocation( - id = 0, + id = checkIn.id, type = TraceLocationOuterClass.TraceLocationType.forNumber(checkIn.type), description = checkIn.description, address = checkIn.address, From d36d6893addf7c61b1867385d191b42ad7f4e8f5 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Sun, 4 Apr 2021 23:18:41 +0200 Subject: [PATCH 20/43] Tweaking --- .../ui/EventRegistrationTestFragment.kt | 19 +++++++++++-------- .../EventRegistrationTestFragmentViewModel.kt | 1 - .../fragment_test_eventregistration.xml | 6 ++++-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt index 453d2d66225..76805b57416 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.os.Bundle import android.view.View import android.widget.Toast +import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import de.rki.coronawarnapp.R @@ -80,21 +81,23 @@ class EventRegistrationTestFragment : Fragment(R.layout.fragment_test_eventregis } viewModel.lastOrganiserLocation.observe(viewLifecycleOwner) { - it?.let { scannedData -> + binding.lastOrganiserLocationCard.isVisible = it != null + it?.let { data -> with(binding) { - lastOrganiserLocation.text = scannedData.traceLocation.toString() - lastOrganiserLocationId.text = scannedData.id - lastOrganiserLocationUrl.text = scannedData.url + lastOrganiserLocation.text = data.traceLocation.toString() + lastOrganiserLocationId.text = "ID: " + data.id + lastOrganiserLocationUrl.text = "URL: " + data.url } } } viewModel.lastAttendeeLocation.observe(viewLifecycleOwner) { - it?.let { scannedData -> + binding.lastAttendeeLocationCard.isVisible = it != null + it?.let { data -> with(binding) { - lastAttendeeLocation.text = scannedData.traceLocation.toString() - lastAttendeeLocationId.text = scannedData.id - lastAttendeeLocationUrl.text = scannedData.url + lastAttendeeLocation.text = data.traceLocation.toString() + lastAttendeeLocationId.text = "ID: " + data.id + lastAttendeeLocationUrl.text = "URL: " + data.url } } } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt index 5caffea1c8f..06e775144e4 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt @@ -20,7 +20,6 @@ import de.rki.coronawarnapp.util.debug.measureTime import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory import kotlinx.coroutines.flow.map -import okio.ByteString.Companion.decodeBase64 import okio.ByteString.Companion.toByteString import timber.log.Timber diff --git a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_eventregistration.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_eventregistration.xml index 574555a1e73..d35124a53e2 100644 --- a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_eventregistration.xml +++ b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_eventregistration.xml @@ -119,6 +119,7 @@ + android:text="Last organiser location" /> + android:text="Last attendee location" /> Date: Mon, 5 Apr 2021 08:45:08 +0200 Subject: [PATCH 21/43] Style text --- .../ui/EventRegistrationTestFragment.kt | 51 ++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt index 76805b57416..0ecd5261be2 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt @@ -2,14 +2,20 @@ package de.rki.coronawarnapp.test.eventregistration.ui import android.annotation.SuppressLint import android.os.Bundle +import android.text.SpannedString import android.view.View import android.widget.Toast +import androidx.core.text.bold +import androidx.core.text.buildSpannedString +import androidx.core.text.color +import androidx.core.text.scale import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentTestEventregistrationBinding import de.rki.coronawarnapp.test.menu.ui.TestMenuItem +import de.rki.coronawarnapp.util.ContextExtensions.getColorCompat import de.rki.coronawarnapp.util.di.AutoInject import de.rki.coronawarnapp.util.ui.doNavigate import de.rki.coronawarnapp.util.ui.observe2 @@ -84,9 +90,9 @@ class EventRegistrationTestFragment : Fragment(R.layout.fragment_test_eventregis binding.lastOrganiserLocationCard.isVisible = it != null it?.let { data -> with(binding) { - lastOrganiserLocation.text = data.traceLocation.toString() - lastOrganiserLocationId.text = "ID: " + data.id - lastOrganiserLocationUrl.text = "URL: " + data.url + lastOrganiserLocation.text = traceLocation(data) + lastOrganiserLocationId.text = styleText("ID", data.id) + lastOrganiserLocationUrl.text = styleText("URL", data.url) } } } @@ -95,14 +101,47 @@ class EventRegistrationTestFragment : Fragment(R.layout.fragment_test_eventregis binding.lastAttendeeLocationCard.isVisible = it != null it?.let { data -> with(binding) { - lastAttendeeLocation.text = data.traceLocation.toString() - lastAttendeeLocationId.text = "ID: " + data.id - lastAttendeeLocationUrl.text = "URL: " + data.url + lastAttendeeLocation.text = traceLocation(data) + lastAttendeeLocationId.text = styleText("ID", data.id) + lastAttendeeLocationUrl.text = styleText("URL", data.url) } } } } + private fun traceLocation(data: LastLocationData): SpannedString = with(data.traceLocation) { + buildSpannedString { + append("TraceLocation [\n") + append(styleText("Id", id)) + append(styleText("type", type)) + append(styleText("version", version)) + append(styleText("address", address)) + append(styleText("description", description)) + append(styleText("startDate", startDate)) + append(styleText("endDate", endDate)) + append(styleText("defaultCheckInLengthInMinutes", defaultCheckInLengthInMinutes)) + append(styleText("cnPublicKey", cnPublicKey)) + append(styleText("cryptographicSeed", cryptographicSeed.base64())) + append("]") + } + } + + private fun styleText(key: String, value: Any?): SpannedString = + buildSpannedString { + bold { + color(requireContext().getColorCompat(R.color.colorAccent)) { + append("$key=") + } + } + + scale(0.85f) { + color(requireContext().getColorCompat(R.color.colorTextPrimary1)) { + append(value.toString()) + } + } + append("\n") + } + companion object { val MENU_ITEM = TestMenuItem( title = "Event Registration", From f1ce0738a21263f1fc1277b36977ed7e02e50409 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Mon, 5 Apr 2021 08:45:25 +0200 Subject: [PATCH 22/43] Base64 --- .../eventregistration/checkins/qrcode/VerifiedTraceLocation.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocation.kt index 6eb4f36452e..8bf538148b5 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocation.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocation.kt @@ -3,6 +3,7 @@ package de.rki.coronawarnapp.eventregistration.checkins.qrcode import android.os.Parcel import android.os.Parcelable import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass +import de.rki.coronawarnapp.util.toOkioByteString import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parceler import kotlinx.parcelize.Parcelize @@ -36,7 +37,7 @@ data class VerifiedTraceLocation( endDate = protoQrCodePayload.locationData.endTimestamp.toInstant(), defaultCheckInLengthInMinutes = vendorData.defaultCheckInLengthInMinutes, cryptographicSeed = protoQrCodePayload.crowdNotifierData.cryptographicSeed.toByteArray().toByteString(), - cnPublicKey = protoQrCodePayload.crowdNotifierData.publicKey.toStringUtf8() + cnPublicKey = protoQrCodePayload.crowdNotifierData.publicKey.toOkioByteString().base64() ) } From 10527f0ee441892217657e10f4607a846376e77d Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Mon, 5 Apr 2021 13:38:26 +0200 Subject: [PATCH 23/43] Refactoring --- .../qrcode/VerifiedTraceLocationTest.kt | 253 ++---------------- .../EventRegistrationTestFragmentViewModel.kt | 17 +- .../checkins/qrcode/VerifiedTraceLocation.kt | 53 ++-- .../events/TraceLocationId.kt | 45 ++-- .../events/TraceLocationUrl.kt | 21 +- .../confirm/ConfirmCheckInViewModel.kt | 7 +- .../details/QrCodeDetailViewModel.kt | 5 +- .../confirm/ConfirmCheckInViewModelTest.kt | 7 +- .../events/TraceLocationIdTest.kt | 15 +- .../events/TraceLocationUrlTest.kt | 6 +- 10 files changed, 84 insertions(+), 345 deletions(-) diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocationTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocationTest.kt index 8bbe73ab745..b71ffa22d51 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocationTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocationTest.kt @@ -1,13 +1,10 @@ package de.rki.coronawarnapp.eventregistration.checkins.qrcode -import de.rki.coronawarnapp.environment.EnvironmentSetup +import de.rki.coronawarnapp.eventregistration.events.locationId +import de.rki.coronawarnapp.eventregistration.events.qrCodePayload import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass import io.kotest.matchers.shouldBe -import io.mockk.MockKAnnotations -import io.mockk.every -import io.mockk.impl.annotations.MockK import okio.ByteString.Companion.decodeBase64 -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 @@ -15,254 +12,36 @@ import testhelpers.BaseTestInstrumentation @RunWith(JUnit4::class) class VerifiedTraceLocationTest : BaseTestInstrumentation() { - - @MockK lateinit var environmentSetup: EnvironmentSetup - - @Before - fun setUp() { - MockKAnnotations.init(this) - every { environmentSetup.appConfigVerificationKey } returns PUB_KEY - } - - // TODO: Ugly but kinda works @Test - fun verifyTraceLocationIdGenerationHash1() { + fun verifyTraceLocationMapping1() { val base64Payload = "CAESLAgBEhFNeSBCaXJ0aGRheSBQYXJ0eRoLYXQgbXkgcGxhY2Uo04ekAT" + "D3h6QBGmUIARJbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7DEst" + "cUIRcyk35OYDJ95/hTg3UVhsaDXKT0zK7NhHPXoyzipEnOp3GyNXDVpaPi3" + "cAfQmxeuFMZAIX2+6A5XhoEMTIzNCIECAEQAg==" - val base64LocationID = "jNcJTCajd9Sen6Tbexl2Yb7O3J7ps47b6k4+QMT4xS0=" - val qrCodePayload = - TraceLocationOuterClass.QRCodePayload.parseFrom(base64Payload.decodeBase64()!!.toByteArray()) - val instance = VerifiedTraceLocation(qrCodePayload) + val qrCodePayload = TraceLocationOuterClass.QRCodePayload.parseFrom( + base64Payload.decodeBase64()!!.toByteArray() + ) - instance.traceLocationID.sha256().base64() shouldBe base64LocationID + VerifiedTraceLocation(qrCodePayload).traceLocation.apply { + locationId.base64() shouldBe "jNcJTCajd9Sen6Tbexl2Yb7O3J7ps47b6k4+QMT4xS0=" + qrCodePayload() shouldBe qrCodePayload + } } @Test - fun verifyTraceLocationIdGenerationHash2() { + fun verifyTraceLocationMapping2() { val base64Payload = "CAESIAgBEg1JY2VjcmVhbSBTaG9wGg1NYWluIFN0cmVldCAxGmUIARJ" + "bMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7DEstcUIRcyk35OYDJ95/hTg3UVhsaDXKT" + "0zK7NhHPXoyzipEnOp3GyNXDVpaPi3cAfQmxeuFMZAIX2+6A5XhoEMTIzNCIGCAEQARgK" - val base64LocationID = "GMuCjqNmOdYyrFhyvFNTVEeLaZh+uShgUoY0LYJo4YQ=" - - val qrCodePayload = - TraceLocationOuterClass.QRCodePayload.parseFrom(base64Payload.decodeBase64()!!.toByteArray()) - val instance = VerifiedTraceLocation(qrCodePayload) - - instance.traceLocationID.sha256().base64() shouldBe base64LocationID - } - - /* disabled because of incompatibilities due to latest tech spec changes... needs to be re-written anyway - -@Test -fun verifyEventSuccess() = runBlockingTest { - val instant = Instant.ofEpochMilli(2687960 * 1_000L) - shouldNotThrowAny { - val verifyResult = traceLocationQRCodeVerifier.verify(ENCODED_EVENT1.decodeBase32().toByteArray()) - verifyResult.apply { - traceLocation.description shouldBe "My Birthday Party" - traceLocation.isBeforeStartTime(instant) shouldBe false - traceLocation.isAfterEndTime(instant) shouldBe false - } - } -} - -@Test -fun verifyParcelization() = runBlockingTest { - val verifyResult = traceLocationQRCodeVerifier.verify(ENCODED_EVENT1.decodeBase32().toByteArray()) - - val expectedTraceLocation = TraceLocation( - guid = "3055331c-2306-43f3-9742-6d8fab54e848", - version = 1, - type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_OTHER, - description = "My Birthday Party", - address = "at my place", - startDate = Instant.ofEpochSecond(2687955), - endDate = Instant.ofEpochSecond(2687991), - defaultCheckInLengthInMinutes = 0, - byteRepresentation = verifyResult.traceLocationBytes, - signature = verifyResult.signature.toByteArray().toByteString(), - ) - - verifyResult.traceLocation shouldBe expectedTraceLocation - - val bundle = Bundle().apply { - putParcelable("test", verifyResult.traceLocation) - } - - val parcelRaw = Parcel.obtain().apply { - writeBundle(bundle) - }.marshall() - - val restoredParcel = Parcel.obtain().apply { - unmarshall(parcelRaw, 0, parcelRaw.size) - setDataPosition(0) - } - - val restoredData = restoredParcel.readBundle()!!.run { - classLoader = TraceLocation::class.java.classLoader - getParcelable("test") - } - restoredData shouldBe expectedTraceLocation -} - -@Test -fun verifyEventStartTimeWaning() = runBlockingTest { - val instant = Instant.ofEpochMilli(2687940 * 1_000L) - shouldNotThrowAny { - val verifyResult = traceLocationQRCodeVerifier.verify(ENCODED_EVENT1.decodeBase32().toByteArray()) - verifyResult.apply { - traceLocation.description shouldBe "My Birthday Party" - traceLocation.isBeforeStartTime(instant) shouldBe true - traceLocation.isAfterEndTime(instant) shouldBe false - } - } -} - -@Test -fun verifyEventEndTimeWarning() = runBlockingTest { - val instant = Instant.now() - shouldNotThrowAny { - val verifyResult = traceLocationQRCodeVerifier.verify(ENCODED_EVENT1.decodeBase32().toByteArray()) - verifyResult.apply { - traceLocation.description shouldBe "My Birthday Party" - traceLocation.isBeforeStartTime(instant) shouldBe false - traceLocation.isAfterEndTime(instant) shouldBe true - } - } -} - -@Test -fun verifyEventWithInvalidKey() = runBlockingTest { - every { environmentSetup.appConfigVerificationKey } returns INVALID_PUB_KEY - shouldThrow { - traceLocationQRCodeVerifier.verify(ENCODED_EVENT1.decodeBase32().toByteArray()) - } -} -@Test -fun eventHasMalformedData() = runBlockingTest { - shouldThrow { - traceLocationQRCodeVerifier.verify( - INVALID_ENCODED_EVENT.decodeBase32().toByteArray() + val qrCodePayload = TraceLocationOuterClass.QRCodePayload.parseFrom( + base64Payload.decodeBase64()!!.toByteArray() ) - } -} - -@Test -fun decodingTest1() = runBlockingTest { - val signedTraceLocation = TraceLocationOuterClass.SignedTraceLocation.parseFrom( - ENCODED_EVENT1.decodeBase32().toByteArray() - ) - val expectedSignature = - "MEQCIGVKfqPF2851IrEyDeVMazlRnIzLX16H6r1TB37PRzjbAiBGP13ADQcbQZsztKUCZMRcvnv5Mgdo0LY/v3qFMnrUkQ==" - - val base32 = signedTraceLocation.toByteArray().toByteString().base32() - - shouldNotThrowAny { - val verifyResult = traceLocationQRCodeVerifier.verify(base32.decodeBase32().toByteArray()) - - verifyResult.apply { - traceLocation.description shouldBe "My Birthday Party" - signedTraceLocation.signature.toByteArray().toByteString().base64() shouldBe expectedSignature - } - } -} - -@Test -fun decodingTest2() = runBlockingTest { - val signedTraceLocation = TraceLocationOuterClass.SignedTraceLocation.parseFrom( - ENCODED_EVENT2.decodeBase32().toByteArray() - ) - val expectedSignature = - "MEQCIDWRTM4ujn1GFPuHlgpUnQIWwwzwI8abxSrF5Er2I5HaAiAbucxg+6d3nC/Iwzo7AXehJAS20TRX1S2rl0LO8kcYxA==" - - val base32 = signedTraceLocation.toByteArray().toByteString().base32() - - shouldNotThrowAny { - val verifyResult = traceLocationQRCodeVerifier.verify(base32.decodeBase32().toByteArray()) - verifyResult.apply { - traceLocation.description shouldBe "Icecream Shop" - signedTraceLocation.signature.toByteArray().toByteString().base64() shouldBe expectedSignature + VerifiedTraceLocation(qrCodePayload).traceLocation.apply { + locationId.base64() shouldBe "GMuCjqNmOdYyrFhyvFNTVEeLaZh+uShgUoY0LYJo4YQ=" + qrCodePayload() shouldBe qrCodePayload } } } - -@Test -fun testVerifiedTraceLocationMapping() { - shouldNotThrowAny { - val signedTraceLocation = TraceLocationOuterClass.SignedTraceLocation.parseFrom( - ENCODED_EVENT1.decodeBase32().toByteArray() - ) - - val traceLocation = TraceLocationOuterClass.TraceLocation.parseFrom( - ENCODED_EVENT1_LOCATION.decodeBase32().toByteArray() - ) - val verifiedTraceLocation = VerifiedTraceLocation( - protoSignedTraceLocation = signedTraceLocation, - protoTraceLocation = traceLocation - ).traceLocation - - verifiedTraceLocation shouldBe TraceLocation( - guid = "3055331c-2306-43f3-9742-6d8fab54e848", - version = 1, - type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_OTHER, - description = "My Birthday Party", - address = "at my place", - startDate = Instant.ofEpochSecond(2687955), - endDate = Instant.ofEpochSecond(2687991), - defaultCheckInLengthInMinutes = 0, - byteRepresentation = signedTraceLocation.location.toByteArray().toByteString(), - signature = signedTraceLocation.signature.toByteArray().toByteString() - ) - } -} - -*/ - - companion object { - - // "signedLocation": { - // "location": { - // "guid": "3055331c-2306-43f3-9742-6d8fab54e848", - // "version": 1, - // "type": 2, - // "description": "My Birthday Party", - // "address": "at my place", - // "startTimestamp": 2687955, - // "endTimestamp": 2687991, - // "defaultCheckInLengthInMinutes": 0 - // }, - // "signature": "MEQCIGVKfqPF2851IrEyDeVMazlRnIzLX16H6r1TB37PRzjbAiBGP13ADQcbQZsztKUCZMRcvnv5Mgdo0LY/v3qFMnrUkQ==" - private const val ENCODED_EVENT1 = - "BJLAUJBTGA2TKMZTGFRS2MRTGA3C2NBTMYZS2OJXGQZC2NTEHBTGCYRVGRSTQNBYCAARQARCCFGXSICCNFZHI2DEMF4SAUDBOJ2HSKQLMF2CA3LZEBYGYYLDMUYNHB5EAE4PPB5EAFAAAESGGBCAEIDFJJ7KHRO3ZZ2SFMJSBXSUY2ZZKGOIZS27L2D6VPKTA57M6RZY3MBCARR7LXAA2BY3IGNTHNFFAJSMIXF6PP4TEB3I2C3D7P32QUZHVVER" - private const val ENCODED_EVENT1_LOCATION = - "BISDGMBVGUZTGMLDFUZDGMBWFU2DGZRTFU4TONBSFU3GIODGMFRDKNDFHA2DQEABDABCEEKNPEQEE2LSORUGIYLZEBIGC4TUPEVAWYLUEBWXSIDQNRQWGZJQ2OD2IAJY66D2IAKAAA" - - // "signedLocation": { - // "location": { - // "guid": "fca84b37-61c0-4a7c-b2f8-825cadd506cf", - // "version": 1, - // "type": 1, - // "description": "Icecream Shop", - // "address": "Main Street 1", - // "startTimestamp": 0, - // "endTimestamp": 0, - // "defaultCheckInLengthInMinutes": 10 - // }, - // "signature": "MEQCIDWRTM4ujn1GFPuHlgpUnQIWwwzwI8abxSrF5Er2I5HaAiAbucxg+6d3nC/Iwzo7AXehJAS20TRX1S2rl0LO8kcYxA==" - private const val ENCODED_EVENT2 = - "BJHAUJDGMNQTQNDCGM3S2NRRMMYC2NDBG5RS2YRSMY4C2OBSGVRWCZDEGUYDMY3GCAARQAJCBVEWGZLDOJSWC3JAKNUG64BKBVGWC2LOEBJXI4TFMV2CAMJQAA4AAQAKCJDDARACEA2ZCTGOF2HH2RQU7ODZMCSUTUBBNQYM6AR4NG6FFLC6ISXWEOI5UARADO44YYH3U53ZYL6IYM5DWALXUESAJNWRGRL5KLNLS5BM54SHDDCA" - - private const val INVALID_ENCODED_EVENT = - "NB2HI4DTHIXS653XO4XHK4TCMFXGI2LDORUW63TBOJ4S4Y3PNUXWIZLGNFXGKLTQNBYD65DFOJWT2VDIMUSTEMCDN53GSZBFGIYDCOI=" - - private const val PUB_KEY = - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEafIKZOiRPuJWjKOUmKv7OTJWTyii4oCQLcGn3FgYoLQaJIvAM3Pl7anFDPPY/jxfqqrLyGc0f6hWQ9JPR3QjBw==" - private const val INVALID_PUB_KEY = - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7DEstcUIRcyk35OYDJ95/hTg3UVhsaDXKT0zK7NhHPXoyzipEnOp3GyNXDVpaPi3cAfQmxeuFMZAIX2+6A5Xg==" - } -} diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt index 06e775144e4..4ac4abfd5f8 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt @@ -8,8 +8,8 @@ import dagger.assisted.AssistedInject import de.rki.coronawarnapp.eventregistration.checkins.CheckIn import de.rki.coronawarnapp.eventregistration.checkins.CheckInRepository import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation -import de.rki.coronawarnapp.eventregistration.events.TraceLocationId -import de.rki.coronawarnapp.eventregistration.events.TraceLocationUrl +import de.rki.coronawarnapp.eventregistration.events.locationId +import de.rki.coronawarnapp.eventregistration.events.locationUrl import de.rki.coronawarnapp.eventregistration.storage.repo.TraceLocationRepository import de.rki.coronawarnapp.presencetracing.risk.CheckInWarningMatcher import de.rki.coronawarnapp.presencetracing.risk.CheckInWarningOverlap @@ -20,7 +20,6 @@ import de.rki.coronawarnapp.util.debug.measureTime import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory import kotlinx.coroutines.flow.map -import okio.ByteString.Companion.toByteString import timber.log.Timber class EventRegistrationTestFragmentViewModel @AssistedInject constructor( @@ -28,9 +27,7 @@ class EventRegistrationTestFragmentViewModel @AssistedInject constructor( traceLocationRepository: TraceLocationRepository, checkInRepository: CheckInRepository, private val checkInWarningMatcher: CheckInWarningMatcher, - private val presenceTracingRiskCalculator: PresenceTracingRiskCalculator, - private val traceLocationId: TraceLocationId, - private val traceLocationUrl: TraceLocationUrl, + private val presenceTracingRiskCalculator: PresenceTracingRiskCalculator ) : CWAViewModel(dispatcherProvider = dispatcherProvider) { val lastOrganiserLocation: LiveData = @@ -116,8 +113,8 @@ class EventRegistrationTestFragmentViewModel @AssistedInject constructor( val traceLocation = it.maxByOrNull { traceLocation -> traceLocation.id } ?: return null return LastLocationData( traceLocation = traceLocation, - id = traceLocationId.locationId(traceLocation).toByteString().base64(), - url = traceLocationUrl.locationUrl(traceLocation) + id = traceLocation.locationId.base64(), + url = traceLocation.locationUrl ) } @@ -139,8 +136,8 @@ class EventRegistrationTestFragmentViewModel @AssistedInject constructor( return LastLocationData( traceLocation = traceLocation, - id = traceLocationId.locationId(traceLocation).toByteString().base64(), - url = traceLocationUrl.locationUrl(traceLocation) + id = traceLocation.locationId.base64(), + url = traceLocation.locationUrl ) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocation.kt index 8bf538148b5..75f64e2c798 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocation.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocation.kt @@ -3,17 +3,14 @@ package de.rki.coronawarnapp.eventregistration.checkins.qrcode import android.os.Parcel import android.os.Parcelable import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass +import de.rki.coronawarnapp.util.TimeAndDateExtensions.secondsToInstant import de.rki.coronawarnapp.util.toOkioByteString import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parceler import kotlinx.parcelize.Parcelize import kotlinx.parcelize.TypeParceler -import okio.Buffer -import okio.ByteString -import okio.ByteString.Companion.encode + import okio.ByteString.Companion.toByteString -import org.joda.time.Instant -import java.util.concurrent.TimeUnit @Parcelize @TypeParceler() @@ -21,42 +18,22 @@ import java.util.concurrent.TimeUnit data class VerifiedTraceLocation( private val protoQrCodePayload: TraceLocationOuterClass.QRCodePayload ) : Parcelable { + @IgnoredOnParcel val traceLocation: TraceLocation = protoQrCodePayload.traceLocation() - @IgnoredOnParcel private val vendorData by lazy { - TraceLocationOuterClass.CWALocationData.parseFrom(protoQrCodePayload.vendorData) - } - - @IgnoredOnParcel val traceLocation: TraceLocation by lazy { - - TraceLocation( - version = protoQrCodePayload.version, - type = vendorData.type, - description = protoQrCodePayload.locationData.description, - address = protoQrCodePayload.locationData.address, - startDate = protoQrCodePayload.locationData.startTimestamp.toInstant(), - endDate = protoQrCodePayload.locationData.endTimestamp.toInstant(), - defaultCheckInLengthInMinutes = vendorData.defaultCheckInLengthInMinutes, - cryptographicSeed = protoQrCodePayload.crowdNotifierData.cryptographicSeed.toByteArray().toByteString(), - cnPublicKey = protoQrCodePayload.crowdNotifierData.publicKey.toOkioByteString().base64() + private fun TraceLocationOuterClass.QRCodePayload.traceLocation(): TraceLocation { + val cwaLocationData = TraceLocationOuterClass.CWALocationData.parseFrom(protoQrCodePayload.vendorData) + return TraceLocation( + version = version, + type = cwaLocationData.type, + defaultCheckInLengthInMinutes = cwaLocationData.defaultCheckInLengthInMinutes, + description = locationData.description, + address = locationData.address, + startDate = locationData.startTimestamp.secondsToInstant(), + endDate = locationData.endTimestamp.secondsToInstant(), + cryptographicSeed = crowdNotifierData.cryptographicSeed.toByteArray().toByteString(), + cnPublicKey = crowdNotifierData.publicKey.toOkioByteString().base64() ) } - - @IgnoredOnParcel private val traceLocationHeader: ByteString by lazy { - "CWA-GUID".encode(Charsets.UTF_8) - } - - @IgnoredOnParcel val traceLocationID: ByteString by lazy { - Buffer() - .write(traceLocationHeader) - .write(protoQrCodePayload.toByteArray()) - .readByteString() - } - - /** - * Converts time in seconds into [Instant] - */ - private fun Long.toInstant() = - if (this == 0L) null else Instant.ofEpochMilli(TimeUnit.SECONDS.toMillis(this)) } private object TraceLocationParceler : Parceler { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationId.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationId.kt index d6850be8b2b..a8ab85cb8f1 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationId.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationId.kt @@ -2,34 +2,31 @@ package de.rki.coronawarnapp.eventregistration.events import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.QRCodePayload +import okio.ByteString import okio.ByteString.Companion.toByteString -import javax.inject.Inject -class TraceLocationId @Inject constructor() { +private const val CWA_GUID = "CWA-GUID" - /** - * Returns a byte sequence that serves as an identifier for the trace location. - * The ID is the byte representation of SHA-256 hash. - * - * @param qrCodePayload [QRCodePayload] - */ - fun locationId(qrCodePayload: QRCodePayload): ByteArray { +/** + * Returns a byte sequence that serves as an identifier for the trace location. + * The ID is the byte representation of SHA-256 hash. + * + * Please note that is an extension value and can not be lazily created here. + * it is the responsibility of the consumer to lazily call it to avoid multiple computations + */ +val QRCodePayload.locationId: ByteString + get() { val cwaDomain = CWA_GUID.toByteArray() - val payloadBytes = qrCodePayload.toByteArray() + val payloadBytes = toByteArray() val totalByteSequence = cwaDomain + payloadBytes - return totalByteSequence.toByteString().sha256().toByteArray() + return totalByteSequence.toByteString().sha256() } - /** - * Returns a byte sequence that serves as an identifier for the trace location. - * The ID is the byte representation of SHA-256 hash. - * - * @param traceLocation [TraceLocation] - */ - fun locationId(traceLocation: TraceLocation): ByteArray = - locationId(traceLocation.qrCodePayload()) - - companion object { - private const val CWA_GUID = "CWA-GUID" - } -} +/** + * Returns a byte sequence that serves as an identifier for the trace location. + * The ID is the byte representation of SHA-256 hash. + * + * Please note that is an extension value and can not be lazily created here. + * it is the responsibility of the consumer to lazily call it to avoid multiple computations + */ +val TraceLocation.locationId: ByteString get() = qrCodePayload().locationId diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrl.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrl.kt index cb87575df89..da690acca6e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrl.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrl.kt @@ -3,21 +3,16 @@ package de.rki.coronawarnapp.eventregistration.events import com.google.common.io.BaseEncoding import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation import de.rki.coronawarnapp.ui.eventregistration.organizer.details.QrCodeGenerator -import javax.inject.Inject -class TraceLocationUrl @Inject constructor() { +private const val AUTHORITY = "https://e.coronawarn.app?v=${TraceLocation.VERSION}#" - /** - * Return a url for [TraceLocation] to be used as an input for [QrCodeGenerator] - * URL format https://e.coronawarn.app?v=1#QR_CODE_PAYLOAD_BASE64URL - */ - fun locationUrl(traceLocation: TraceLocation): String { - val payloadBytes = traceLocation.qrCodePayload().toByteArray() +/** + * Return a url for [TraceLocation] to be used as an input for [QrCodeGenerator] + * URL format https://e.coronawarn.app?v=1#QR_CODE_PAYLOAD_BASE64URL + */ +val TraceLocation.locationUrl: String + get() { + val payloadBytes = qrCodePayload().toByteArray() val base64Url = BaseEncoding.base64Url().omitPadding().encode(payloadBytes) return AUTHORITY.plus(base64Url) } - - companion object { - private const val AUTHORITY = "https://e.coronawarn.app?v=${TraceLocation.VERSION}#" - } -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt index bd50ffcbe58..af2748280a9 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt @@ -8,6 +8,7 @@ import de.rki.coronawarnapp.eventregistration.checkins.CheckIn import de.rki.coronawarnapp.eventregistration.checkins.CheckInRepository import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation import de.rki.coronawarnapp.eventregistration.checkins.qrcode.VerifiedTraceLocation +import de.rki.coronawarnapp.eventregistration.events.locationId import de.rki.coronawarnapp.ui.durationpicker.toContactDiaryFormat import de.rki.coronawarnapp.ui.durationpicker.toReadableDuration import de.rki.coronawarnapp.ui.eventregistration.organizer.category.adapter.category.mapTraceLocationToTitleRes @@ -89,8 +90,10 @@ class ConfirmCheckInViewModel @AssistedInject constructor( completed: Boolean = false, createJournalEntry: Boolean = true ): CheckIn = CheckIn( - traceLocationId = verifiedTraceLocation.traceLocationID, - traceLocationIdHash = verifiedTraceLocation.traceLocationID.sha256(), + traceLocationId = verifiedTraceLocation.traceLocation.locationId, + // TODO verify this. According to the specs locationId is the byte representation of the SHA-256 hash. + // Does not make sense to have another column traceLocationIdHash + traceLocationIdHash = verifiedTraceLocation.traceLocation.locationId, version = traceLocation.version, type = traceLocation.type.number, description = traceLocation.description, diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt index 072e083e3ab..e4ff1863be5 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt @@ -6,7 +6,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation -import de.rki.coronawarnapp.eventregistration.events.TraceLocationUrl +import de.rki.coronawarnapp.eventregistration.events.locationUrl import de.rki.coronawarnapp.eventregistration.storage.repo.DefaultTraceLocationRepository import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.ui.SingleLiveEvent @@ -22,7 +22,6 @@ class QrCodeDetailViewModel @AssistedInject constructor( @Assisted private val traceLocationId: Long?, private val dispatcher: DispatcherProvider, private val qrCodeGenerator: QrCodeGenerator, - private val traceLocationUrl: TraceLocationUrl, private val traceLocationRepository: DefaultTraceLocationRepository ) : CWAViewModel() { @@ -91,7 +90,7 @@ class QrCodeDetailViewModel @AssistedInject constructor( */ private fun createQrCode(traceLocation: TraceLocation) = launch(context = dispatcher.IO) { try { - val input = traceLocationUrl.locationUrl(traceLocation) + val input = traceLocation.locationUrl Timber.d("input=$input") qrCodeBitmap.postValue(qrCodeGenerator.createQrCode(input)) } catch (e: Exception) { diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/attendee/confirm/ConfirmCheckInViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/attendee/confirm/ConfirmCheckInViewModelTest.kt index 054e7b94736..bf53eca3bea 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/attendee/confirm/ConfirmCheckInViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/attendee/confirm/ConfirmCheckInViewModelTest.kt @@ -10,6 +10,7 @@ import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK +import org.joda.time.Instant import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -33,6 +34,7 @@ class ConfirmCheckInViewModelTest : BaseTest() { every { verifiedTraceLocation.traceLocation } returns traceLocation every { traceLocation.defaultCheckInLengthInMinutes } returns 10 + every { timeStamper.nowUTC } returns Instant.parse("2021-03-04T10:30:00Z") viewModel = ConfirmCheckInViewModel( verifiedTraceLocation = verifiedTraceLocation, @@ -49,8 +51,7 @@ class ConfirmCheckInViewModelTest : BaseTest() { @Test fun onConfirmEvent() { - // TODO -// viewModel.onConfirmTraceLocation() -// viewModel.events.getOrAwaitValue() shouldBe ConfirmCheckInNavigation.ConfirmNavigation + viewModel.onConfirmTraceLocation() + viewModel.events.getOrAwaitValue() shouldBe ConfirmCheckInNavigation.ConfirmNavigation } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationIdTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationIdTest.kt index 4ff7ef412d7..663d47a9e0d 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationIdTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationIdTest.kt @@ -5,7 +5,6 @@ import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass import de.rki.coronawarnapp.util.TimeAndDateExtensions.secondsToInstant import io.kotest.matchers.shouldBe import okio.ByteString.Companion.decodeBase64 -import okio.ByteString.Companion.toByteString import org.junit.jupiter.api.Test import testhelpers.BaseTest @@ -19,8 +18,7 @@ class TraceLocationIdTest : BaseTest() { val qrCodePayload = TraceLocationOuterClass.QRCodePayload.parseFrom( qrCodePayloadBase64.decodeBase64()!!.toByteArray() ) - createInstance().locationId(qrCodePayload).toByteString().base64() shouldBe - "jNcJTCajd9Sen6Tbexl2Yb7O3J7ps47b6k4+QMT4xS0=" + qrCodePayload.locationId.base64() shouldBe "jNcJTCajd9Sen6Tbexl2Yb7O3J7ps47b6k4+QMT4xS0=" } @Test @@ -31,8 +29,7 @@ class TraceLocationIdTest : BaseTest() { val qrCodePayload = TraceLocationOuterClass.QRCodePayload.parseFrom( qrCodePayloadBase64.decodeBase64()!!.toByteArray() ) - createInstance().locationId(qrCodePayload).toByteString().base64() shouldBe - "GMuCjqNmOdYyrFhyvFNTVEeLaZh+uShgUoY0LYJo4YQ=" + qrCodePayload.locationId.base64() shouldBe "GMuCjqNmOdYyrFhyvFNTVEeLaZh+uShgUoY0LYJo4YQ=" } @Test @@ -49,8 +46,7 @@ class TraceLocationIdTest : BaseTest() { cnPublicKey = PUB_KEY, version = TraceLocation.VERSION ) - createInstance().locationId(traceLocation).toByteString().base64() shouldBe - "jNcJTCajd9Sen6Tbexl2Yb7O3J7ps47b6k4+QMT4xS0=" + traceLocation.locationId.base64() shouldBe "jNcJTCajd9Sen6Tbexl2Yb7O3J7ps47b6k4+QMT4xS0=" } @Test @@ -68,12 +64,9 @@ class TraceLocationIdTest : BaseTest() { version = TraceLocation.VERSION ) - createInstance().locationId(traceLocation).toByteString().base64() shouldBe - "GMuCjqNmOdYyrFhyvFNTVEeLaZh+uShgUoY0LYJo4YQ=" + traceLocation.locationId.base64() shouldBe "GMuCjqNmOdYyrFhyvFNTVEeLaZh+uShgUoY0LYJo4YQ=" } - private fun createInstance() = TraceLocationId() - companion object { private const val CRYPTOGRAPHIC_SEED = "MTIzNA==" private const val PUB_KEY = diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlTest.kt index fd2ab811c11..aee6965e78d 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrlTest.kt @@ -26,7 +26,7 @@ class TraceLocationUrlTest : BaseTest() { version = TraceLocation.VERSION ) - createInstance().locationUrl(traceLocation) shouldBe + traceLocation.locationUrl shouldBe "https://e.coronawarn.app?v=1#CAESLAgBEhFNeSBCaXJ0aGRheSBQYXJ0eRoLYXQgbXkgcGxhY2Uo04ekATD3h6QBGmoIAR" + "JgOMTa6eYSiaDv8lW13xdYEvGHOZ1EYTiFSxt51HEoPCD7CNnvCUiIYPhax1MpkN0UfNClCm9ZWYy0JH01CDVD9eq-voxQ1EcFJ" + "QkEIujVwoCNK0MNGuDK1ayjGxeDc4UDGgQxMjM0IgQIARAC" @@ -47,14 +47,12 @@ class TraceLocationUrlTest : BaseTest() { version = TraceLocation.VERSION ) - createInstance().locationUrl(traceLocation) shouldBe + traceLocation.locationUrl shouldBe "https://e.coronawarn.app?v=1#CAESIAgBEg1JY2VjcmVhbSBTaG9wGg1NYWluIFN0cmVldCAxGmoIARJgOMTa6eYSiaDv8l" + "W13xdYEvGHOZ1EYTiFSxt51HEoPCD7CNnvCUiIYPhax1MpkN0UfNClCm9ZWYy0JH01CDVD9eq-voxQ1EcFJQkEIujVwoCNK0MNG" + "uDK1ayjGxeDc4UDGgQxMjM0IgYIARABGAo" } - private fun createInstance() = TraceLocationUrl() - companion object { private const val CRYPTOGRAPHIC_SEED = "MTIzNA==" private const val PUB_KEY = From 33338fb6ebdd2f4e3b6f7c72ebdb67bb34dde19c Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Mon, 5 Apr 2021 18:31:00 +0200 Subject: [PATCH 24/43] Move location id and url to TraceLocation and create them lazily --- .../qrcode/VerifiedTraceLocationTest.kt | 2 -- .../EventRegistrationTestFragmentViewModel.kt | 2 -- .../qrcode}/QrCodePayload.kt | 21 ++++++++++-- .../checkins/qrcode/TraceLocation.kt | 30 +++++++++++++++++ .../checkins/qrcode/VerifiedTraceLocation.kt | 19 ----------- .../events/TraceLocationId.kt | 32 ------------------- .../events/TraceLocationUrl.kt | 18 ----------- .../confirm/ConfirmCheckInViewModel.kt | 1 - .../details/QrCodeDetailViewModel.kt | 1 - .../events/QrCodePayloadTest.kt | 1 + .../events/TraceLocationIdTest.kt | 5 +-- 11 files changed, 53 insertions(+), 79 deletions(-) rename Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/{events => checkins/qrcode}/QrCodePayload.kt (63%) delete mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationId.kt delete mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrl.kt diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocationTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocationTest.kt index b71ffa22d51..c1cf8a86e62 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocationTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocationTest.kt @@ -1,7 +1,5 @@ package de.rki.coronawarnapp.eventregistration.checkins.qrcode -import de.rki.coronawarnapp.eventregistration.events.locationId -import de.rki.coronawarnapp.eventregistration.events.qrCodePayload import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass import io.kotest.matchers.shouldBe import okio.ByteString.Companion.decodeBase64 diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt index 4ac4abfd5f8..ef817264dbc 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt @@ -8,8 +8,6 @@ import dagger.assisted.AssistedInject import de.rki.coronawarnapp.eventregistration.checkins.CheckIn import de.rki.coronawarnapp.eventregistration.checkins.CheckInRepository import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation -import de.rki.coronawarnapp.eventregistration.events.locationId -import de.rki.coronawarnapp.eventregistration.events.locationUrl import de.rki.coronawarnapp.eventregistration.storage.repo.TraceLocationRepository import de.rki.coronawarnapp.presencetracing.risk.CheckInWarningMatcher import de.rki.coronawarnapp.presencetracing.risk.CheckInWarningOverlap diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/QrCodePayload.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QrCodePayload.kt similarity index 63% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/QrCodePayload.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QrCodePayload.kt index 2bd51c53186..51ab7334faa 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/QrCodePayload.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QrCodePayload.kt @@ -1,13 +1,15 @@ -package de.rki.coronawarnapp.eventregistration.events +package de.rki.coronawarnapp.eventregistration.checkins.qrcode -import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.CWALocationData import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.CrowdNotifierData import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.QRCodePayload import de.rki.coronawarnapp.util.TimeAndDateExtensions.seconds +import de.rki.coronawarnapp.util.TimeAndDateExtensions.secondsToInstant +import de.rki.coronawarnapp.util.toOkioByteString import de.rki.coronawarnapp.util.toProtoByteString import okio.ByteString.Companion.decodeBase64 +import okio.ByteString.Companion.toByteString fun TraceLocation.qrCodePayload(): QRCodePayload { val vendorData = CWALocationData.newBuilder() @@ -36,3 +38,18 @@ fun TraceLocation.qrCodePayload(): QRCodePayload { .setVersion(TraceLocation.VERSION) .build() } + +fun QRCodePayload.traceLocation(): TraceLocation { + val cwaLocationData = CWALocationData.parseFrom(vendorData) + return TraceLocation( + version = version, + type = cwaLocationData.type, + defaultCheckInLengthInMinutes = cwaLocationData.defaultCheckInLengthInMinutes, + description = locationData.description, + address = locationData.address, + startDate = locationData.startTimestamp.secondsToInstant(), + endDate = locationData.endTimestamp.secondsToInstant(), + cryptographicSeed = crowdNotifierData.cryptographicSeed.toByteArray().toByteString(), + cnPublicKey = crowdNotifierData.publicKey.toOkioByteString().base64() + ) +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/TraceLocation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/TraceLocation.kt index 3e8463cb334..117d3663ea2 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/TraceLocation.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/TraceLocation.kt @@ -1,11 +1,15 @@ package de.rki.coronawarnapp.eventregistration.checkins.qrcode import android.os.Parcelable +import com.google.common.io.BaseEncoding import de.rki.coronawarnapp.eventregistration.storage.entity.TraceLocationEntity import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass +import de.rki.coronawarnapp.ui.eventregistration.organizer.details.QrCodeGenerator +import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize import okio.ByteString import okio.ByteString.Companion.decodeBase64 +import okio.ByteString.Companion.toByteString import org.joda.time.Instant @Parcelize @@ -22,6 +26,29 @@ data class TraceLocation( val version: Int = VERSION, ) : Parcelable { + /** + * Return a url for [TraceLocation] to be used as an input for [QrCodeGenerator] + * URL format https://e.coronawarn.app?v=1#QR_CODE_PAYLOAD_BASE64URL + */ + @IgnoredOnParcel + val locationUrl: String by lazy { + val payloadBytes = qrCodePayload().toByteArray() + val base64Url = BaseEncoding.base64Url().omitPadding().encode(payloadBytes) + AUTHORITY.plus(base64Url) + } + + /** + * Returns a byte sequence that serves as an identifier for the trace location. + * The ID is the byte representation of SHA-256 hash. + */ + @IgnoredOnParcel + val locationId: ByteString by lazy { + val cwaDomain = CWA_GUID.toByteArray() + val payloadBytes = qrCodePayload().toByteArray() + val totalByteSequence = cwaDomain + payloadBytes + totalByteSequence.toByteString().sha256() + } + fun isBeforeStartTime(now: Instant): Boolean = startDate?.isAfter(now) ?: false fun isAfterEndTime(now: Instant): Boolean = endDate?.isBefore(now) ?: false @@ -31,6 +58,9 @@ data class TraceLocation( * Trace location version. This is a static data and not calculated from [TraceLocation] */ const val VERSION = 1 + + private const val AUTHORITY = "https://e.coronawarn.app?v=$VERSION#" + private const val CWA_GUID = "CWA-GUID" } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocation.kt index 75f64e2c798..b524577b562 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocation.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocation.kt @@ -3,15 +3,11 @@ package de.rki.coronawarnapp.eventregistration.checkins.qrcode import android.os.Parcel import android.os.Parcelable import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass -import de.rki.coronawarnapp.util.TimeAndDateExtensions.secondsToInstant -import de.rki.coronawarnapp.util.toOkioByteString import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parceler import kotlinx.parcelize.Parcelize import kotlinx.parcelize.TypeParceler -import okio.ByteString.Companion.toByteString - @Parcelize @TypeParceler() @TypeParceler() @@ -19,21 +15,6 @@ data class VerifiedTraceLocation( private val protoQrCodePayload: TraceLocationOuterClass.QRCodePayload ) : Parcelable { @IgnoredOnParcel val traceLocation: TraceLocation = protoQrCodePayload.traceLocation() - - private fun TraceLocationOuterClass.QRCodePayload.traceLocation(): TraceLocation { - val cwaLocationData = TraceLocationOuterClass.CWALocationData.parseFrom(protoQrCodePayload.vendorData) - return TraceLocation( - version = version, - type = cwaLocationData.type, - defaultCheckInLengthInMinutes = cwaLocationData.defaultCheckInLengthInMinutes, - description = locationData.description, - address = locationData.address, - startDate = locationData.startTimestamp.secondsToInstant(), - endDate = locationData.endTimestamp.secondsToInstant(), - cryptographicSeed = crowdNotifierData.cryptographicSeed.toByteArray().toByteString(), - cnPublicKey = crowdNotifierData.publicKey.toOkioByteString().base64() - ) - } } private object TraceLocationParceler : Parceler { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationId.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationId.kt deleted file mode 100644 index a8ab85cb8f1..00000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationId.kt +++ /dev/null @@ -1,32 +0,0 @@ -package de.rki.coronawarnapp.eventregistration.events - -import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation -import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.QRCodePayload -import okio.ByteString -import okio.ByteString.Companion.toByteString - -private const val CWA_GUID = "CWA-GUID" - -/** - * Returns a byte sequence that serves as an identifier for the trace location. - * The ID is the byte representation of SHA-256 hash. - * - * Please note that is an extension value and can not be lazily created here. - * it is the responsibility of the consumer to lazily call it to avoid multiple computations - */ -val QRCodePayload.locationId: ByteString - get() { - val cwaDomain = CWA_GUID.toByteArray() - val payloadBytes = toByteArray() - val totalByteSequence = cwaDomain + payloadBytes - return totalByteSequence.toByteString().sha256() - } - -/** - * Returns a byte sequence that serves as an identifier for the trace location. - * The ID is the byte representation of SHA-256 hash. - * - * Please note that is an extension value and can not be lazily created here. - * it is the responsibility of the consumer to lazily call it to avoid multiple computations - */ -val TraceLocation.locationId: ByteString get() = qrCodePayload().locationId diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrl.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrl.kt deleted file mode 100644 index da690acca6e..00000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationUrl.kt +++ /dev/null @@ -1,18 +0,0 @@ -package de.rki.coronawarnapp.eventregistration.events - -import com.google.common.io.BaseEncoding -import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation -import de.rki.coronawarnapp.ui.eventregistration.organizer.details.QrCodeGenerator - -private const val AUTHORITY = "https://e.coronawarn.app?v=${TraceLocation.VERSION}#" - -/** - * Return a url for [TraceLocation] to be used as an input for [QrCodeGenerator] - * URL format https://e.coronawarn.app?v=1#QR_CODE_PAYLOAD_BASE64URL - */ -val TraceLocation.locationUrl: String - get() { - val payloadBytes = qrCodePayload().toByteArray() - val base64Url = BaseEncoding.base64Url().omitPadding().encode(payloadBytes) - return AUTHORITY.plus(base64Url) - } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt index af2748280a9..c6c42c48e95 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt @@ -8,7 +8,6 @@ import de.rki.coronawarnapp.eventregistration.checkins.CheckIn import de.rki.coronawarnapp.eventregistration.checkins.CheckInRepository import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation import de.rki.coronawarnapp.eventregistration.checkins.qrcode.VerifiedTraceLocation -import de.rki.coronawarnapp.eventregistration.events.locationId import de.rki.coronawarnapp.ui.durationpicker.toContactDiaryFormat import de.rki.coronawarnapp.ui.durationpicker.toReadableDuration import de.rki.coronawarnapp.ui.eventregistration.organizer.category.adapter.category.mapTraceLocationToTitleRes diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt index e4ff1863be5..863daf7a12b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt @@ -6,7 +6,6 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation -import de.rki.coronawarnapp.eventregistration.events.locationUrl import de.rki.coronawarnapp.eventregistration.storage.repo.DefaultTraceLocationRepository import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.ui.SingleLiveEvent diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/QrCodePayloadTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/QrCodePayloadTest.kt index 98881b794ea..683a6456398 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/QrCodePayloadTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/QrCodePayloadTest.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.eventregistration.events import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.qrCodePayload import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass import de.rki.coronawarnapp.util.TimeAndDateExtensions.secondsToInstant import io.kotest.matchers.shouldBe diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationIdTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationIdTest.kt index 663d47a9e0d..bc1c62af21a 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationIdTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationIdTest.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.eventregistration.events import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.traceLocation import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass import de.rki.coronawarnapp.util.TimeAndDateExtensions.secondsToInstant import io.kotest.matchers.shouldBe @@ -18,7 +19,7 @@ class TraceLocationIdTest : BaseTest() { val qrCodePayload = TraceLocationOuterClass.QRCodePayload.parseFrom( qrCodePayloadBase64.decodeBase64()!!.toByteArray() ) - qrCodePayload.locationId.base64() shouldBe "jNcJTCajd9Sen6Tbexl2Yb7O3J7ps47b6k4+QMT4xS0=" + qrCodePayload.traceLocation().locationId.base64() shouldBe "jNcJTCajd9Sen6Tbexl2Yb7O3J7ps47b6k4+QMT4xS0=" } @Test @@ -29,7 +30,7 @@ class TraceLocationIdTest : BaseTest() { val qrCodePayload = TraceLocationOuterClass.QRCodePayload.parseFrom( qrCodePayloadBase64.decodeBase64()!!.toByteArray() ) - qrCodePayload.locationId.base64() shouldBe "GMuCjqNmOdYyrFhyvFNTVEeLaZh+uShgUoY0LYJo4YQ=" + qrCodePayload.traceLocation().locationId.base64() shouldBe "GMuCjqNmOdYyrFhyvFNTVEeLaZh+uShgUoY0LYJo4YQ=" } @Test From c4bad2cc4e0ee904b4ceeb30c65dd5b00e18b84e Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Mon, 5 Apr 2021 18:54:41 +0200 Subject: [PATCH 25/43] Update ConfirmCheckInViewModelTest.kt --- .../confirm/ConfirmCheckInViewModelTest.kt | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/attendee/confirm/ConfirmCheckInViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/attendee/confirm/ConfirmCheckInViewModelTest.kt index bf53eca3bea..aee24dd7643 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/attendee/confirm/ConfirmCheckInViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/attendee/confirm/ConfirmCheckInViewModelTest.kt @@ -3,13 +3,17 @@ package de.rki.coronawarnapp.eventregistration.attendee.confirm import de.rki.coronawarnapp.eventregistration.checkins.CheckInRepository import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation import de.rki.coronawarnapp.eventregistration.checkins.qrcode.VerifiedTraceLocation +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass import de.rki.coronawarnapp.ui.eventregistration.attendee.confirm.ConfirmCheckInNavigation import de.rki.coronawarnapp.ui.eventregistration.attendee.confirm.ConfirmCheckInViewModel +import de.rki.coronawarnapp.util.TimeAndDateExtensions.secondsToInstant import de.rki.coronawarnapp.util.TimeStamper import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations +import io.mockk.coEvery import io.mockk.every import io.mockk.impl.annotations.MockK +import okio.ByteString.Companion.decodeBase64 import org.joda.time.Instant import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -21,19 +25,31 @@ import testhelpers.extensions.getOrAwaitValue @ExtendWith(InstantExecutorExtension::class) class ConfirmCheckInViewModelTest : BaseTest() { - @MockK lateinit var traceLocation: TraceLocation @MockK lateinit var verifiedTraceLocation: VerifiedTraceLocation @MockK lateinit var checkInRepository: CheckInRepository @MockK lateinit var timeStamper: TimeStamper private lateinit var viewModel: ConfirmCheckInViewModel + private val traceLocation = TraceLocation( + id = 1, + type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_OTHER, + description = "My Birthday Party", + address = "at my place", + startDate = 2687955L.secondsToInstant(), + endDate = 2687991L.secondsToInstant(), + defaultCheckInLengthInMinutes = null, + cryptographicSeed = "CRYPTOGRAPHIC_SEED".decodeBase64()!!, + cnPublicKey = "PUB_KEY", + version = TraceLocation.VERSION + ) + @BeforeEach fun setUp() { MockKAnnotations.init(this) + coEvery { checkInRepository.addCheckIn(any()) } returns 1L every { verifiedTraceLocation.traceLocation } returns traceLocation - every { traceLocation.defaultCheckInLengthInMinutes } returns 10 every { timeStamper.nowUTC } returns Instant.parse("2021-03-04T10:30:00Z") viewModel = ConfirmCheckInViewModel( From 45af8dda62607f0af7392665cede94fe40185f2a Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Mon, 5 Apr 2021 19:05:49 +0200 Subject: [PATCH 26/43] Update test screen --- .../ui/EventRegistrationTestFragment.kt | 19 ++++++------ .../EventRegistrationTestFragmentViewModel.kt | 30 ++++--------------- 2 files changed, 16 insertions(+), 33 deletions(-) diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt index 0ecd5261be2..c02d24b1d32 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt @@ -14,6 +14,7 @@ import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentTestEventregistrationBinding +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation import de.rki.coronawarnapp.test.menu.ui.TestMenuItem import de.rki.coronawarnapp.util.ContextExtensions.getColorCompat import de.rki.coronawarnapp.util.di.AutoInject @@ -88,28 +89,28 @@ class EventRegistrationTestFragment : Fragment(R.layout.fragment_test_eventregis viewModel.lastOrganiserLocation.observe(viewLifecycleOwner) { binding.lastOrganiserLocationCard.isVisible = it != null - it?.let { data -> + it?.let { traceLocation -> with(binding) { - lastOrganiserLocation.text = traceLocation(data) - lastOrganiserLocationId.text = styleText("ID", data.id) - lastOrganiserLocationUrl.text = styleText("URL", data.url) + lastOrganiserLocation.text = traceLocationText(traceLocation) + lastOrganiserLocationId.text = styleText("ID", traceLocation.locationId) + lastOrganiserLocationUrl.text = styleText("URL", traceLocation.locationUrl) } } } viewModel.lastAttendeeLocation.observe(viewLifecycleOwner) { binding.lastAttendeeLocationCard.isVisible = it != null - it?.let { data -> + it?.let { traceLocation -> with(binding) { - lastAttendeeLocation.text = traceLocation(data) - lastAttendeeLocationId.text = styleText("ID", data.id) - lastAttendeeLocationUrl.text = styleText("URL", data.url) + lastAttendeeLocation.text = traceLocationText(traceLocation) + lastAttendeeLocationId.text = styleText("ID", traceLocation.locationId) + lastAttendeeLocationUrl.text = styleText("URL", traceLocation.locationUrl) } } } } - private fun traceLocation(data: LastLocationData): SpannedString = with(data.traceLocation) { + private fun traceLocationText(traceLocation: TraceLocation): SpannedString = with(traceLocation) { buildSpannedString { append("TraceLocation [\n") append(styleText("Id", id)) diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt index ef817264dbc..a44796fb212 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragmentViewModel.kt @@ -28,12 +28,12 @@ class EventRegistrationTestFragmentViewModel @AssistedInject constructor( private val presenceTracingRiskCalculator: PresenceTracingRiskCalculator ) : CWAViewModel(dispatcherProvider = dispatcherProvider) { - val lastOrganiserLocation: LiveData = + val lastOrganiserLocation: LiveData = traceLocationRepository.allTraceLocations .map { lastLocationData(it) } .asLiveData(dispatcherProvider.Default) - val lastAttendeeLocation: LiveData = + val lastAttendeeLocation: LiveData = checkInRepository.allCheckIns .map { lastAttendeeLocationData(it) } .asLiveData(dispatcherProvider.Default) @@ -107,19 +107,13 @@ class EventRegistrationTestFragmentViewModel @AssistedInject constructor( } } - private fun lastLocationData(it: List): LastLocationData? { - val traceLocation = it.maxByOrNull { traceLocation -> traceLocation.id } ?: return null - return LastLocationData( - traceLocation = traceLocation, - id = traceLocation.locationId.base64(), - url = traceLocation.locationUrl - ) - } + private fun lastLocationData(it: List): TraceLocation? = + it.maxByOrNull { traceLocation -> traceLocation.id } - private fun lastAttendeeLocationData(it: List): LastLocationData? { + private fun lastAttendeeLocationData(it: List): TraceLocation? { val checkIn = it.maxByOrNull { checkIn -> checkIn.id } ?: return null - val traceLocation = TraceLocation( + return TraceLocation( id = checkIn.id, type = TraceLocationOuterClass.TraceLocationType.forNumber(checkIn.type), description = checkIn.description, @@ -131,20 +125,8 @@ class EventRegistrationTestFragmentViewModel @AssistedInject constructor( cnPublicKey = checkIn.cnPublicKey, version = checkIn.version ) - - return LastLocationData( - traceLocation = traceLocation, - id = traceLocation.locationId.base64(), - url = traceLocation.locationUrl - ) } @AssistedFactory interface Factory : SimpleCWAViewModelFactory } - -data class LastLocationData( - val traceLocation: TraceLocation, - val id: String, - val url: String -) From ac32bb252782306b6d98c797b0de9b03abf799bf Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Mon, 5 Apr 2021 19:10:04 +0200 Subject: [PATCH 27/43] Base64 Id for readability --- .../eventregistration/ui/EventRegistrationTestFragment.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt index c02d24b1d32..dc916befbe2 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/EventRegistrationTestFragment.kt @@ -92,7 +92,7 @@ class EventRegistrationTestFragment : Fragment(R.layout.fragment_test_eventregis it?.let { traceLocation -> with(binding) { lastOrganiserLocation.text = traceLocationText(traceLocation) - lastOrganiserLocationId.text = styleText("ID", traceLocation.locationId) + lastOrganiserLocationId.text = styleText("ID", traceLocation.locationId.base64()) lastOrganiserLocationUrl.text = styleText("URL", traceLocation.locationUrl) } } @@ -103,7 +103,7 @@ class EventRegistrationTestFragment : Fragment(R.layout.fragment_test_eventregis it?.let { traceLocation -> with(binding) { lastAttendeeLocation.text = traceLocationText(traceLocation) - lastAttendeeLocationId.text = styleText("ID", traceLocation.locationId) + lastAttendeeLocationId.text = styleText("ID", traceLocation.locationId.base64()) lastAttendeeLocationUrl.text = styleText("URL", traceLocation.locationUrl) } } From 28e0f67a4f0c77d22a005aa566f6bacecd6857cc Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Mon, 5 Apr 2021 19:14:16 +0200 Subject: [PATCH 28/43] Remove added descriptor --- .../appconfig/mapping/PresenceTracingConfigMapper.kt | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/PresenceTracingConfigMapper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/PresenceTracingConfigMapper.kt index e5a96d4d136..739a0e6fd00 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/PresenceTracingConfigMapper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/PresenceTracingConfigMapper.kt @@ -8,8 +8,6 @@ import de.rki.coronawarnapp.appconfig.PresenceTracingConfigContainer import de.rki.coronawarnapp.appconfig.PresenceTracingRiskCalculationParamContainer import de.rki.coronawarnapp.appconfig.PresenceTracingSubmissionParamContainer import de.rki.coronawarnapp.server.protocols.internal.v2.AppConfigAndroid -import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptor.PayloadEncoding -import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptor import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingSubmissionParameters import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingRiskCalculationParameters import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingParameters.QRCodeErrorCorrectionLevel @@ -94,14 +92,7 @@ class PresenceTracingConfigMapper @Inject constructor() : PresenceTracingConfig. riskCalculationParameters = riskCalculationParameters, submissionParameters = submissionParameters, plausibleDeniabilityParameters = plausibleDeniabilityParameters, - qrCodeDescriptors = qrCodeDescriptorsOrBuilderList + - // TODO remove - PresenceTracingQRCodeDescriptor.newBuilder() - .setEncodedPayloadGroupIndex(1) - .setPayloadEncoding(PayloadEncoding.BASE64) - .setRegexPattern("https://e\\.coronawarn\\.app\\?v=(\\d+)\\#(.+)") - .setVersionGroupIndex(0) - .build() + qrCodeDescriptors = qrCodeDescriptorsOrBuilderList ) } } From 615901fce5dedd3e9bb8020131bc341f079944d1 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Mon, 5 Apr 2021 19:43:31 +0200 Subject: [PATCH 29/43] Return null instead of 0 it does change the logic ,but how the usr sees it --- .../eventregistration/checkins/qrcode/QrCodePayload.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QrCodePayload.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QrCodePayload.kt index 51ab7334faa..25fa3475703 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QrCodePayload.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QrCodePayload.kt @@ -10,6 +10,7 @@ import de.rki.coronawarnapp.util.toOkioByteString import de.rki.coronawarnapp.util.toProtoByteString import okio.ByteString.Companion.decodeBase64 import okio.ByteString.Companion.toByteString +import org.joda.time.Instant fun TraceLocation.qrCodePayload(): QRCodePayload { val vendorData = CWALocationData.newBuilder() @@ -47,9 +48,11 @@ fun QRCodePayload.traceLocation(): TraceLocation { defaultCheckInLengthInMinutes = cwaLocationData.defaultCheckInLengthInMinutes, description = locationData.description, address = locationData.address, - startDate = locationData.startTimestamp.secondsToInstant(), - endDate = locationData.endTimestamp.secondsToInstant(), + startDate = locationData.startTimestamp.instant(), + endDate = locationData.endTimestamp.instant(), cryptographicSeed = crowdNotifierData.cryptographicSeed.toByteArray().toByteString(), cnPublicKey = crowdNotifierData.publicKey.toOkioByteString().base64() ) } + +private fun Long.instant(): Instant? = if (this == 0L) null else secondsToInstant() From c5aa6295bfb2886501356b503f4226ccecbd556f Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Mon, 5 Apr 2021 23:31:32 +0200 Subject: [PATCH 30/43] Use Okio extension --- .../eventregistration/checkins/qrcode/QrCodePayload.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QrCodePayload.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QrCodePayload.kt index 25fa3475703..7ac7412993b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QrCodePayload.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QrCodePayload.kt @@ -9,7 +9,6 @@ import de.rki.coronawarnapp.util.TimeAndDateExtensions.secondsToInstant import de.rki.coronawarnapp.util.toOkioByteString import de.rki.coronawarnapp.util.toProtoByteString import okio.ByteString.Companion.decodeBase64 -import okio.ByteString.Companion.toByteString import org.joda.time.Instant fun TraceLocation.qrCodePayload(): QRCodePayload { @@ -50,7 +49,7 @@ fun QRCodePayload.traceLocation(): TraceLocation { address = locationData.address, startDate = locationData.startTimestamp.instant(), endDate = locationData.endTimestamp.instant(), - cryptographicSeed = crowdNotifierData.cryptographicSeed.toByteArray().toByteString(), + cryptographicSeed = crowdNotifierData.cryptographicSeed.toOkioByteString(), cnPublicKey = crowdNotifierData.publicKey.toOkioByteString().base64() ) } From bc44252d0babaf4a4cdd0ccb5a8062981fa50678 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Mon, 5 Apr 2021 23:31:44 +0200 Subject: [PATCH 31/43] Add parcelization test --- .../qrcode/VerifiedTraceLocationTest.kt | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocationTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocationTest.kt index c1cf8a86e62..ba6ad03a786 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocationTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocationTest.kt @@ -1,5 +1,7 @@ package de.rki.coronawarnapp.eventregistration.checkins.qrcode +import android.os.Bundle +import android.os.Parcel import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass import io.kotest.matchers.shouldBe import okio.ByteString.Companion.decodeBase64 @@ -42,4 +44,36 @@ class VerifiedTraceLocationTest : BaseTestInstrumentation() { qrCodePayload() shouldBe qrCodePayload } } + + @Test + fun parcelization() { + val base64Payload = "CAESIAgBEg1JY2VjcmVhbSBTaG9wGg1NYWluIFN0cmVldCAxGmUIARJ" + + "bMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7DEstcUIRcyk35OYDJ95/hTg3UVhsaDXKT" + + "0zK7NhHPXoyzipEnOp3GyNXDVpaPi3cAfQmxeuFMZAIX2+6A5XhoEMTIzNCIGCAEQARgK" + + val qrCodePayload = TraceLocationOuterClass.QRCodePayload.parseFrom( + base64Payload.decodeBase64()!!.toByteArray() + ) + + val expectedVerifiedLocation = VerifiedTraceLocation(qrCodePayload) + + val bundle = Bundle().apply { + putParcelable("verifiedTraceLocation", expectedVerifiedLocation) + } + + val parcelRaw = Parcel.obtain().apply { + writeBundle(bundle) + }.marshall() + + val restoredParcel = Parcel.obtain().apply { + unmarshall(parcelRaw, 0, parcelRaw.size) + setDataPosition(0) + } + + val restoredData = restoredParcel.readBundle()!!.run { + classLoader = VerifiedTraceLocation::class.java.classLoader + getParcelable("verifiedTraceLocation") + } + restoredData shouldBe expectedVerifiedLocation + } } From 1ef8fa1ca30017720898ee93e9aa2a7610a8049d Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Mon, 5 Apr 2021 23:36:05 +0200 Subject: [PATCH 32/43] Update VerifiedTraceLocationTest.kt --- .../qrcode/VerifiedTraceLocationTest.kt | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocationTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocationTest.kt index ba6ad03a786..2e78d0912c0 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocationTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/VerifiedTraceLocationTest.kt @@ -14,45 +14,34 @@ import testhelpers.BaseTestInstrumentation class VerifiedTraceLocationTest : BaseTestInstrumentation() { @Test fun verifyTraceLocationMapping1() { - val base64Payload = "CAESLAgBEhFNeSBCaXJ0aGRheSBQYXJ0eRoLYXQgbXkgcGxhY2Uo04ekAT" + - "D3h6QBGmUIARJbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7DEst" + - "cUIRcyk35OYDJ95/hTg3UVhsaDXKT0zK7NhHPXoyzipEnOp3GyNXDVpaPi3" + - "cAfQmxeuFMZAIX2+6A5XhoEMTIzNCIECAEQAg==" - val qrCodePayload = TraceLocationOuterClass.QRCodePayload.parseFrom( - base64Payload.decodeBase64()!!.toByteArray() + BASE64_PAYLOAD_1.decodeBase64()!!.toByteArray() ) - VerifiedTraceLocation(qrCodePayload).traceLocation.apply { - locationId.base64() shouldBe "jNcJTCajd9Sen6Tbexl2Yb7O3J7ps47b6k4+QMT4xS0=" - qrCodePayload() shouldBe qrCodePayload - } + VerifiedTraceLocation(qrCodePayload).traceLocation + .apply { + locationId.base64() shouldBe "jNcJTCajd9Sen6Tbexl2Yb7O3J7ps47b6k4+QMT4xS0=" + qrCodePayload() shouldBe qrCodePayload + } } @Test fun verifyTraceLocationMapping2() { - val base64Payload = "CAESIAgBEg1JY2VjcmVhbSBTaG9wGg1NYWluIFN0cmVldCAxGmUIARJ" + - "bMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7DEstcUIRcyk35OYDJ95/hTg3UVhsaDXKT" + - "0zK7NhHPXoyzipEnOp3GyNXDVpaPi3cAfQmxeuFMZAIX2+6A5XhoEMTIzNCIGCAEQARgK" - val qrCodePayload = TraceLocationOuterClass.QRCodePayload.parseFrom( - base64Payload.decodeBase64()!!.toByteArray() + BASE64_PAYLOAD_2.decodeBase64()!!.toByteArray() ) - VerifiedTraceLocation(qrCodePayload).traceLocation.apply { - locationId.base64() shouldBe "GMuCjqNmOdYyrFhyvFNTVEeLaZh+uShgUoY0LYJo4YQ=" - qrCodePayload() shouldBe qrCodePayload - } + VerifiedTraceLocation(qrCodePayload).traceLocation + .apply { + locationId.base64() shouldBe "GMuCjqNmOdYyrFhyvFNTVEeLaZh+uShgUoY0LYJo4YQ=" + qrCodePayload() shouldBe qrCodePayload + } } @Test fun parcelization() { - val base64Payload = "CAESIAgBEg1JY2VjcmVhbSBTaG9wGg1NYWluIFN0cmVldCAxGmUIARJ" + - "bMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7DEstcUIRcyk35OYDJ95/hTg3UVhsaDXKT" + - "0zK7NhHPXoyzipEnOp3GyNXDVpaPi3cAfQmxeuFMZAIX2+6A5XhoEMTIzNCIGCAEQARgK" - val qrCodePayload = TraceLocationOuterClass.QRCodePayload.parseFrom( - base64Payload.decodeBase64()!!.toByteArray() + BASE64_PAYLOAD_2.decodeBase64()!!.toByteArray() ) val expectedVerifiedLocation = VerifiedTraceLocation(qrCodePayload) @@ -76,4 +65,15 @@ class VerifiedTraceLocationTest : BaseTestInstrumentation() { } restoredData shouldBe expectedVerifiedLocation } + + companion object { + private const val BASE64_PAYLOAD_1 = "CAESLAgBEhFNeSBCaXJ0aGRheSBQYXJ0eRoLYXQgbXkgcGxhY2Uo04ekAT" + + "D3h6QBGmUIARJbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7DEst" + + "cUIRcyk35OYDJ95/hTg3UVhsaDXKT0zK7NhHPXoyzipEnOp3GyNXDVpaPi3" + + "cAfQmxeuFMZAIX2+6A5XhoEMTIzNCIECAEQAg==" + + private const val BASE64_PAYLOAD_2 = "CAESIAgBEg1JY2VjcmVhbSBTaG9wGg1NYWluIFN0cmVldCAxGmUIARJ" + + "bMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7DEstcUIRcyk35OYDJ95/hTg3UVhsaDXKT" + + "0zK7NhHPXoyzipEnOp3GyNXDVpaPi3cAfQmxeuFMZAIX2+6A5XhoEMTIzNCIGCAEQARgK" + } } From 71be2dd8f2eb65ff0937124ef06efe6d44a3c724 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Tue, 6 Apr 2021 09:04:34 +0200 Subject: [PATCH 33/43] Provide locationId hash --- .../confirm/ConfirmCheckInViewModel.kt | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt index c6c42c48e95..11e3be37f5a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt @@ -88,25 +88,27 @@ class ConfirmCheckInViewModel @AssistedInject constructor( ), completed: Boolean = false, createJournalEntry: Boolean = true - ): CheckIn = CheckIn( - traceLocationId = verifiedTraceLocation.traceLocation.locationId, - // TODO verify this. According to the specs locationId is the byte representation of the SHA-256 hash. - // Does not make sense to have another column traceLocationIdHash - traceLocationIdHash = verifiedTraceLocation.traceLocation.locationId, - version = traceLocation.version, - type = traceLocation.type.number, - description = traceLocation.description, - address = traceLocation.address, - traceLocationStart = traceLocation.startDate, - traceLocationEnd = traceLocation.endDate, - defaultCheckInLengthInMinutes = traceLocation.defaultCheckInLengthInMinutes, - cryptographicSeed = traceLocation.cryptographicSeed, - cnPublicKey = traceLocation.cnPublicKey, - checkInStart = checkInStart, - checkInEnd = checkInEnd, - completed = completed, - createJournalEntry = createJournalEntry, - ) + ): CheckIn { + val traceLocationId = verifiedTraceLocation.traceLocation.locationId + return CheckIn( + traceLocationId = traceLocationId, + // sha256() Hash of locationId ,which is itself a sha256() Hash + traceLocationIdHash = traceLocationId.sha256(), + version = traceLocation.version, + type = traceLocation.type.number, + description = traceLocation.description, + address = traceLocation.address, + traceLocationStart = traceLocation.startDate, + traceLocationEnd = traceLocation.endDate, + defaultCheckInLengthInMinutes = traceLocation.defaultCheckInLengthInMinutes, + cryptographicSeed = traceLocation.cryptographicSeed, + cnPublicKey = traceLocation.cnPublicKey, + checkInStart = checkInStart, + checkInEnd = checkInEnd, + completed = completed, + createJournalEntry = createJournalEntry, + ) + } @AssistedFactory interface Factory : CWAViewModelFactory { From b5d0fa6ddd8500f1817d4eb34c1922d52d2a1138 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Tue, 6 Apr 2021 10:23:55 +0200 Subject: [PATCH 34/43] Delete DefaultQRCodeVerifierTest2.kt --- .../qrcode/DefaultQRCodeVerifierTest2.kt | 64 ------------------- 1 file changed, 64 deletions(-) delete mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/DefaultQRCodeVerifierTest2.kt diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/DefaultQRCodeVerifierTest2.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/DefaultQRCodeVerifierTest2.kt deleted file mode 100644 index 5a767d467dc..00000000000 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/DefaultQRCodeVerifierTest2.kt +++ /dev/null @@ -1,64 +0,0 @@ -package de.rki.coronawarnapp.eventregistration.checkins.qrcode - -import testhelpers.BaseTest - -class DefaultQRCodeVerifierTest2 : BaseTest() { - - /* Disabled, because new protobuf doesn't include signed traceLocation, we should write tests for the parsing of - QrCodePayload protobuf ... - - @Test - fun `protobuf decoding 1`() { - val signedTraceLocation = - TraceLocationOuterClass.SignedTraceLocation.parseFrom( - "BJLAUJBTGA2TKMZTGFRS2MRTGA3C2NBTMYZS2OJXGQZC2NTEHBTGCYRVGRSTQNBYCAARQARCCFGXSICCNFZHI2DEMF4SAUDBOJ2HSKQLMF2CA3LZEBYGYYLDMUYNHB5EAE4PPB5EAFAAAESGGBCAEIDFJJ7KHRO3ZZ2SFMJSBXSUY2ZZKGOIZS27L2D6VPKTA57M6RZY3MBCARR7LXAA2BY3IGNTHNFFAJSMIXF6PP4TEB3I2C3D7P32QUZHVVER" - .decodeBase32().toByteArray() - ) - - signedTraceLocation.apply { - TraceLocationOuterClass.TraceLocation.parseFrom(location).apply { - guid shouldBe "3055331c-2306-43f3-9742-6d8fab54e848" - version shouldBe 1 - typeValue shouldBe 2 - description shouldBe "My Birthday Party" - address shouldBe "at my place" - startTimestamp shouldBe 2687955 - endTimestamp shouldBe 2687991 - defaultCheckInLengthInMinutes shouldBe 0 - } - signature.toByteArray().toByteString() - .base64() shouldBe "MEQCIGVKfqPF2851IrEyDeVMazlRnIzLX16H6r1TB37PRzjbAiBGP13ADQcbQZsztKUCZMRcvnv5Mgdo0LY/v3qFMnrUkQ==" - } - - signedTraceLocation.location.toByteArray().toByteString() - .base64() shouldBe "CiQzMDU1MzMxYy0yMzA2LTQzZjMtOTc0Mi02ZDhmYWI1NGU4NDgQARgCIhFNeSBCaXJ0aGRheSBQYXJ0eSoLYXQgbXkgcGxhY2Uw04ekATj3h6QBQAA=" - } - - @Test - fun `protobuf decoding 2`() { - val signedTraceLocation = TraceLocationOuterClass.SignedTraceLocation.parseFrom( - "BJHAUJDGMNQTQNDCGM3S2NRRMMYC2NDBG5RS2YRSMY4C2OBSGVRWCZDEGUYDMY3GCAARQAJCBVEWGZLDOJSWC3JAKNUG64BKBVGWC2LOEBJXI4TFMV2CAMJQAA4AAQAKCJDDARACEA2ZCTGOF2HH2RQU7ODZMCSUTUBBNQYM6AR4NG6FFLC6ISXWEOI5UARADO44YYH3U53ZYL6IYM5DWALXUESAJNWRGRL5KLNLS5BM54SHDDCA" - .decodeBase32().toByteArray() - ) - - signedTraceLocation.apply { - TraceLocationOuterClass.TraceLocation.parseFrom(location).apply { - guid shouldBe "fca84b37-61c0-4a7c-b2f8-825cadd506cf" - version shouldBe 1 - typeValue shouldBe 1 - description shouldBe "Icecream Shop" - address shouldBe "Main Street 1" - startTimestamp shouldBe 0 - endTimestamp shouldBe 0 - defaultCheckInLengthInMinutes shouldBe 10 - } - signature.toByteArray().toByteString() - .base64() shouldBe "MEQCIDWRTM4ujn1GFPuHlgpUnQIWwwzwI8abxSrF5Er2I5HaAiAbucxg+6d3nC/Iwzo7AXehJAS20TRX1S2rl0LO8kcYxA==" - } - - signedTraceLocation.location.toByteArray().toByteString() - .base64() shouldBe "CiRmY2E4NGIzNy02MWMwLTRhN2MtYjJmOC04MjVjYWRkNTA2Y2YQARgBIg1JY2VjcmVhbSBTaG9wKg1NYWluIFN0cmVldCAxMAA4AEAK" - } - - */ -} From ebee84bbc9ef2e0be0bd91811c26539d194351b0 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Tue, 6 Apr 2021 12:23:50 +0200 Subject: [PATCH 35/43] Add traceLocationIdHash to TraceLocation --- .../eventregistration/checkins/qrcode/TraceLocation.kt | 8 ++++++++ .../attendee/confirm/ConfirmCheckInViewModel.kt | 7 +++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/TraceLocation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/TraceLocation.kt index 117d3663ea2..0669968f006 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/TraceLocation.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/TraceLocation.kt @@ -49,6 +49,14 @@ data class TraceLocation( totalByteSequence.toByteString().sha256() } + /** + * Returns SHA-256 hash of [locationId] which itself is SHA-256 hash + */ + @IgnoredOnParcel + val locationIdHash: ByteString by lazy { + locationId.sha256() + } + fun isBeforeStartTime(now: Instant): Boolean = startDate?.isAfter(now) ?: false fun isAfterEndTime(now: Instant): Boolean = endDate?.isBefore(now) ?: false diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt index 11e3be37f5a..685f2f245ba 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt @@ -89,11 +89,10 @@ class ConfirmCheckInViewModel @AssistedInject constructor( completed: Boolean = false, createJournalEntry: Boolean = true ): CheckIn { - val traceLocationId = verifiedTraceLocation.traceLocation.locationId + val traceLocation = verifiedTraceLocation.traceLocation return CheckIn( - traceLocationId = traceLocationId, - // sha256() Hash of locationId ,which is itself a sha256() Hash - traceLocationIdHash = traceLocationId.sha256(), + traceLocationId = traceLocation.locationId, + traceLocationIdHash = traceLocation.locationIdHash, version = traceLocation.version, type = traceLocation.type.number, description = traceLocation.description, From 0b631c418d27f22d2da328e8e7f7c1218d6698c5 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Tue, 6 Apr 2021 12:25:18 +0200 Subject: [PATCH 36/43] Rename --- .../java/de/rki/coronawarnapp/util/{ProtoBuff.kt => ProtoBuf.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/{ProtoBuff.kt => ProtoBuf.kt} (100%) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ProtoBuff.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ProtoBuf.kt similarity index 100% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ProtoBuff.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ProtoBuf.kt From bef85980ca3893f34c1357432ac55b6258d17bd3 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Tue, 6 Apr 2021 13:26:30 +0200 Subject: [PATCH 37/43] Revert , server will match both --- .../src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt index 7cdf4b0cda7..87224434596 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt @@ -181,8 +181,7 @@ class MainActivity : AppCompatActivity(), HasAndroidInjector { private fun navigateByIntentUri(intent: Intent?) { val uri = intent?.data ?: return Timber.i("Uri:$uri") - val rootUri = uri.toString().replace(".app/?", ".app?") - navController.navigate(CheckInsFragment.createCheckInUri(rootUri)) + navController.navigate(CheckInsFragment.createCheckInUri(uri.toString())) } /** From d1c97a4c356704d9d181cb2d74e17cfcd480446a Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Tue, 6 Apr 2021 15:48:10 +0200 Subject: [PATCH 38/43] Wrap exceptions --- .../checkins/qrcode/QRCodeException.kt | 11 ++++++++-- .../checkins/qrcode/QRCodeUriParser.kt | 22 ++++++++++++++----- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeException.kt index 47683b8b605..962bbbb1893 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeException.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeException.kt @@ -1,7 +1,14 @@ package de.rki.coronawarnapp.eventregistration.checkins.qrcode -sealed class QRCodeException constructor(message: String? = null) : Exception(message) +sealed class QRCodeException constructor( + message: String? = null, + cause: Throwable? = null +) : Exception(message, cause) class InvalidQrCodeUriException constructor(message: String? = null) : QRCodeException(message) -class InvalidQrCodeDataException constructor(message: String? = null) : QRCodeException(message) + class InvalidQrCodePayloadException constructor(message: String? = null) : QRCodeException(message) + +class InvalidQrCodeDataException constructor( + message: String? = null, cause: Throwable? = null +) : QRCodeException(message, cause) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt index 6c5dcbe1406..b30a69b6008 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.eventregistration.checkins.qrcode +import com.google.common.io.BaseEncoding import dagger.Reusable import de.rki.coronawarnapp.appconfig.AppConfigProvider import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass.QRCodePayload @@ -7,6 +8,7 @@ import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParamete import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptor.PayloadEncoding import de.rki.coronawarnapp.util.decodeBase32 import okio.ByteString.Companion.decodeBase64 +import okio.ByteString.Companion.toByteString import timber.log.Timber import java.net.URI import javax.inject.Inject @@ -26,7 +28,12 @@ class QRCodeUriParser @Inject constructor( @Suppress("BlockingMethodInNonBlockingContext") suspend fun getQrCodePayload(input: String): QRCodePayload { Timber.d("input=$input") - URI.create(input) // Verify it is a valid uri + try { + URI.create(input) // Verify it is a valid uri + } catch (e: Exception) { + Timber.d(e, "Invalid URI") + throw InvalidQrCodeUriException("Invalid URI") + } val descriptor = descriptor(input) val groups = descriptor.matchedGroups(input) @@ -37,10 +44,15 @@ class QRCodeUriParser @Inject constructor( val encoding = PayloadEncoding.forNumber(descriptor.payloadEncoding.number) Timber.d("encoding=$encoding") - val rawPayload = when (encoding) { - PayloadEncoding.BASE32 -> payload.decodeBase32() - PayloadEncoding.BASE64 -> payload.decodeBase64() - else -> null + val rawPayload = try { + when (encoding) { + PayloadEncoding.BASE32 -> payload.decodeBase32() + PayloadEncoding.BASE64 -> BaseEncoding.base64Url().decode(payload).toByteString() + else -> null + } + } catch (e: Exception) { + Timber.d(e, "Payload decoding failed") + throw InvalidQrCodeDataException("Payload decoding failed", e) } ?: throw InvalidQrCodeDataException("Payload decoding failed") return QRCodePayload.parseFrom(rawPayload.toByteArray()) From b195f0151ae86f5695730aee24d98e4b0ea215ff Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Tue, 6 Apr 2021 15:54:16 +0200 Subject: [PATCH 39/43] Pass cause --- .../checkins/qrcode/QRCodeException.kt | 10 ++++++++-- .../checkins/qrcode/QRCodeUriParser.kt | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeException.kt index 962bbbb1893..3f130823a50 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeException.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeException.kt @@ -5,9 +5,15 @@ sealed class QRCodeException constructor( cause: Throwable? = null ) : Exception(message, cause) -class InvalidQrCodeUriException constructor(message: String? = null) : QRCodeException(message) +class InvalidQrCodeUriException constructor( + message: String? = null, + cause: Throwable? = null +) : QRCodeException(message, cause) -class InvalidQrCodePayloadException constructor(message: String? = null) : QRCodeException(message) +class InvalidQrCodePayloadException constructor( + message: String? = null, + cause: Throwable? = null +) : QRCodeException(message, cause) class InvalidQrCodeDataException constructor( message: String? = null, cause: Throwable? = null diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt index b30a69b6008..c0f5661308f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt @@ -32,7 +32,7 @@ class QRCodeUriParser @Inject constructor( URI.create(input) // Verify it is a valid uri } catch (e: Exception) { Timber.d(e, "Invalid URI") - throw InvalidQrCodeUriException("Invalid URI") + throw InvalidQrCodeUriException("Invalid URI", e) } val descriptor = descriptor(input) From 19c76a8db3efaa627afc324a93dcc60459f247ce Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Tue, 6 Apr 2021 15:56:25 +0200 Subject: [PATCH 40/43] lint --- .../eventregistration/checkins/qrcode/QRCodeException.kt | 3 ++- .../eventregistration/checkins/qrcode/QRCodeUriParser.kt | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeException.kt index 3f130823a50..3df60ea22ec 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeException.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeException.kt @@ -16,5 +16,6 @@ class InvalidQrCodePayloadException constructor( ) : QRCodeException(message, cause) class InvalidQrCodeDataException constructor( - message: String? = null, cause: Throwable? = null + message: String? = null, + cause: Throwable? = null ) : QRCodeException(message, cause) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt index c0f5661308f..d6d986d833e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt @@ -7,7 +7,6 @@ import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptorOrBuilder import de.rki.coronawarnapp.server.protocols.internal.v2.PresenceTracingParametersOuterClass.PresenceTracingQRCodeDescriptor.PayloadEncoding import de.rki.coronawarnapp.util.decodeBase32 -import okio.ByteString.Companion.decodeBase64 import okio.ByteString.Companion.toByteString import timber.log.Timber import java.net.URI From 1c8e2d8221032ef338e381b9482f96007ae613b7 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Tue, 6 Apr 2021 16:01:32 +0200 Subject: [PATCH 41/43] Return null --- .../eventregistration/checkins/qrcode/QRCodeUriParser.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt index d6d986d833e..493d2bdb667 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/QRCodeUriParser.kt @@ -51,7 +51,7 @@ class QRCodeUriParser @Inject constructor( } } catch (e: Exception) { Timber.d(e, "Payload decoding failed") - throw InvalidQrCodeDataException("Payload decoding failed", e) + null } ?: throw InvalidQrCodeDataException("Payload decoding failed") return QRCodePayload.parseFrom(rawPayload.toByteArray()) From a527f27c0d565cd9e255130d6b5b5f81fe9b5510 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Tue, 6 Apr 2021 16:09:28 +0200 Subject: [PATCH 42/43] Create ProtoBufKtTest.kt --- .../rki/coronawarnapp/util/ProtoBufKtTest.kt | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/ProtoBufKtTest.kt diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/ProtoBufKtTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/ProtoBufKtTest.kt new file mode 100644 index 00000000000..76ad1fd1e5e --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/ProtoBufKtTest.kt @@ -0,0 +1,28 @@ +package de.rki.coronawarnapp.util + +import com.google.protobuf.ByteString +import io.kotest.matchers.shouldBe +import okio.ByteString.Companion.toByteString +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +class ProtoBufKtTest : BaseTest() { + + @Test + fun toProtoByteString() { + val okioByteString = KEY.toByteArray().toByteString() + + okioByteString.toProtoByteString() shouldBe ByteString.copyFromUtf8(KEY) + } + + @Test + fun toOkioByteString() { + val protoByteString = ByteString.copyFromUtf8(KEY) + + protoByteString.toOkioByteString() shouldBe KEY.toByteArray().toByteString() + } + + companion object { + private const val KEY = "No generated key" + } +} \ No newline at end of file From 9a61f27b8d533ec68b60abb0b83e8b2c6cc80e06 Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli Date: Tue, 6 Apr 2021 16:12:29 +0200 Subject: [PATCH 43/43] lint --- .../src/test/java/de/rki/coronawarnapp/util/ProtoBufKtTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/ProtoBufKtTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/ProtoBufKtTest.kt index 76ad1fd1e5e..b1678a5b599 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/ProtoBufKtTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/ProtoBufKtTest.kt @@ -25,4 +25,4 @@ class ProtoBufKtTest : BaseTest() { companion object { private const val KEY = "No generated key" } -} \ No newline at end of file +}