From c730bc4343ee79928486e676bb465e175444a567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Santos?= Date: Wed, 3 Jan 2024 17:26:11 +0000 Subject: [PATCH 1/3] fix: Verify cargo bound for private gateways (#593) Closes: https://github.com/relaycorp/relaynet-courier-android/issues/255 In the process I found a bug. Here: https://github.com/relaycorp/awala-testing-jvm/blob/d057538ede0ebc7636c97152b4da42764bf566ad/src/main/kotlin/tech/relaycorp/relaynet/testing/pki/CDACertPath.kt#L27 It should be `KeyPairSet.PRIVATE_GW.private`. I'll do a PR for that and then incorporate it here. --- .../relaycorp/courier/domain/StoreMessage.kt | 17 ++++++++- .../courier/domain/StoreMessageTest.kt | 36 +++++++++++++------ 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/tech/relaycorp/courier/domain/StoreMessage.kt b/app/src/main/java/tech/relaycorp/courier/domain/StoreMessage.kt index 1385563c..f4d770de 100644 --- a/app/src/main/java/tech/relaycorp/courier/domain/StoreMessage.kt +++ b/app/src/main/java/tech/relaycorp/courier/domain/StoreMessage.kt @@ -11,6 +11,7 @@ import tech.relaycorp.courier.data.model.StoredMessage import tech.relaycorp.relaynet.cogrpc.readBytesAndClose import tech.relaycorp.relaynet.messages.Cargo import tech.relaycorp.relaynet.messages.CargoCollectionAuthorization +import tech.relaycorp.relaynet.messages.InvalidMessageException import tech.relaycorp.relaynet.ramf.RAMFException import tech.relaycorp.relaynet.ramf.RAMFMessage import java.io.InputStream @@ -38,10 +39,24 @@ class StoreMessage } try { - cargo.validate(null) + cargo.validate( + when (recipientType) { + GatewayType.Internet -> null + GatewayType.Private -> + cargo.recipientCertificate + ?.let { setOf(it) } + ?: run { + logger.warning("Invalid cargo received with missing recipient certificate") + return Result.Error.Invalid + } + }, + ) } catch (exc: RAMFException) { logger.warning("Invalid cargo received: ${exc.message}") return Result.Error.Invalid + } catch (exc: InvalidMessageException) { + logger.warning("Invalid cargo received: ${exc.message}") + return Result.Error.Invalid } return storeMessage(MessageType.Cargo, cargo, cargoBytes, recipientType) diff --git a/app/src/test/java/tech/relaycorp/courier/domain/StoreMessageTest.kt b/app/src/test/java/tech/relaycorp/courier/domain/StoreMessageTest.kt index 41863f38..3b4227ca 100644 --- a/app/src/test/java/tech/relaycorp/courier/domain/StoreMessageTest.kt +++ b/app/src/test/java/tech/relaycorp/courier/domain/StoreMessageTest.kt @@ -11,7 +11,6 @@ import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import tech.relaycorp.courier.data.database.StoredMessageDao @@ -20,6 +19,7 @@ import tech.relaycorp.courier.data.model.GatewayType import tech.relaycorp.courier.data.model.StorageSize import tech.relaycorp.courier.data.model.StorageUsage import tech.relaycorp.courier.data.model.StoredMessage +import tech.relaycorp.relaynet.issueDeliveryAuthorization import tech.relaycorp.relaynet.messages.Cargo import tech.relaycorp.relaynet.messages.CargoCollectionAuthorization import tech.relaycorp.relaynet.messages.Recipient @@ -145,7 +145,10 @@ class StoreMessageTest { invalidCargo.serialize(KeyPairSet.PRIVATE_GW.private) val result = - subject.storeCargo(invalidCargoSerialized.inputStream(), GatewayType.Internet) + subject.storeCargo( + invalidCargoSerialized.inputStream(), + GatewayType.Internet, + ) assertEquals(StoreMessage.Result.Error.Invalid, result) verify(diskRepository, never()).writeMessage(any()) @@ -163,7 +166,8 @@ class StoreMessageTest { ) val cargoSerialized = cargo.serialize(KeyPairSet.PRIVATE_GW.private) - val result = subject.storeCargo(cargoSerialized.inputStream(), GatewayType.Internet) + val result = + subject.storeCargo(cargoSerialized.inputStream(), GatewayType.Internet) assertTrue(result is StoreMessage.Result.Success) verify(diskRepository).writeMessage(any()) @@ -185,7 +189,8 @@ class StoreMessageTest { ) val cargoSerialized = cargo.serialize(KeyPairSet.PRIVATE_GW.private) - val result = subject.storeCargo(cargoSerialized.inputStream(), GatewayType.Internet) + val result = + subject.storeCargo(cargoSerialized.inputStream(), GatewayType.Internet) assertTrue(result is StoreMessage.Result.Success) verify(diskRepository).writeMessage(any()) @@ -195,21 +200,30 @@ class StoreMessageTest { @Nested inner class BoundForPrivateGateway { - private val recipient = Recipient(KeyPairSet.PRIVATE_GW.public.nodeId) + private val recipient = Recipient(CDACertPath.PRIVATE_GW.subjectPublicKey.nodeId) + private val senderCertificate = + issueDeliveryAuthorization( + KeyPairSet.INTERNET_GW.public, + KeyPairSet.PRIVATE_GW.private, + ZonedDateTime.now().plusHours(1), + CDACertPath.PRIVATE_GW, + validityStartDate = ZonedDateTime.now().minusMinutes(1), + ) @Test - @Disabled // See https://github.com/relaycorp/relaynet-courier-android/issues/255 fun `Unauthorized cargo should be refused`() = runTest { val cargo = Cargo( recipient.copy(id = "${recipient.id}abc"), "payload".toByteArray(), - CDACertPath.INTERNET_GW, + senderCertificate, + senderCertificateChain = setOf(CDACertPath.PRIVATE_GW), ) val cargoSerialized = cargo.serialize(KeyPairSet.INTERNET_GW.private) - val result = subject.storeCargo(cargoSerialized.inputStream(), GatewayType.Private) + val result = + subject.storeCargo(cargoSerialized.inputStream(), GatewayType.Private) assertEquals(StoreMessage.Result.Error.Invalid, result) verify(diskRepository, never()).writeMessage(any()) @@ -223,11 +237,13 @@ class StoreMessageTest { Cargo( recipient, "payload".toByteArray(), - CDACertPath.INTERNET_GW, + senderCertificate, + senderCertificateChain = setOf(CDACertPath.PRIVATE_GW), ) val cargoSerialized = cargo.serialize(KeyPairSet.INTERNET_GW.private) - val result = subject.storeCargo(cargoSerialized.inputStream(), GatewayType.Private) + val result = + subject.storeCargo(cargoSerialized.inputStream(), GatewayType.Private) assertTrue(result is StoreMessage.Result.Success) verify(diskRepository).writeMessage(any()) From c3460c3742270fcbbc05ce3987c89682cc5555e0 Mon Sep 17 00:00:00 2001 From: Gus Narea Date: Fri, 19 Jan 2024 14:53:13 +0000 Subject: [PATCH 2/3] chore(Kodiak): Use org-level config --- .kodiak.toml | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .kodiak.toml diff --git a/.kodiak.toml b/.kodiak.toml deleted file mode 100644 index d122acef..00000000 --- a/.kodiak.toml +++ /dev/null @@ -1,12 +0,0 @@ -version = 1 - -[merge] -method = "squash" -prioritize_ready_to_merge = true - -[approve] -auto_approve_usernames = ["dependabot", "gnarea"] - -[merge.message] -title = "pull_request_title" -body = "pull_request_body" From 7ded781d212a4f5fa66ff40da66f072f608fccc5 Mon Sep 17 00:00:00 2001 From: Gus Narea Date: Thu, 25 Jan 2024 10:32:45 +0000 Subject: [PATCH 3/3] chore: Integrate shared workflows (#600) --- .github/workflows/ci-cd.yml | 44 +++++-------------------------------- 1 file changed, 6 insertions(+), 38 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 8d5c7f26..adbfed12 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -1,45 +1,13 @@ name: CI and automatic releases on: + pull_request: push: - branches-ignore: [gh-pages] + branches: [master] jobs: - static-checks: - name: Static checks - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Validate Gradle wrapper - uses: gradle/wrapper-validation-action@v1 - ci: - needs: static-checks - name: "Run static checks and tests" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-java@v1 - with: - java-version: 17 - - name: Cache Grade dependencies - uses: actions/cache@v1 - with: - path: ~/.gradle - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} - restore-keys: | - ${{ runner.os }}-gradle- - - uses: eskatos/gradle-command-action@v1 - with: - arguments: build --scan - - name: Assemble instrumentation tests APK - uses: eskatos/gradle-command-action@v1 - with: - arguments: "app:assembleDebug app:assembleDebugAndroidTest" - - name: Run tests on physical and virtual devices - uses: asadmansr/Firebase-Test-Lab-Action@v1.0 - with: - arg-spec: 'app/firebase-test-lab.yml:spec' - env: - SERVICE_ACCOUNT: ${{ secrets.CI_GCP_SERVICE_ACCOUNT }} + uses: relaycorp/shared-workflows/.github/workflows/android-app-ci.yml@main + secrets: + gcp_service_account: ${{ secrets.CI_GCP_SERVICE_ACCOUNT }} release: needs: ci @@ -47,6 +15,6 @@ jobs: secrets: android_keystore: ${{ secrets.ANDROID_KEYSTORE }} android_keystore_password: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} - android_key_alias: ${{ secrets.ANDROID_KEYSTORE_KEY_NAME }} + android_key_alias: ${{ secrets.ANDROID_KEYSTORE_KEY_ALIAS }} android_key_password: ${{ secrets.ANDROID_KEYSTORE_KEY_PASSWORD }} android_publisher_credentials: ${{ secrets.PUBLISHER_GCP_SERVICE_ACCOUNT }}