Skip to content
This repository has been archived by the owner on Jun 20, 2023. It is now read-only.

Add Crowd Notifier Public Key for different environments (EXPOSUREAPP-6118) #2729

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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 {

Expand All @@ -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()
)
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
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.Companion.toByteString
import java.security.SecureRandom
import javax.inject.Inject
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
val traceLocation = traceLocationUserInput.toTraceLocation(secureRandom, cnPublicKey)
return repository.addTraceLocation(traceLocation)
}
}

fun TraceLocationUserInput.toTraceLocation(secureRandom: SecureRandom, cnPublicKey: String) = 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(),
mtwalli marked this conversation as resolved.
Show resolved Hide resolved
cnPublicKey = cnPublicKey
)
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
}
}
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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"
}
}
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
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
Expand Down Expand Up @@ -38,7 +39,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(secureRandom, "cnPublicKey123") shouldBe TraceLocation(
id = 0,
type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_PRIVATE_EVENT,
description = "Top Secret Private Event",
Expand All @@ -47,7 +48,7 @@ internal class TraceLocationUserInputToTraceLocationMapperTest {
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"
cnPublicKey = "cnPublicKey123"
)
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
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
Expand All @@ -20,13 +21,15 @@ 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"
}

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`() =
Expand All @@ -41,7 +44,7 @@ internal class TraceLocationCreatorTest : BaseTest() {
defaultCheckInLengthInMinutes = 180
)

val expectedTraceLocation = userInput.toTraceLocation(secureRandom)
val expectedTraceLocation = userInput.toTraceLocation(secureRandom, "cnPublicKey123")

coEvery { repository.addTraceLocation(any()) } returns expectedTraceLocation

Expand Down