From 6c3eec08ead0d0cb845c36f3659e5e26baa51715 Mon Sep 17 00:00:00 2001 From: chris-cwa <69595386+chris-cwa@users.noreply.github.com> Date: Fri, 4 Jun 2021 15:49:40 +0200 Subject: [PATCH] Dcc Server (EXPOSUREAPP-7504) (#3347) * Extend corona test data structures with digital covid certificate related properties. +Some additional wiring, plumbing and tests for future PRs. * LINTs * Adjust TestRegistrationRequest to supply dcc consent and DOB on test registration. * Remove explicit assignment, defaults are sufficient. * A few additional unit tests to check defaults. * DateOfBirthKey calculation, draft 1 * Fix date parser pattern. * wip * Adjust padding calculation to take the new dobHash into account. Some refactoring to make it less complicated to adjust for future changes. * klint, ofc. * TestCertificate repo, draft1. * DGC -> dcc * TestCertificateRepository, draft 2 * TestCertificateRepository, draft 3 * TestCertificateRepository, draft 4 * Unit tests, draft 1. * Add new app config parameters and implement delay mechanism. * Unit tests, draft 2 * dcc server * merge 2.4 add di module * clean up * fix exception * add server environment * fix test Co-authored-by: Matthias Urhahn <matthias.urhahn@sap.com> Co-authored-by: Mohamed Metwalli <mohamed.metwalli@sap.com> Co-authored-by: Chilja Gossow <49635654+chiljamgossow@users.noreply.github.com> --- .../server/CovidCertificateApiV1.kt | 33 ++++++++++++++++++ .../server/CovidCertificateModule.kt | 26 ++++++++++++++ .../server/CovidCertificateServer.kt | 34 ++++++++++++++++--- .../covidcertificate/server/DccException.kt | 3 ++ .../environment/EnvironmentModule.kt | 4 ++- .../environment/EnvironmentSetup.kt | 7 +++- .../covidcertificate/DCCHttpClient.kt | 8 +++++ .../environment/covidcertificate/DCCModule.kt | 28 +++++++++++++++ .../covidcertificate/DCCServerUrl.kt | 8 +++++ .../util/di/ApplicationComponent.kt | 2 ++ .../environment/EnvironmentSetupTest.kt | 28 ++++++++++----- 11 files changed, 165 insertions(+), 16 deletions(-) create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/server/CovidCertificateApiV1.kt create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/server/CovidCertificateModule.kt create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/server/DccException.kt create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCHttpClient.kt create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCModule.kt create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCServerUrl.kt diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/server/CovidCertificateApiV1.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/server/CovidCertificateApiV1.kt new file mode 100644 index 000000000..393236da3 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/server/CovidCertificateApiV1.kt @@ -0,0 +1,33 @@ +package de.rki.coronawarnapp.covidcertificate.server + +import com.google.gson.annotations.SerializedName +import retrofit2.Response +import retrofit2.http.Body +import retrofit2.http.POST + +interface CovidCertificateApiV1 { + + data class PublicKeyUploadRequest( + @SerializedName("registrationToken") val registrationToken: String, + @SerializedName("publicKey") val publicKey: String + ) + + @POST("/version/v1/publicKey") + suspend fun sendPublicKey( + @Body requestBody: PublicKeyUploadRequest + ) + + data class ComponentsRequest( + @SerializedName("registrationToken") val registrationToken: String, + ) + + data class ComponentsResponse( + @SerializedName("dek") val dek: String, + @SerializedName("dcc") val dcc: String + ) + + @POST("/version/v1/publicKey") + suspend fun getComponents( + @Body requestBody: ComponentsRequest + ): Response<ComponentsResponse> +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/server/CovidCertificateModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/server/CovidCertificateModule.kt new file mode 100644 index 000000000..47ca09e5f --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/server/CovidCertificateModule.kt @@ -0,0 +1,26 @@ +package de.rki.coronawarnapp.covidcertificate.server + +import dagger.Module +import dagger.Provides +import dagger.Reusable +import de.rki.coronawarnapp.environment.covidcertificate.DCCHttpClient +import de.rki.coronawarnapp.environment.covidcertificate.DCCServerUrl +import okhttp3.OkHttpClient +import retrofit2.Retrofit + +@Module +class CovidCertificateModule { + + @Reusable + @Provides + fun apiV1( + @DCCHttpClient httpClient: OkHttpClient, + @DCCServerUrl url: String, + ): CovidCertificateApiV1 { + return Retrofit.Builder() + .client(httpClient) + .baseUrl(url) + .build() + .create(CovidCertificateApiV1::class.java) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/server/CovidCertificateServer.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/server/CovidCertificateServer.kt index 912482395..9e4eed79c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/server/CovidCertificateServer.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/server/CovidCertificateServer.kt @@ -1,27 +1,51 @@ package de.rki.coronawarnapp.covidcertificate.server +import dagger.Lazy import dagger.Reusable import de.rki.coronawarnapp.coronatest.type.RegistrationToken +import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.encryption.rsa.RSAKey +import kotlinx.coroutines.withContext import timber.log.Timber import javax.inject.Inject @Reusable -class CovidCertificateServer @Inject constructor() { +class CovidCertificateServer @Inject constructor( + private val dccApi: Lazy<CovidCertificateApiV1>, + private val dispatcherProvider: DispatcherProvider +) { + + private val api: CovidCertificateApiV1 + get() = dccApi.get() suspend fun registerPublicKeyForTest( testRegistrationToken: RegistrationToken, publicKey: RSAKey.Public, - ) { + ): Unit = withContext(dispatcherProvider.IO) { Timber.tag(TAG).v("registerPublicKeyForTest(token=%s, key=%s)", testRegistrationToken, publicKey) - throw NotImplementedError() + api.sendPublicKey( + requestBody = CovidCertificateApiV1.PublicKeyUploadRequest( + registrationToken = testRegistrationToken, + publicKey = publicKey.base64 + ) + ) } + @Throws(DccException::class) suspend fun requestCertificateForTest( testRegistrationToken: RegistrationToken, - ): TestCertificateComponents { + ): TestCertificateComponents = withContext(dispatcherProvider.IO) { Timber.tag(TAG).v("requestCertificateForTest(token=%s)", testRegistrationToken) - throw NotImplementedError() + val response = api.getComponents( + requestBody = CovidCertificateApiV1.ComponentsRequest(testRegistrationToken) + ) + // TODO replace with InvalidTestCertificateException + correct error codes + if (response.code() == 202) throw DccException() + val result = response.body() ?: throw DccException() + TestCertificateComponents( + dataEncryptionKeyBase64 = result.dek, + encryptedCoseTestCertificateBase64 = result.dcc + ) } companion object { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/server/DccException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/server/DccException.kt new file mode 100644 index 000000000..058cb8292 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/server/DccException.kt @@ -0,0 +1,3 @@ +package de.rki.coronawarnapp.covidcertificate.server + +class DccException : Exception() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentModule.kt index b99710e6d..10fa9b978 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentModule.kt @@ -2,6 +2,7 @@ package de.rki.coronawarnapp.environment import dagger.Module import de.rki.coronawarnapp.environment.bugreporting.BugReportingServerModule +import de.rki.coronawarnapp.environment.covidcertificate.DCCModule import de.rki.coronawarnapp.environment.datadonation.DataDonationCDNModule import de.rki.coronawarnapp.environment.download.DownloadCDNModule import de.rki.coronawarnapp.environment.submission.SubmissionCDNModule @@ -13,7 +14,8 @@ import de.rki.coronawarnapp.environment.verification.VerificationCDNModule SubmissionCDNModule::class, VerificationCDNModule::class, DataDonationCDNModule::class, - BugReportingServerModule::class + BugReportingServerModule::class, + DCCModule::class ] ) class EnvironmentModule diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentSetup.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentSetup.kt index d44e8e88e..bc62e8bab 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentSetup.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentSetup.kt @@ -7,6 +7,7 @@ import com.google.gson.JsonObject import com.google.gson.JsonPrimitive import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.CROWD_NOTIFIER_PUBLIC_KEY import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.DATA_DONATION +import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.DCC import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.DOWNLOAD import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.LOG_UPLOAD import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.SAFETYNET_API_KEY @@ -37,7 +38,8 @@ class EnvironmentSetup @Inject constructor( DATA_DONATION("DATA_DONATION_CDN_URL"), LOG_UPLOAD("LOG_UPLOAD_SERVER_URL"), SAFETYNET_API_KEY("SAFETYNET_API_KEY"), - CROWD_NOTIFIER_PUBLIC_KEY("CROWD_NOTIFIER_PUBLIC_KEY") + CROWD_NOTIFIER_PUBLIC_KEY("CROWD_NOTIFIER_PUBLIC_KEY"), + DCC("DCC_SERVER_URL"), } enum class Type(val rawKey: String) { @@ -135,6 +137,9 @@ class EnvironmentSetup @Inject constructor( val logUploadServerUrl: String get() = getEnvironmentValue(LOG_UPLOAD).asString + val dccServerUrl: String + get() = getEnvironmentValue(DCC).asString + companion object { private const val PKEY_CURRENT_ENVINROMENT = "environment.current" } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCHttpClient.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCHttpClient.kt new file mode 100644 index 000000000..d34bb8687 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCHttpClient.kt @@ -0,0 +1,8 @@ +package de.rki.coronawarnapp.environment.covidcertificate + +import javax.inject.Qualifier + +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class DCCHttpClient diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCModule.kt new file mode 100644 index 000000000..47821bb16 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCModule.kt @@ -0,0 +1,28 @@ +package de.rki.coronawarnapp.environment.covidcertificate + +import dagger.Module +import dagger.Provides +import dagger.Reusable +import de.rki.coronawarnapp.environment.BaseEnvironmentModule +import de.rki.coronawarnapp.environment.EnvironmentSetup +import de.rki.coronawarnapp.http.HttpClientDefault +import okhttp3.OkHttpClient +import javax.inject.Singleton + +@Module +class DCCModule : BaseEnvironmentModule() { + + @Reusable + @DCCHttpClient + @Provides + fun dccHttpClient(@HttpClientDefault defaultHttpClient: OkHttpClient): OkHttpClient = + defaultHttpClient.newBuilder().build() + + @Singleton + @DCCServerUrl + @Provides + fun dccServerUrl(environment: EnvironmentSetup): String { + val url = environment.dccServerUrl + return requireValidUrl(url) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCServerUrl.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCServerUrl.kt new file mode 100644 index 000000000..8c9cffcad --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCServerUrl.kt @@ -0,0 +1,8 @@ +package de.rki.coronawarnapp.environment.covidcertificate + +import javax.inject.Qualifier + +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class DCCServerUrl diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ApplicationComponent.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ApplicationComponent.kt index 471f0e586..9cb1bf7c2 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ApplicationComponent.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ApplicationComponent.kt @@ -14,6 +14,7 @@ import de.rki.coronawarnapp.bugreporting.BugReportingSharedModule import de.rki.coronawarnapp.bugreporting.debuglog.DebugLogger import de.rki.coronawarnapp.coronatest.CoronaTestModule import de.rki.coronawarnapp.coronatest.server.VerificationModule +import de.rki.coronawarnapp.covidcertificate.server.CovidCertificateModule import de.rki.coronawarnapp.datadonation.DataDonationModule import de.rki.coronawarnapp.diagnosiskeys.DiagnosisKeysModule import de.rki.coronawarnapp.diagnosiskeys.DownloadDiagnosisKeysTaskModule @@ -81,6 +82,7 @@ import javax.inject.Singleton PresenceTracingModule::class, CoronaTestModule::class, VaccinationModule::class, + CovidCertificateModule::class, ] ) interface ApplicationComponent : AndroidInjector<CoronaWarnApplication> { diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/environment/EnvironmentSetupTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/environment/EnvironmentSetupTest.kt index 12b178e9d..b1a4b64e4 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/environment/EnvironmentSetupTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/environment/EnvironmentSetupTest.kt @@ -68,6 +68,7 @@ class EnvironmentSetupTest : BaseTest() { dataDonationCdnUrl shouldBe "https://datadonation-${env.rawKey}" logUploadServerUrl shouldBe "https://logupload-${env.rawKey}" crowdNotifierPublicKey shouldBe "123_abc-${env.rawKey}" + dccServerUrl shouldBe "https://dcc-${env.rawKey}" } } } @@ -127,7 +128,8 @@ class EnvironmentSetupTest : BaseTest() { EnvironmentSetup.EnvKey.LOG_UPLOAD.rawKey shouldBe "LOG_UPLOAD_SERVER_URL" EnvironmentSetup.EnvKey.SAFETYNET_API_KEY.rawKey shouldBe "SAFETYNET_API_KEY" EnvironmentSetup.EnvKey.CROWD_NOTIFIER_PUBLIC_KEY.rawKey shouldBe "CROWD_NOTIFIER_PUBLIC_KEY" - EnvironmentSetup.EnvKey.values().size shouldBe 9 + EnvironmentSetup.EnvKey.DCC.rawKey shouldBe "DCC_SERVER_URL" + EnvironmentSetup.EnvKey.values().size shouldBe 10 } companion object { @@ -152,7 +154,8 @@ class EnvironmentSetupTest : BaseTest() { "VACCINATION_CDN_URL": "https://vaccination-PROD", "SAFETYNET_API_KEY": "placeholder-PROD", "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-PROD", - "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-PROD" + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-PROD", + "DCC_SERVER_URL": "https://dcc-PROD" }, "DEV": { "USE_EUR_KEY_PKGS" : false, @@ -164,7 +167,8 @@ class EnvironmentSetupTest : BaseTest() { "VACCINATION_CDN_URL": "https://vaccination-DEV", "SAFETYNET_API_KEY": "placeholder-DEV", "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-DEV", - "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-DEV" + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-DEV", + "DCC_SERVER_URL": "https://dcc-DEV" }, "INT": { "USE_EUR_KEY_PKGS" : false, @@ -176,7 +180,8 @@ class EnvironmentSetupTest : BaseTest() { "VACCINATION_CDN_URL": "https://vaccination-INT", "SAFETYNET_API_KEY": "placeholder-INT", "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-INT", - "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-INT" + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-INT", + "DCC_SERVER_URL": "https://dcc-INT" }, "WRU": { "USE_EUR_KEY_PKGS" : false, @@ -189,7 +194,8 @@ class EnvironmentSetupTest : BaseTest() { "SAFETYNET_API_KEY": "placeholder-WRU", "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-WRU", "CREATE_TRACELOCATION_URL": "https://tracelocation-WRU", - "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-WRU" + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-WRU", + "DCC_SERVER_URL": "https://dcc-WRU" }, "WRU-XD": { "USE_EUR_KEY_PKGS" : true, @@ -201,7 +207,8 @@ class EnvironmentSetupTest : BaseTest() { "VACCINATION_CDN_URL": "https://vaccination-WRU-XD", "SAFETYNET_API_KEY": "placeholder-WRU-XD", "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-WRU-XD", - "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-WRU-XD" + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-WRU-XD", + "DCC_SERVER_URL": "https://dcc-WRU-XD" }, "WRU-XA": { "USE_EUR_KEY_PKGS" : true, @@ -213,7 +220,8 @@ class EnvironmentSetupTest : BaseTest() { "VACCINATION_CDN_URL": "https://vaccination-WRU-XA", "SAFETYNET_API_KEY": "placeholder-WRU-XA", "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-WRU-XA", - "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-WRU-XA" + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-WRU-XA", + "DCC_SERVER_URL": "https://dcc-WRU-XA" }, "TESTER-MOCK": { "USE_EUR_KEY_PKGS" : true, @@ -225,7 +233,8 @@ class EnvironmentSetupTest : BaseTest() { "VACCINATION_CDN_URL": "https://vaccination-TESTER-MOCK", "SAFETYNET_API_KEY": "placeholder-TESTER-MOCK", "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-TESTER-MOCK", - "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-TESTER-MOCK" + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-TESTER-MOCK", + "DCC_SERVER_URL": "https://dcc-TESTER-MOCK" }, "LOCAL": { "USE_EUR_KEY_PKGS" : true, @@ -237,7 +246,8 @@ class EnvironmentSetupTest : BaseTest() { "VACCINATION_CDN_URL": "https://vaccination-LOCAL", "SAFETYNET_API_KEY": "placeholder-LOCAL", "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-LOCAL", - "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-LOCAL" + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-LOCAL", + "DCC_SERVER_URL": "https://dcc-LOCAL" } } """ -- GitLab