Skip to content

Commit

Permalink
chore: Avoid hardcoded internet gateway certificate
Browse files Browse the repository at this point in the history
  • Loading branch information
sdsantos committed Jan 4, 2024
1 parent 82afd23 commit 8687e92
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 87 deletions.
17 changes: 0 additions & 17 deletions app/src/main/java/tech/relaycorp/gateway/data/disk/ReadRawFile.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,11 @@ import com.fredporciuncula.flow.preferences.FlowSharedPreferences
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import tech.relaycorp.gateway.R
import tech.relaycorp.gateway.data.disk.ReadRawFile
import tech.relaycorp.gateway.data.doh.InternetAddressResolutionException
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.nodeId
import tech.relaycorp.relaynet.wrappers.x509.Certificate
import java.security.PublicKey
import javax.inject.Inject
import javax.inject.Provider
Expand All @@ -23,7 +20,6 @@ import javax.inject.Singleton
class InternetGatewayPreferences
@Inject constructor(
private val preferences: Provider<FlowSharedPreferences>,
private val readRawFile: ReadRawFile,
private val resolveServiceAddress: ResolveServiceAddress,
) {
// Address
Expand All @@ -45,17 +41,14 @@ class InternetGatewayPreferences
preferences.get().getString("public_gateway_public_key")
}

suspend fun getPublicKey(): PublicKey = observePublicKey().first()
suspend fun getPublicKey(): PublicKey? = observePublicKey().first()

private fun observePublicKey(): Flow<PublicKey> = { publicKey }.toFlow()
private fun observePublicKey(): Flow<PublicKey?> = { publicKey }.toFlow()
.map {
if (it.isEmpty()) {
readRawFile.read(R.raw.public_gateway_cert)
.let(Certificate.Companion::deserialize)
.subjectPublicKey
if (it.isNotEmpty()) {
Base64.decode(it, Base64.DEFAULT).deserializeRSAPublicKey()
} else {
Base64.decode(it, Base64.DEFAULT)
.deserializeRSAPublicKey()
null
}
}

Expand All @@ -70,8 +63,8 @@ class InternetGatewayPreferences
preferences.get().getString("public_gateway_id")
}

