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 f5504b7792d..952449960c4 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 @@ -5,6 +5,7 @@ import androidx.core.content.edit import com.google.gson.Gson 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.DOWNLOAD import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.LOG_UPLOAD @@ -36,7 +37,8 @@ class EnvironmentSetup @Inject constructor( DATA_DONATION("DATA_DONATION_CDN_URL"), QRCODE_POSTER_TEMPLATE("QRCODE_POSTER_TEMPLATE_URL"), LOG_UPLOAD("LOG_UPLOAD_SERVER_URL"), - SAFETYNET_API_KEY("SAFETYNET_API_KEY") + SAFETYNET_API_KEY("SAFETYNET_API_KEY"), + CROWD_NOTIFIER_PUBLIC_KEY("CROWD_NOTIFIER_PUBLIC_KEY") } enum class Type(val rawKey: String) { @@ -128,6 +130,9 @@ class EnvironmentSetup @Inject constructor( val safetyNetApiKey: String get() = getEnvironmentValue(SAFETYNET_API_KEY).asString + val crowdNotifierPublicKey: String + get() = getEnvironmentValue(CROWD_NOTIFIER_PUBLIC_KEY).asString + val logUploadServerUrl: String get() = getEnvironmentValue(LOG_UPLOAD).asString 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 38da32754ff..92409f9bf5c 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,15 +1,12 @@ package de.rki.coronawarnapp.eventregistration.checkins.qrcode import android.os.Parcelable -import de.rki.coronawarnapp.eventregistration.events.TraceLocationUserInput import de.rki.coronawarnapp.eventregistration.storage.entity.TraceLocationEntity import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass import kotlinx.parcelize.Parcelize import okio.ByteString import okio.ByteString.Companion.decodeBase64 -import okio.ByteString.Companion.toByteString import org.joda.time.Instant -import java.security.SecureRandom const val TRACE_LOCATION_VERSION = 1 @@ -23,7 +20,7 @@ data class TraceLocation( val endDate: Instant?, val defaultCheckInLengthInMinutes: Int?, val cryptographicSeed: ByteString, - val cnPublicKey: String = "hardcoded public key TODO: replace with real one", + val cnPublicKey: String, val version: Int = TRACE_LOCATION_VERSION, ) : Parcelable { @@ -46,14 +43,3 @@ fun TraceLocationEntity.toTraceLocation() = TraceLocation( cnPublicKey = cnPublicKey, version = version ) - -fun TraceLocationUserInput.toTraceLocation(secureRandom: SecureRandom) = TraceLocation( - type = type, - description = description, - address = address, - startDate = startDate, - endDate = endDate, - defaultCheckInLengthInMinutes = defaultCheckInLengthInMinutes, - // cryptographic seed is a sequence of 16 random bytes - cryptographicSeed = ByteArray(16).apply { secureRandom.nextBytes(this) }.toByteString() -) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationCreator.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationCreator.kt index 57140429dee..3373755f51b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationCreator.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationCreator.kt @@ -1,8 +1,10 @@ package de.rki.coronawarnapp.eventregistration.events +import de.rki.coronawarnapp.environment.EnvironmentSetup import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation -import de.rki.coronawarnapp.eventregistration.checkins.qrcode.toTraceLocation import de.rki.coronawarnapp.eventregistration.storage.repo.TraceLocationRepository +import okio.ByteString +import okio.ByteString.Companion.toByteString import java.security.SecureRandom import javax.inject.Inject import javax.inject.Singleton @@ -10,10 +12,28 @@ import javax.inject.Singleton @Singleton class TraceLocationCreator @Inject constructor( private val repository: TraceLocationRepository, - private val secureRandom: SecureRandom + private val secureRandom: SecureRandom, + private val environmentSetup: EnvironmentSetup ) { suspend fun createTraceLocation(traceLocationUserInput: TraceLocationUserInput): TraceLocation { - return repository.addTraceLocation(traceLocationUserInput.toTraceLocation(secureRandom)) + val cnPublicKey = environmentSetup.crowdNotifierPublicKey + + // cryptographic seed is a sequence of 16 random bytes + val cryptographicSeed = ByteArray(16).apply { secureRandom.nextBytes(this) }.toByteString() + + val traceLocation = traceLocationUserInput.toTraceLocation(cryptographicSeed, cnPublicKey) + return repository.addTraceLocation(traceLocation) } } + +fun TraceLocationUserInput.toTraceLocation(cryptographicSeed: ByteString, cnPublicKey: String) = TraceLocation( + type = type, + description = description, + address = address, + startDate = startDate, + endDate = endDate, + defaultCheckInLengthInMinutes = defaultCheckInLengthInMinutes, + cryptographicSeed = cryptographicSeed, + cnPublicKey = cnPublicKey +) 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 44b3cabcf71..fa335d5e812 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}" qrCodePosterTemplateCdnUrl shouldBe "https://qrcodepostertemplate-${env.rawKey}" + crowdNotifierPublicKey shouldBe "123_abc-${env.rawKey}" } } } @@ -126,7 +127,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.QRCODE_POSTER_TEMPLATE.rawKey shouldBe "QRCODE_POSTER_TEMPLATE_URL" - EnvironmentSetup.EnvKey.values().size shouldBe 9 + EnvironmentSetup.EnvKey.CROWD_NOTIFIER_PUBLIC_KEY.rawKey shouldBe "CROWD_NOTIFIER_PUBLIC_KEY" + EnvironmentSetup.EnvKey.values().size shouldBe 10 } companion object { @@ -149,7 +151,8 @@ class EnvironmentSetupTest : BaseTest() { "LOG_UPLOAD_SERVER_URL": "https://logupload-PROD", "QRCODE_POSTER_TEMPLATE_URL": "https://qrcodepostertemplate-PROD", "SAFETYNET_API_KEY": "placeholder-PROD", - "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-PROD" + "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-PROD", + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-PROD" }, "DEV": { "USE_EUR_KEY_PKGS" : false, @@ -160,7 +163,8 @@ class EnvironmentSetupTest : BaseTest() { "LOG_UPLOAD_SERVER_URL": "https://logupload-DEV", "QRCODE_POSTER_TEMPLATE_URL": "https://qrcodepostertemplate-DEV", "SAFETYNET_API_KEY": "placeholder-DEV", - "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-DEV" + "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-DEV", + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-DEV" }, "INT": { "USE_EUR_KEY_PKGS" : false, @@ -171,7 +175,8 @@ class EnvironmentSetupTest : BaseTest() { "LOG_UPLOAD_SERVER_URL": "https://logupload-INT", "QRCODE_POSTER_TEMPLATE_URL": "https://qrcodepostertemplate-INT", "SAFETYNET_API_KEY": "placeholder-INT", - "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-INT" + "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-INT", + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-INT" }, "WRU": { "USE_EUR_KEY_PKGS" : false, @@ -183,7 +188,8 @@ class EnvironmentSetupTest : BaseTest() { "QRCODE_POSTER_TEMPLATE_URL": "https://qrcodepostertemplate-WRU", "SAFETYNET_API_KEY": "placeholder-WRU", "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-WRU", - "CREATE_TRACELOCATION_URL": "https://tracelocation-WRU" + "CREATE_TRACELOCATION_URL": "https://tracelocation-WRU", + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-WRU" }, "WRU-XD": { "USE_EUR_KEY_PKGS" : true, @@ -194,7 +200,8 @@ class EnvironmentSetupTest : BaseTest() { "LOG_UPLOAD_SERVER_URL": "https://logupload-WRU-XD", "QRCODE_POSTER_TEMPLATE_URL": "https://qrcodepostertemplate-WRU-XD", "SAFETYNET_API_KEY": "placeholder-WRU-XD", - "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-WRU-XD" + "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-WRU-XD", + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-WRU-XD" }, "WRU-XA": { "USE_EUR_KEY_PKGS" : true, @@ -205,7 +212,8 @@ class EnvironmentSetupTest : BaseTest() { "LOG_UPLOAD_SERVER_URL": "https://logupload-WRU-XA", "QRCODE_POSTER_TEMPLATE_URL": "https://qrcodepostertemplate-WRU-XA", "SAFETYNET_API_KEY": "placeholder-WRU-XA", - "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-WRU-XA" + "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-WRU-XA", + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-WRU-XA" }, "LOCAL": { "USE_EUR_KEY_PKGS" : true, @@ -216,7 +224,8 @@ class EnvironmentSetupTest : BaseTest() { "LOG_UPLOAD_SERVER_URL": "https://logupload-LOCAL", "QRCODE_POSTER_TEMPLATE_URL": "https://qrcodepostertemplate-LOCAL", "SAFETYNET_API_KEY": "placeholder-LOCAL", - "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-LOCAL" + "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-LOCAL", + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-LOCAL" } } """ diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/TraceLocationUserInputToTraceLocationMapperTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/TraceLocationUserInputToTraceLocationMapperTest.kt index 78cc66fbc94..fe1c63234c4 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/TraceLocationUserInputToTraceLocationMapperTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/TraceLocationUserInputToTraceLocationMapperTest.kt @@ -1,23 +1,17 @@ package de.rki.coronawarnapp.eventregistration.checkins.qrcode import de.rki.coronawarnapp.eventregistration.events.TraceLocationUserInput +import de.rki.coronawarnapp.eventregistration.events.toTraceLocation 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.decodeHex -import okio.ByteString.Companion.toByteString +import okio.ByteString.Companion.encode import org.joda.time.Instant import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import java.security.SecureRandom -import kotlin.random.Random internal class TraceLocationUserInputToTraceLocationMapperTest { - @MockK private lateinit var secureRandom: SecureRandom - @BeforeEach fun setUp() { MockKAnnotations.init(this) @@ -25,12 +19,6 @@ internal class TraceLocationUserInputToTraceLocationMapperTest { @Test fun toTraceLocation() { - - every { secureRandom.nextBytes(any()) } answers { - val byteArray = arg(0) - Random(0).nextBytes(byteArray) - } - TraceLocationUserInput( type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_PRIVATE_EVENT, description = "Top Secret Private Event", @@ -38,7 +26,7 @@ internal class TraceLocationUserInputToTraceLocationMapperTest { startDate = Instant.parse("2020-01-01T14:00:00.000Z"), endDate = Instant.parse("2020-01-01T18:00:00.000Z"), defaultCheckInLengthInMinutes = 180 - ).toTraceLocation(secureRandom) shouldBe TraceLocation( + ).toTraceLocation("cryptographicSeed".encode(), "cnPublicKey123") shouldBe TraceLocation( id = 0, type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_PRIVATE_EVENT, description = "Top Secret Private Event", @@ -46,8 +34,8 @@ internal class TraceLocationUserInputToTraceLocationMapperTest { startDate = Instant.parse("2020-01-01T14:00:00.000Z"), endDate = Instant.parse("2020-01-01T18:00:00.000Z"), defaultCheckInLengthInMinutes = 180, - cryptographicSeed = "2cc2b48c50aefe53b3974ed91e6b4ea9".decodeHex().toByteArray().toByteString(), - cnPublicKey = "hardcoded public key TODO: replace with real one" + cryptographicSeed = "cryptographicSeed".encode(), + cnPublicKey = "cnPublicKey123" ) } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationCreatorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationCreatorTest.kt index ffd3fd5c48b..45ecec1e50c 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationCreatorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/events/TraceLocationCreatorTest.kt @@ -1,32 +1,43 @@ package de.rki.coronawarnapp.eventregistration.events -import de.rki.coronawarnapp.eventregistration.checkins.qrcode.toTraceLocation +import de.rki.coronawarnapp.environment.EnvironmentSetup import de.rki.coronawarnapp.eventregistration.storage.repo.TraceLocationRepository import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations import io.mockk.coEvery import io.mockk.coVerify +import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.RelaxedMockK import kotlinx.coroutines.test.runBlockingTest +import okio.ByteString.Companion.decodeHex +import okio.ByteString.Companion.toByteString import org.joda.time.Instant import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import testhelpers.BaseTest import java.security.SecureRandom +import kotlin.random.Random internal class TraceLocationCreatorTest : BaseTest() { @MockK lateinit var repository: TraceLocationRepository @RelaxedMockK lateinit var secureRandom: SecureRandom + @MockK private lateinit var environmentSetup: EnvironmentSetup @BeforeEach fun setUp() { MockKAnnotations.init(this) + every { environmentSetup.crowdNotifierPublicKey } returns "cnPublicKey123" + + every { secureRandom.nextBytes(any()) } answers { + val byteArray = arg(0) + Random(0).nextBytes(byteArray) + } } - private fun createInstance() = TraceLocationCreator(repository, secureRandom) + private fun createInstance() = TraceLocationCreator(repository, secureRandom, environmentSetup) @Test fun `createTraceLocation() should return traceLocation and store it in repository when everything works fine`() = @@ -41,7 +52,10 @@ internal class TraceLocationCreatorTest : BaseTest() { defaultCheckInLengthInMinutes = 180 ) - val expectedTraceLocation = userInput.toTraceLocation(secureRandom) + val expectedTraceLocation = userInput.toTraceLocation( + cryptographicSeed = "2cc2b48c50aefe53b3974ed91e6b4ea9".decodeHex().toByteArray().toByteString(), + cnPublicKey = "cnPublicKey123" + ) coEvery { repository.addTraceLocation(any()) } returns expectedTraceLocation