diff --git a/app/build.gradle b/app/build.gradle index 26c79d26..5dd70758 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,7 +12,7 @@ buildscript { ext { ktorVersion = '1.6.2' junitVersion = '5.7.2' - awalaTestingVersion = '1.4.0' + awalaTestingVersion = '1.5.0' } } @@ -139,8 +139,8 @@ dependencies { kapt 'com.google.dagger:dagger-compiler:2.37' // Awala - implementation 'tech.relaycorp:awala:1.62.1' - implementation 'tech.relaycorp:awala-keystore-file:1.5.0' + implementation 'tech.relaycorp:awala:1.63.1' + implementation 'tech.relaycorp:awala-keystore-file:1.6.0' implementation 'tech.relaycorp:cogrpc:1.1.18' implementation 'tech.relaycorp:cogrpc-okhttp:1.1.11' testImplementation "tech.relaycorp:awala-testing:$awalaTestingVersion" diff --git a/app/src/androidTest/java/tech/relaycorp/gateway/test/KeystoreResetTestRule.kt b/app/src/androidTest/java/tech/relaycorp/gateway/test/KeystoreResetTestRule.kt index 7f4ea4ae..5d3cc2ac 100644 --- a/app/src/androidTest/java/tech/relaycorp/gateway/test/KeystoreResetTestRule.kt +++ b/app/src/androidTest/java/tech/relaycorp/gateway/test/KeystoreResetTestRule.kt @@ -5,6 +5,7 @@ import kotlinx.coroutines.runBlocking import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement +import tech.relaycorp.gateway.data.preference.PublicGatewayPreferences import tech.relaycorp.relaynet.keystores.CertificateStore import tech.relaycorp.relaynet.keystores.PrivateKeyStore import tech.relaycorp.relaynet.testing.pki.KeyPairSet @@ -20,6 +21,9 @@ class KeystoreResetTestRule : TestRule { @Inject lateinit var privateKeyStore: PrivateKeyStore + @Inject + lateinit var publicGatewayPreferences: PublicGatewayPreferences + @Inject lateinit var certificateStore: CertificateStore @@ -32,7 +36,11 @@ class KeystoreResetTestRule : TestRule { keystoresFile.deleteRecursively() runBlocking { privateKeyStore.saveIdentityKey(KeyPairSet.PRIVATE_GW.private) - certificateStore.save(PDACertPath.PRIVATE_GW) + certificateStore.save( + PDACertPath.PRIVATE_GW, + emptyList(), + publicGatewayPreferences.getPrivateAddress() + ) } base.evaluate() diff --git a/app/src/main/java/tech/relaycorp/gateway/data/preference/PublicGatewayPreferences.kt b/app/src/main/java/tech/relaycorp/gateway/data/preference/PublicGatewayPreferences.kt index e0a084bc..07579d44 100644 --- a/app/src/main/java/tech/relaycorp/gateway/data/preference/PublicGatewayPreferences.kt +++ b/app/src/main/java/tech/relaycorp/gateway/data/preference/PublicGatewayPreferences.kt @@ -11,10 +11,15 @@ import tech.relaycorp.gateway.data.disk.ReadRawFile import tech.relaycorp.gateway.data.doh.PublicAddressResolutionException import tech.relaycorp.gateway.data.doh.ResolveServiceAddress import tech.relaycorp.gateway.data.model.RegistrationState +import tech.relaycorp.relaynet.wrappers.deserializeRSAPublicKey +import tech.relaycorp.relaynet.wrappers.privateAddress import tech.relaycorp.relaynet.wrappers.x509.Certificate +import java.security.PublicKey import javax.inject.Inject import javax.inject.Provider +import javax.inject.Singleton +@Singleton class PublicGatewayPreferences @Inject constructor( private val preferences: Provider, @@ -36,26 +41,46 @@ class PublicGatewayPreferences @Throws(PublicAddressResolutionException::class) suspend fun getPoWebAddress() = resolveServiceAddress.resolvePoWeb(getAddress()) - // Certificate + // Public Key - private val certificate by lazy { - preferences.get().getString("public_gateway_certificate") + private val publicKey by lazy { + preferences.get().getString("public_gateway_public_key") } - suspend fun getCertificate() = observeCertificate().first() + suspend fun getPublicKey(): PublicKey = observePublicKey().first() - private fun observeCertificate() = { certificate }.toFlow() + private fun observePublicKey(): Flow = { publicKey }.toFlow() .map { - val certificateBytes = if (it.isEmpty()) { + if (it.isEmpty()) { readRawFile.read(R.raw.public_gateway_cert) + .let(Certificate.Companion::deserialize) + .subjectPublicKey } else { Base64.decode(it, Base64.DEFAULT) + .deserializeRSAPublicKey() } - Certificate.deserialize(certificateBytes) } - suspend fun setCertificate(value: Certificate) { - certificate.setAndCommit(Base64.encodeToString(value.serialize(), Base64.DEFAULT)) + suspend fun setPublicKey(value: PublicKey) { + publicKey.setAndCommit(Base64.encodeToString(value.encoded, Base64.DEFAULT)) + setPrivateAddress(value.privateAddress) + } + + // Private Address + + private val privateAddress by lazy { + preferences.get().getString("public_gateway_private_address") + } + + suspend fun getPrivateAddress(): String = + privateAddress.get().ifEmpty { + getPublicKey().privateAddress.also { + setPrivateAddress(it) + } + } + + private suspend fun setPrivateAddress(value: String) { + privateAddress.setAndCommit(value) } // Registration State diff --git a/app/src/main/java/tech/relaycorp/gateway/domain/LocalConfig.kt b/app/src/main/java/tech/relaycorp/gateway/domain/LocalConfig.kt index 82b7caf6..135d67d7 100644 --- a/app/src/main/java/tech/relaycorp/gateway/domain/LocalConfig.kt +++ b/app/src/main/java/tech/relaycorp/gateway/domain/LocalConfig.kt @@ -3,6 +3,7 @@ package tech.relaycorp.gateway.domain import tech.relaycorp.gateway.common.nowInUtc import tech.relaycorp.gateway.common.toPublicKey import tech.relaycorp.gateway.data.disk.FileStore +import tech.relaycorp.gateway.data.preference.PublicGatewayPreferences import tech.relaycorp.gateway.domain.courier.CalculateCRCMessageCreationDate import tech.relaycorp.relaynet.issueGatewayCertificate import tech.relaycorp.relaynet.keystores.CertificateStore @@ -22,7 +23,8 @@ class LocalConfig @Inject constructor( private val fileStore: FileStore, private val privateKeyStore: Provider, - private val certificateStore: Provider + private val certificateStore: Provider, + private val publicGatewayPreferences: PublicGatewayPreferences ) { // Private Gateway Key Pair @@ -42,7 +44,8 @@ class LocalConfig getIdentityCertificationPath().leafCertificate private suspend fun getIdentityCertificationPath(): CertificationPath = getIdentityKey().let { - certificateStore.get().retrieveLatest(it.privateAddress) + certificateStore.get() + .retrieveLatest(it.privateAddress, getPublicGatewayPrivateAddress()) ?: CertificationPath(generateIdentityCertificate(it), emptyList()) } @@ -50,13 +53,15 @@ class LocalConfig getAllValidIdentityCertificationPaths().map { it.leafCertificate } private suspend fun getAllValidIdentityCertificationPaths(): List = - certificateStore.get().retrieveAll(getIdentityKey().privateAddress) + certificateStore.get() + .retrieveAll(getIdentityKey().privateAddress, getPublicGatewayPrivateAddress()) suspend fun setIdentityCertificate( leafCertificate: Certificate, certificateChain: List = emptyList() ) { - certificateStore.get().save(leafCertificate, certificateChain) + certificateStore.get() + .save(leafCertificate, certificateChain, getPublicGatewayPrivateAddress()) } private suspend fun generateIdentityCertificate(privateKey: PrivateKey): Certificate { @@ -110,6 +115,9 @@ class LocalConfig certificateStore.get().deleteExpired() } + private suspend fun getPublicGatewayPrivateAddress() = + publicGatewayPreferences.getPrivateAddress() + // Helpers companion object { diff --git a/app/src/main/java/tech/relaycorp/gateway/domain/courier/GenerateCCA.kt b/app/src/main/java/tech/relaycorp/gateway/domain/courier/GenerateCCA.kt index cf400aab..ec3ae6fb 100644 --- a/app/src/main/java/tech/relaycorp/gateway/domain/courier/GenerateCCA.kt +++ b/app/src/main/java/tech/relaycorp/gateway/domain/courier/GenerateCCA.kt @@ -23,7 +23,7 @@ class GenerateCCA suspend fun generateSerialized(): ByteArray { val identityPrivateKey = localConfig.getIdentityKey() val cdaIssuer = localConfig.getCargoDeliveryAuth() - val publicGatewayPublicKey = publicGatewayPreferences.getCertificate().subjectPublicKey + val publicGatewayPublicKey = publicGatewayPreferences.getPublicKey() val cda = issueDeliveryAuthorization( publicGatewayPublicKey, identityPrivateKey, diff --git a/app/src/main/java/tech/relaycorp/gateway/domain/courier/GenerateCargo.kt b/app/src/main/java/tech/relaycorp/gateway/domain/courier/GenerateCargo.kt index 954ef87d..bf477554 100644 --- a/app/src/main/java/tech/relaycorp/gateway/domain/courier/GenerateCargo.kt +++ b/app/src/main/java/tech/relaycorp/gateway/domain/courier/GenerateCargo.kt @@ -97,7 +97,7 @@ class GenerateCargo logger.info("Generating cargo for $recipientAddress") val cargoMessageSetCiphertext = gatewayManager.get().wrapMessagePayload( cargoMessageSet, - publicGatewayPreferences.getCertificate().subjectPrivateAddress, + publicGatewayPreferences.getPrivateAddress(), identityCert.subjectPrivateAddress ) val cargo = Cargo( diff --git a/app/src/main/java/tech/relaycorp/gateway/domain/courier/RotateCertificate.kt b/app/src/main/java/tech/relaycorp/gateway/domain/courier/RotateCertificate.kt index 892b92d1..99d75fb8 100644 --- a/app/src/main/java/tech/relaycorp/gateway/domain/courier/RotateCertificate.kt +++ b/app/src/main/java/tech/relaycorp/gateway/domain/courier/RotateCertificate.kt @@ -30,7 +30,7 @@ class RotateCertificate @Inject constructor( localConfig.setIdentityCertificate(newIdCert) certRotation.chain.first().let { publicGatewayCert -> - publicGatewayPreferences.setCertificate(publicGatewayCert) + publicGatewayPreferences.setPublicKey(publicGatewayCert.subjectPublicKey) } notifyEndpointsChangeNotifier.notifyAll() diff --git a/app/src/main/java/tech/relaycorp/gateway/domain/endpoint/EndpointRegistration.kt b/app/src/main/java/tech/relaycorp/gateway/domain/endpoint/EndpointRegistration.kt index d0e8e7f7..4a154fca 100644 --- a/app/src/main/java/tech/relaycorp/gateway/domain/endpoint/EndpointRegistration.kt +++ b/app/src/main/java/tech/relaycorp/gateway/domain/endpoint/EndpointRegistration.kt @@ -54,7 +54,7 @@ class EndpointRegistration val endpointCertificate = issueEndpointCertificate( request.privateNodePublicKey, identityKey, - ZonedDateTime.now().plusYears(ENDPOINT_CERTIFICATE_VALIDITY_YEARS), + identityCert.expiryDate, identityCert ) val registration = PrivateNodeRegistration(endpointCertificate, identityCert) @@ -63,6 +63,5 @@ class EndpointRegistration companion object { private const val AUTHORIZATION_VALIDITY_SECONDS: Long = 15 - private const val ENDPOINT_CERTIFICATE_VALIDITY_YEARS = 3L } } diff --git a/app/src/main/java/tech/relaycorp/gateway/domain/publicsync/RegisterGateway.kt b/app/src/main/java/tech/relaycorp/gateway/domain/publicsync/RegisterGateway.kt index c3affa4b..503501c7 100644 --- a/app/src/main/java/tech/relaycorp/gateway/domain/publicsync/RegisterGateway.kt +++ b/app/src/main/java/tech/relaycorp/gateway/domain/publicsync/RegisterGateway.kt @@ -104,7 +104,7 @@ class RegisterGateway ) { publicGatewayPreferences.setRegistrationState(RegistrationState.ToDo) publicGatewayPreferences.setAddress(publicGatewayPublicAddress) - publicGatewayPreferences.setCertificate(registration.gatewayCertificate) + publicGatewayPreferences.setPublicKey(registration.gatewayCertificate.subjectPublicKey) localConfig.setIdentityCertificate(registration.privateNodeCertificate) publicKeyStore.save( registration.gatewaySessionKey!!, diff --git a/app/src/test/java/tech/relaycorp/gateway/data/preference/PublicGatewayPreferencesTest.kt b/app/src/test/java/tech/relaycorp/gateway/data/preference/PublicGatewayPreferencesTest.kt index 61fe158a..85233bf9 100644 --- a/app/src/test/java/tech/relaycorp/gateway/data/preference/PublicGatewayPreferencesTest.kt +++ b/app/src/test/java/tech/relaycorp/gateway/data/preference/PublicGatewayPreferencesTest.kt @@ -1,10 +1,13 @@ package tech.relaycorp.gateway.data.preference +import com.fredporciuncula.flow.preferences.FlowSharedPreferences +import com.fredporciuncula.flow.preferences.Preference import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.anyOrNull +import com.nhaarman.mockitokotlin2.eq import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.whenever -import com.fredporciuncula.flow.preferences.FlowSharedPreferences -import com.fredporciuncula.flow.preferences.Preference +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runBlockingTest import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested @@ -14,6 +17,7 @@ import tech.relaycorp.gateway.data.disk.ReadRawFile import tech.relaycorp.gateway.data.doh.PublicAddressResolutionException import tech.relaycorp.gateway.data.doh.ResolveServiceAddress import tech.relaycorp.gateway.data.model.ServiceAddress +import tech.relaycorp.relaynet.testing.pki.PDACertPath import javax.inject.Provider import kotlin.test.assertEquals @@ -31,6 +35,10 @@ class PublicGatewayPreferencesTest { private val publicGatewayTargetHost = "poweb.example.com" private val publicGatewayTargetPort = 135 private val mockPublicGatewayAddressPreference = mock>() + private val emptyStringPreference = mock> { + whenever(it.asFlow()).thenReturn(flowOf("")) + whenever(it.get()).thenReturn("") + } @BeforeEach internal fun setUp() { @@ -40,6 +48,8 @@ class PublicGatewayPreferencesTest { mockSharedPreferences .getString("address", PublicGatewayPreferences.DEFAULT_ADDRESS) ).thenReturn(mockPublicGatewayAddressPreference) + whenever(mockSharedPreferences.getString(eq("public_gateway_public_key"), anyOrNull())) + .thenReturn(emptyStringPreference) } } @@ -66,4 +76,50 @@ class PublicGatewayPreferencesTest { } } } + + @Nested + inner class GetPublicKey { + @Test + fun `getPublicKey returns certificate public key`() = runBlockingTest { + whenever(mockReadRawFile.read(any())).thenReturn(PDACertPath.PUBLIC_GW.serialize()) + + val publicKey = gwPreferences.getPublicKey() + + assertEquals(PDACertPath.PUBLIC_GW.subjectPublicKey, publicKey) + } + } + + @Nested + inner class GetPrivateAddress { + @Test + fun `getPrivateAddress returns certificate private address`() = runBlockingTest { + whenever( + mockSharedPreferences.getString( + eq("public_gateway_private_address"), + anyOrNull() + ) + ) + .thenReturn(emptyStringPreference) + whenever(mockReadRawFile.read(any())).thenReturn(PDACertPath.PUBLIC_GW.serialize()) + + val address = gwPreferences.getPrivateAddress() + + assertEquals(PDACertPath.PUBLIC_GW.subjectPrivateAddress, address) + } + + @Test + fun `getPrivateAddress returns cached private address`() = runBlockingTest { + val preference = mock> { + whenever(it.get()).thenReturn("private_address") + } + whenever( + mockSharedPreferences.getString(eq("public_gateway_private_address"), anyOrNull()) + ) + .thenReturn(preference) + + val address = gwPreferences.getPrivateAddress() + + assertEquals("private_address", address) + } + } } diff --git a/app/src/test/java/tech/relaycorp/gateway/domain/LocalConfigTest.kt b/app/src/test/java/tech/relaycorp/gateway/domain/LocalConfigTest.kt index 524c88d1..5dcbfa30 100644 --- a/app/src/test/java/tech/relaycorp/gateway/domain/LocalConfigTest.kt +++ b/app/src/test/java/tech/relaycorp/gateway/domain/LocalConfigTest.kt @@ -1,6 +1,7 @@ package tech.relaycorp.gateway.domain import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.eq import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.whenever @@ -12,14 +13,18 @@ import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import tech.relaycorp.gateway.data.disk.FileStore +import tech.relaycorp.gateway.data.preference.PublicGatewayPreferences import tech.relaycorp.gateway.test.BaseDataTestCase +import tech.relaycorp.relaynet.testing.pki.PDACertPath import kotlin.test.assertEquals class LocalConfigTest : BaseDataTestCase() { private val fileStore = mock() - private val localConfig = - LocalConfig(fileStore, privateKeyStoreProvider, certificateStoreProvider) + private val publicGatewayPreferences = mock() + private val localConfig = LocalConfig( + fileStore, privateKeyStoreProvider, certificateStoreProvider, publicGatewayPreferences + ) @BeforeEach fun setUp() { @@ -35,6 +40,8 @@ class LocalConfigTest : BaseDataTestCase() { val key = it.getArgument(0) memoryStore[key] } + whenever(publicGatewayPreferences.getPrivateAddress()) + .thenReturn(PDACertPath.PUBLIC_GW.subjectPrivateAddress) } } @@ -104,6 +111,19 @@ class LocalConfigTest : BaseDataTestCase() { assertEquals(originalKeyPair, keyPair) } + @Test + fun `Correct public gateway private address used as issuer in set identity certificate `() = + runBlockingTest { + localConfig.bootstrap() + + verify(certificateStore).setCertificate( + any(), + any(), + any(), + eq(PDACertPath.PUBLIC_GW.subjectPrivateAddress) + ) + } + @Test fun `CDA issuer should be created if it doesn't already exist`() = runBlockingTest { localConfig.bootstrap() diff --git a/app/src/test/java/tech/relaycorp/gateway/domain/courier/GenerateCCATest.kt b/app/src/test/java/tech/relaycorp/gateway/domain/courier/GenerateCCATest.kt index e510aed5..979832f4 100644 --- a/app/src/test/java/tech/relaycorp/gateway/domain/courier/GenerateCCATest.kt +++ b/app/src/test/java/tech/relaycorp/gateway/domain/courier/GenerateCCATest.kt @@ -16,7 +16,6 @@ import tech.relaycorp.gateway.domain.LocalConfig import tech.relaycorp.gateway.test.BaseDataTestCase import tech.relaycorp.relaynet.issueGatewayCertificate import tech.relaycorp.relaynet.messages.CargoCollectionAuthorization -import tech.relaycorp.relaynet.testing.pki.CDACertPath import tech.relaycorp.relaynet.testing.pki.KeyPairSet import tech.relaycorp.relaynet.testing.pki.PDACertPath import java.time.Duration @@ -25,8 +24,9 @@ class GenerateCCATest : BaseDataTestCase() { private val publicGatewayPreferences = mock() private val mockFileStore = mock() - private val localConfig = - LocalConfig(mockFileStore, privateKeyStoreProvider, certificateStoreProvider) + private val localConfig = LocalConfig( + mockFileStore, privateKeyStoreProvider, certificateStoreProvider, publicGatewayPreferences + ) private val calculateCreationDate = mock() private val generateCCA = GenerateCCA( @@ -55,9 +55,11 @@ class GenerateCCATest : BaseDataTestCase() { whenever(mockFileStore.read(eq(LocalConfig.CDA_CERTIFICATE_FILE_NAME))) .thenReturn(certificate.serialize()) + whenever(publicGatewayPreferences.getPrivateAddress()) + .thenReturn(PDACertPath.PUBLIC_GW.subjectPrivateAddress) whenever(publicGatewayPreferences.getCogRPCAddress()).thenReturn(ADDRESS) - whenever(publicGatewayPreferences.getCertificate()) - .thenReturn(CDACertPath.PUBLIC_GW) + whenever(publicGatewayPreferences.getPublicKey()) + .thenReturn(KeyPairSet.PUBLIC_GW.public) registerPublicGatewaySessionKey() } diff --git a/app/src/test/java/tech/relaycorp/gateway/domain/courier/GenerateCargoTest.kt b/app/src/test/java/tech/relaycorp/gateway/domain/courier/GenerateCargoTest.kt index 7377d51d..ea1c6a5d 100644 --- a/app/src/test/java/tech/relaycorp/gateway/domain/courier/GenerateCargoTest.kt +++ b/app/src/test/java/tech/relaycorp/gateway/domain/courier/GenerateCargoTest.kt @@ -20,6 +20,7 @@ import tech.relaycorp.gateway.test.BaseDataTestCase import tech.relaycorp.gateway.test.factory.ParcelCollectionFactory import tech.relaycorp.gateway.test.factory.StoredParcelFactory import tech.relaycorp.relaynet.messages.Cargo +import tech.relaycorp.relaynet.testing.pki.KeyPairSet import tech.relaycorp.relaynet.testing.pki.PDACertPath import java.io.InputStream import java.time.Duration @@ -31,8 +32,9 @@ class GenerateCargoTest : BaseDataTestCase() { private val diskMessageOperations = mock() private val publicGatewayPreferences = mock() private val mockFileStore = mock() - private val localConfig = - LocalConfig(mockFileStore, privateKeyStoreProvider, certificateStoreProvider) + private val localConfig = LocalConfig( + mockFileStore, privateKeyStoreProvider, certificateStoreProvider, publicGatewayPreferences + ) private val calculateCRCMessageCreationDate = mock() private val generateCargo = GenerateCargo( storedParcelDao, @@ -47,8 +49,10 @@ class GenerateCargoTest : BaseDataTestCase() { @BeforeEach internal fun setUp() = runBlockingTest { registerPrivateGatewayIdentity() + whenever(publicGatewayPreferences.getPrivateAddress()) + .thenReturn(PDACertPath.PUBLIC_GW.subjectPrivateAddress) whenever(publicGatewayPreferences.getCogRPCAddress()).thenReturn("https://example.org") - whenever(publicGatewayPreferences.getCertificate()).thenReturn(PDACertPath.PUBLIC_GW) + whenever(publicGatewayPreferences.getPublicKey()).thenReturn(KeyPairSet.PUBLIC_GW.public) whenever(calculateCRCMessageCreationDate.calculate()).thenReturn(nowInUtc()) val messageStream: () -> InputStream = "ABC".toByteArray()::inputStream diff --git a/app/src/test/java/tech/relaycorp/gateway/domain/courier/RotateCertificateTest.kt b/app/src/test/java/tech/relaycorp/gateway/domain/courier/RotateCertificateTest.kt index 416de290..bad2712c 100644 --- a/app/src/test/java/tech/relaycorp/gateway/domain/courier/RotateCertificateTest.kt +++ b/app/src/test/java/tech/relaycorp/gateway/domain/courier/RotateCertificateTest.kt @@ -35,7 +35,6 @@ class RotateCertificateTest { KeyPairSet.PRIVATE_GW.public, KeyPairSet.PUBLIC_GW.private, ZonedDateTime.now().plusYears(10), - PDACertPath.PUBLIC_GW, validityStartDate = ZonedDateTime.now().minusDays(1) ) val certificateRotation = CertificateRotation( @@ -49,7 +48,7 @@ class RotateCertificateTest { check { assertArrayEquals(newIdCertificate.serialize(), it.serialize()) }, any() ) - verify(publicGatewayPreferences).setCertificate(PDACertPath.PUBLIC_GW) + verify(publicGatewayPreferences).setPublicKey(PDACertPath.PUBLIC_GW.subjectPublicKey) } @Test @@ -57,7 +56,7 @@ class RotateCertificateTest { rotateCertificate("invalid".toByteArray()) verify(localConfig, never()).setIdentityCertificate(any(), any()) - verify(publicGatewayPreferences, never()).setCertificate(any()) + verify(publicGatewayPreferences, never()).setPublicKey(any()) verify(notifyEndpoints, never()).notifyAll() } @@ -78,23 +77,24 @@ class RotateCertificateTest { rotateCertificate(certificateRotation.serialize()) verify(localConfig, never()).setIdentityCertificate(any(), any()) - verify(publicGatewayPreferences, never()).setCertificate(any()) + verify(publicGatewayPreferences, never()).setPublicKey(any()) verify(notifyEndpoints, never()).notifyAll() } @Test fun `new certificate triggers notification`() = runBlockingTest { - val newIdCertificate = issueGatewayCertificate( + val oldCertificate = issueGatewayCertificate( KeyPairSet.PRIVATE_GW.public, KeyPairSet.PUBLIC_GW.private, - ZonedDateTime.now().plusYears(10), + PDACertPath.PRIVATE_GW.expiryDate.minusSeconds(1), PDACertPath.PUBLIC_GW, validityStartDate = ZonedDateTime.now().minusDays(1) ) val certificateRotation = CertificateRotation( - newIdCertificate, listOf(PDACertPath.PUBLIC_GW) + PDACertPath.PRIVATE_GW, listOf(PDACertPath.PUBLIC_GW) ) - whenever(localConfig.getIdentityCertificate()).thenReturn(PDACertPath.PRIVATE_GW) + whenever(localConfig.getIdentityCertificate()).thenReturn(oldCertificate) + rotateCertificate(certificateRotation.serialize()) verify(notifyEndpoints, times(1)).notifyAll() diff --git a/app/src/test/java/tech/relaycorp/gateway/domain/endpoint/EndpointRegistrationTest.kt b/app/src/test/java/tech/relaycorp/gateway/domain/endpoint/EndpointRegistrationTest.kt index e15bb652..ff43a107 100644 --- a/app/src/test/java/tech/relaycorp/gateway/domain/endpoint/EndpointRegistrationTest.kt +++ b/app/src/test/java/tech/relaycorp/gateway/domain/endpoint/EndpointRegistrationTest.kt @@ -2,6 +2,7 @@ package tech.relaycorp.gateway.domain.endpoint import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.whenever import kotlinx.coroutines.test.runBlockingTest import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested @@ -11,6 +12,7 @@ import tech.relaycorp.gateway.data.database.LocalEndpointDao import tech.relaycorp.gateway.data.disk.FileStore import tech.relaycorp.gateway.data.model.LocalEndpoint import tech.relaycorp.gateway.data.model.PrivateMessageAddress +import tech.relaycorp.gateway.data.preference.PublicGatewayPreferences import tech.relaycorp.gateway.domain.LocalConfig import tech.relaycorp.gateway.test.BaseDataTestCase import tech.relaycorp.relaynet.messages.InvalidMessageException @@ -29,8 +31,11 @@ import kotlin.test.assertTrue class EndpointRegistrationTest : BaseDataTestCase() { private val mockLocalEndpointDao = mock() private val mockFileStore = mock() - private val mockLocalConfig = - LocalConfig(mockFileStore, privateKeyStoreProvider, certificateStoreProvider) + private val mockPublicGatewayPreferences = mock() + private val mockLocalConfig = LocalConfig( + mockFileStore, privateKeyStoreProvider, certificateStoreProvider, + mockPublicGatewayPreferences + ) private val endpointRegistration = EndpointRegistration(mockLocalEndpointDao, mockLocalConfig) private val dummyApplicationId = "tech.relaycorp.foo" @@ -38,6 +43,8 @@ class EndpointRegistrationTest : BaseDataTestCase() { @BeforeEach internal fun setUp() = runBlockingTest { registerPrivateGatewayIdentity() + whenever(mockPublicGatewayPreferences.getPrivateAddress()) + .thenReturn(PDACertPath.PUBLIC_GW.subjectPrivateAddress) } @Nested @@ -74,7 +81,7 @@ class EndpointRegistrationTest : BaseDataTestCase() { @Nested inner class Register { private val authorization = PrivateNodeRegistrationAuthorization( - ZonedDateTime.now().plusSeconds(3), + ZonedDateTime.now().plusSeconds(10), dummyApplicationId.toByteArray() ) private val crr = PrivateNodeRegistrationRequest( @@ -145,17 +152,13 @@ class EndpointRegistrationTest : BaseDataTestCase() { } @Test - fun `Expiry date should be in three years`() = runBlockingTest { + fun `Expiry date should be the same as identity certificate`() = runBlockingTest { val registrationSerialized = endpointRegistration.register(crr) val registration = PrivateNodeRegistration.deserialize(registrationSerialized) - val threeYearsFromNow = ZonedDateTime.now().plusYears(3) assertEquals( - 0, - ChronoUnit.MINUTES.between( - registration.privateNodeCertificate.expiryDate, - threeYearsFromNow - ) + PDACertPath.PRIVATE_GW.expiryDate, + registration.privateNodeCertificate.expiryDate ) } } diff --git a/app/src/test/java/tech/relaycorp/gateway/domain/publicsync/CollectParcelsFromGatewayTest.kt b/app/src/test/java/tech/relaycorp/gateway/domain/publicsync/CollectParcelsFromGatewayTest.kt index e60d12ec..902671f6 100644 --- a/app/src/test/java/tech/relaycorp/gateway/domain/publicsync/CollectParcelsFromGatewayTest.kt +++ b/app/src/test/java/tech/relaycorp/gateway/domain/publicsync/CollectParcelsFromGatewayTest.kt @@ -24,6 +24,7 @@ import tech.relaycorp.gateway.data.disk.FileStore import tech.relaycorp.gateway.data.doh.PublicAddressResolutionException import tech.relaycorp.gateway.data.model.MessageAddress import tech.relaycorp.gateway.data.model.RecipientLocation +import tech.relaycorp.gateway.data.preference.PublicGatewayPreferences import tech.relaycorp.gateway.domain.LocalConfig import tech.relaycorp.gateway.domain.StoreParcel import tech.relaycorp.gateway.domain.endpoint.IncomingParcelNotifier @@ -48,8 +49,11 @@ class CollectParcelsFromGatewayTest : BaseDataTestCase() { override suspend fun get() = poWebClient } private val mockFileStore = mock() - private val mockLocalConfig = - LocalConfig(mockFileStore, privateKeyStoreProvider, certificateStoreProvider) + private val mockPublicGatewayPreferences = mock() + private val mockLocalConfig = LocalConfig( + mockFileStore, privateKeyStoreProvider, certificateStoreProvider, + mockPublicGatewayPreferences + ) private val notifyEndpoints = mock() private val subject = CollectParcelsFromGateway( storeParcel, poWebClientBuilder, notifyEndpoints, mockLocalConfig @@ -60,6 +64,8 @@ class CollectParcelsFromGatewayTest : BaseDataTestCase() { registerPrivateGatewayIdentity() whenever(storeParcel.store(any(), any())) .thenReturn(StoreParcel.Result.Success(mock())) + whenever(mockPublicGatewayPreferences.getPrivateAddress()) + .thenReturn(PDACertPath.PUBLIC_GW.subjectPrivateAddress) } @Test diff --git a/app/src/test/java/tech/relaycorp/gateway/domain/publicsync/DeliverParcelsToGatewayTest.kt b/app/src/test/java/tech/relaycorp/gateway/domain/publicsync/DeliverParcelsToGatewayTest.kt index a8c7a0d7..78ed9f7f 100644 --- a/app/src/test/java/tech/relaycorp/gateway/domain/publicsync/DeliverParcelsToGatewayTest.kt +++ b/app/src/test/java/tech/relaycorp/gateway/domain/publicsync/DeliverParcelsToGatewayTest.kt @@ -16,9 +16,10 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import tech.relaycorp.gateway.data.database.StoredParcelDao import tech.relaycorp.gateway.data.disk.DiskMessageOperations -import tech.relaycorp.gateway.data.disk.MessageDataNotFoundException import tech.relaycorp.gateway.data.disk.FileStore +import tech.relaycorp.gateway.data.disk.MessageDataNotFoundException import tech.relaycorp.gateway.data.doh.PublicAddressResolutionException +import tech.relaycorp.gateway.data.preference.PublicGatewayPreferences import tech.relaycorp.gateway.domain.DeleteParcel import tech.relaycorp.gateway.domain.LocalConfig import tech.relaycorp.gateway.pdc.PoWebClientProvider @@ -39,8 +40,11 @@ class DeliverParcelsToGatewayTest : BaseDataTestCase() { override suspend fun get() = poWebClient } private val mockFileStore = mock() - private val localConfig = - LocalConfig(mockFileStore, privateKeyStoreProvider, certificateStoreProvider) + private val mockPublicGatewayPreferences = mock() + private val localConfig = LocalConfig( + mockFileStore, privateKeyStoreProvider, certificateStoreProvider, + mockPublicGatewayPreferences + ) private val deleteParcel = mock() private val subject = DeliverParcelsToGateway( storedParcelDao, diskMessageOperations, poWebClientProvider, localConfig, deleteParcel @@ -51,6 +55,8 @@ class DeliverParcelsToGatewayTest : BaseDataTestCase() { registerPrivateGatewayIdentity() whenever(diskMessageOperations.readMessage(any(), any())) .thenReturn { "".byteInputStream() } + whenever(mockPublicGatewayPreferences.getPrivateAddress()) + .thenReturn(PDACertPath.PUBLIC_GW.subjectPrivateAddress) } @Test diff --git a/app/src/test/java/tech/relaycorp/gateway/domain/publicsync/RegisterGatewayTest.kt b/app/src/test/java/tech/relaycorp/gateway/domain/publicsync/RegisterGatewayTest.kt index 19fc205b..9a9443c2 100644 --- a/app/src/test/java/tech/relaycorp/gateway/domain/publicsync/RegisterGatewayTest.kt +++ b/app/src/test/java/tech/relaycorp/gateway/domain/publicsync/RegisterGatewayTest.kt @@ -36,8 +36,9 @@ class RegisterGatewayTest : BaseDataTestCase() { private val pgwPreferences = mock() private val mockFileStore = mock() - private val localConfig = - LocalConfig(mockFileStore, privateKeyStoreProvider, certificateStoreProvider) + private val localConfig = LocalConfig( + mockFileStore, privateKeyStoreProvider, certificateStoreProvider, pgwPreferences + ) private val poWebClient = mock() private val poWebClientBuilder = object : PoWebClientBuilder { override suspend fun build(address: ServiceAddress) = poWebClient @@ -56,6 +57,8 @@ class RegisterGatewayTest : BaseDataTestCase() { @BeforeEach internal fun setUp() = runBlockingTest { registerPrivateGatewayIdentity() + whenever(pgwPreferences.getPrivateAddress()) + .thenReturn(PDACertPath.PUBLIC_GW.subjectPrivateAddress) } @Test @@ -87,7 +90,6 @@ class RegisterGatewayTest : BaseDataTestCase() { KeyPairSet.PRIVATE_GW.public, KeyPairSet.PUBLIC_GW.private, ZonedDateTime.now().plusYears(1), // not expiring soon - PDACertPath.PUBLIC_GW, validityStartDate = ZonedDateTime.now().minusSeconds(1) ) ) @@ -130,7 +132,7 @@ class RegisterGatewayTest : BaseDataTestCase() { registerGateway.registerIfNeeded() - verify(pgwPreferences).setCertificate(eq(pnr.gatewayCertificate)) + verify(pgwPreferences).setPublicKey(eq(pnr.gatewayCertificate.subjectPublicKey)) verify(pgwPreferences).setRegistrationState(eq(RegistrationState.Done)) publicKeyStore.retrieve(pnr.gatewayCertificate.subjectPrivateAddress) assertEquals(pnr.privateNodeCertificate, localConfig.getIdentityCertificate()) @@ -144,7 +146,7 @@ class RegisterGatewayTest : BaseDataTestCase() { assertEquals(RegisterGateway.Result.FailedToRegister, registerGateway.registerIfNeeded()) - verify(pgwPreferences, never()).setCertificate(any()) + verify(pgwPreferences, never()).setPublicKey(any()) verify(pgwPreferences, never()).setRegistrationState(any()) assertEquals(0, publicKeyStore.keys.size) } @@ -159,7 +161,7 @@ class RegisterGatewayTest : BaseDataTestCase() { assertEquals(RegisterGateway.Result.FailedToRegister, registerGateway.registerIfNeeded()) - verify(pgwPreferences, never()).setCertificate(any()) + verify(pgwPreferences, never()).setPublicKey(any()) verify(pgwPreferences, never()).setRegistrationState(any()) assertEquals(0, publicKeyStore.keys.size) } diff --git a/app/src/test/java/tech/relaycorp/gateway/test/BaseDataTestCase.kt b/app/src/test/java/tech/relaycorp/gateway/test/BaseDataTestCase.kt index 75a1baf4..d6629b3c 100644 --- a/app/src/test/java/tech/relaycorp/gateway/test/BaseDataTestCase.kt +++ b/app/src/test/java/tech/relaycorp/gateway/test/BaseDataTestCase.kt @@ -38,7 +38,9 @@ abstract class BaseDataTestCase { protected suspend fun registerPrivateGatewayIdentity() { privateKeyStore.saveIdentityKey(KeyPairSet.PRIVATE_GW.private) - certificateStore.save(PDACertPath.PRIVATE_GW) + certificateStore.save( + PDACertPath.PRIVATE_GW, emptyList(), PDACertPath.PUBLIC_GW.subjectPrivateAddress + ) } protected suspend fun registerPrivateGatewaySessionKey() {