suspend fun getId(): String = id.get().ifEmpty {
getPublicKey().nodeId.also {
suspend fun getId(): String? = id.get().ifEmpty {
getPublicKey()?.nodeId?.also {
setId(it)
}
}
Expand All @@ -87,7 +80,7 @@ class InternetGatewayPreferences
}

suspend fun getRegistrationState() = observeRegistrationState().first()
fun observeRegistrationState() = { registrationState }.toFlow()
private fun observeRegistrationState() = { registrationState }.toFlow()
suspend fun setRegistrationState(value: RegistrationState) =
registrationState.setAndCommit(value)

Expand Down
27 changes: 18 additions & 9 deletions app/src/main/java/tech/relaycorp/gateway/domain/LocalConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package tech.relaycorp.gateway.domain

import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import tech.relaycorp.gateway.common.Logging.logger
import tech.relaycorp.gateway.common.nowInUtc
import tech.relaycorp.gateway.common.toPublicKey
import tech.relaycorp.gateway.data.preference.InternetGatewayPreferences
Expand Down Expand Up @@ -47,27 +48,35 @@ class LocalConfig
getIdentityCertificationPath().leafCertificate

private suspend fun getIdentityCertificationPath(): CertificationPath = getIdentityKey().let {
certificateStore.get()
.retrieveLatest(it.nodeId, getInternetGatewayId())
getInternetGatewayId()?.let { internetGatewayId ->
certificateStore.get()
.retrieveLatest(it.nodeId, internetGatewayId)
}
?: CertificationPath(generateIdentityCertificate(it), emptyList())
}

suspend fun getAllValidIdentityCertificates(): List<Certificate> =
getAllValidIdentityCertificationPaths().map { it.leafCertificate }

private suspend fun getAllValidIdentityCertificationPaths(): List<CertificationPath> =
certificateStore.get()
.retrieveAll(getIdentityKey().nodeId, getInternetGatewayId())
getInternetGatewayId()?.let { internetGatewayId ->
certificateStore.get()
.retrieveAll(getIdentityKey().nodeId, internetGatewayId)
}.orEmpty()

suspend fun setIdentityCertificate(
leafCertificate: Certificate,
certificateChain: List<Certificate> = emptyList(),
) {
certificateStore.get()
.save(
CertificationPath(leafCertificate, certificateChain),
getInternetGatewayId(),
)
getInternetGatewayId()?.let { internetGatewayId ->
certificateStore.get()
.save(
CertificationPath(leafCertificate, certificateChain),
internetGatewayId,
)
} ?: logger.severe(
"Will not save identity certificate because the internet gateway is not registered yet",
)
}

private suspend fun generateIdentityCertificate(privateKey: PrivateKey): Certificate {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,15 @@ class CargoCollection
clientBuilder.build(it, OkHTTPChannelBuilderProvider.Companion::makeBuilder)
} ?: throw Disconnected()

val ccaInputStream = generateCCAInputStream() ?: run {
logger.warning("CCA missing because the internet gateway is not registered")
client.close()
return
}

try {
client
.collectCargo { generateCCAInputStream() }
.collectCargo { ccaInputStream }
.collect { cargoStorage.store(it) }
} catch (e: CogRPCClient.CCARefusedException) {
logger.log(Level.WARNING, "CCA refused")
Expand All @@ -46,7 +52,7 @@ class CargoCollection
}

private fun generateCCAInputStream() = runBlocking {
generateCCA.generateSerialized().inputStream()
generateCCA.generateSerialized()?.inputStream()
}

private suspend fun getCourierAddress() = connectionStateObserver
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ class GenerateCCA
private val gatewayManager: Provider<GatewayManager>,
) {

suspend fun generateSerialized(): ByteArray {
suspend fun generateSerialized(): ByteArray? {
val identityPrivateKey = localConfig.getIdentityKey()
val cdaIssuer = localConfig.getCargoDeliveryAuth()
val internetGatewayPublicKey = internetGatewayPreferences.getPublicKey()
val internetGatewayPublicKey = internetGatewayPreferences.getPublicKey() ?: return null
val cda = issueDeliveryAuthorization(
internetGatewayPublicKey,
identityPrivateKey,
Expand All @@ -37,9 +37,10 @@ class GenerateCCA
internetGatewayPublicKey.nodeId,
cdaIssuer.subjectId,
)
val internetGatewayId = internetGatewayPreferences.getId() ?: return null
val cca = CargoCollectionAuthorization(
recipient = Recipient(
internetGatewayPreferences.getId(),
internetGatewayId,
internetGatewayPreferences.getAddress(),
),
payload = ccrCiphertext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package tech.relaycorp.gateway.domain.courier
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import tech.relaycorp.gateway.common.Logging.logger
import tech.relaycorp.gateway.common.nowInUtc
import tech.relaycorp.gateway.data.database.ParcelCollectionDao
Expand Down Expand Up @@ -43,7 +44,7 @@ class GenerateCargo
.asSequence()
.batch()
.asFlow()
.map { it.toCargoSerialized().inputStream() }
.mapNotNull { it.toCargoSerialized()?.inputStream() }

private suspend fun getPCAsMessages() =
parcelCollectionDao.getAll().map { it.toCargoMessageWithExpiry() }
Expand Down Expand Up @@ -79,7 +80,7 @@ class GenerateCargo
null
}

private suspend fun CargoMessageSetWithExpiry.toCargoSerialized(): ByteArray {
private suspend fun CargoMessageSetWithExpiry.toCargoSerialized(): ByteArray? {
if (nowInUtc() > latestMessageExpiryDate) {
logger.warning(
"The latest expiration date $latestMessageExpiryDate has expired already",
Expand All @@ -90,13 +91,18 @@ class GenerateCargo
val identityCert = localConfig.getIdentityCertificate()

val recipientAddress = internetGatewayPreferences.getAddress()
val recipientId = internetGatewayPreferences.getId()
val recipientId = internetGatewayPreferences.getId() ?: run {
logger.warning(
"Failed to generate cargo because the internet gateway is not registered",
)
return null
}
val creationDate = calculateCreationDate.calculate()

logger.info("Generating cargo for $recipientAddress")
val cargoMessageSetCiphertext = gatewayManager.get().wrapMessagePayload(
cargoMessageSet,
internetGatewayPreferences.getId(),
recipientId,
identityCert.subjectId,
)
val cargo = Cargo(
Expand Down
Binary file removed app/src/main/res/raw/public_gateway_cert.der
Binary file not shown.
17 changes: 17 additions & 0 deletions app/src/test/java/android/util/Base64.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package android.util

import java.util.Base64

object Base64 {
@JvmStatic
fun encodeToString(input: ByteArray?, flags: Int): String {
return Base64.getEncoder().encodeToString(input)
}

@JvmStatic
fun decode(str: String?, flags: Int): ByteArray {
return Base64.getDecoder().decode(str)
}

const val DEFAULT = 0
}
Loading

0 comments on commit 8687e92

Please sign in to comment.