diff --git a/lib/src/main/java/tech/relaycorp/awaladroid/Awala.kt b/lib/src/main/java/tech/relaycorp/awaladroid/Awala.kt index 324b7566..9abbe073 100644 --- a/lib/src/main/java/tech/relaycorp/awaladroid/Awala.kt +++ b/lib/src/main/java/tech/relaycorp/awaladroid/Awala.kt @@ -37,6 +37,7 @@ public object Awala { /** * Set up the endpoint library. */ + @Throws(GatewayUnregisteredException::class) public suspend fun setUp(context: Context) { val keystoreRoot = FileKeystoreRoot(File(context.filesDir, "awaladroid${File.separator}keystores")) diff --git a/lib/src/main/java/tech/relaycorp/awaladroid/GatewayClientImpl.kt b/lib/src/main/java/tech/relaycorp/awaladroid/GatewayClientImpl.kt index aaa1dbff..3758e790 100644 --- a/lib/src/main/java/tech/relaycorp/awaladroid/GatewayClientImpl.kt +++ b/lib/src/main/java/tech/relaycorp/awaladroid/GatewayClientImpl.kt @@ -89,6 +89,7 @@ internal constructor( @Throws( RegistrationFailedException::class, GatewayProtocolException::class, + GatewayUnregisteredException::class, ) internal suspend fun registerEndpoint(keyPair: KeyPair): PrivateNodeRegistration = withContext(coroutineContext) { @@ -119,6 +120,7 @@ internal constructor( ServiceInteractor.BindFailedException::class, ServiceInteractor.SendFailedException::class, GatewayProtocolException::class, + GatewayUnregisteredException::class, ) private suspend fun preRegister(): ByteArray { val interactor = serviceInteractorBuilder().apply { @@ -132,15 +134,24 @@ internal constructor( return suspendCoroutine { cont -> val request = android.os.Message.obtain(null, PREREGISTRATION_REQUEST) interactor.sendMessage(request) { replyMessage -> - if (replyMessage.what != REGISTRATION_AUTHORIZATION) { - interactor.unbind() - cont.resumeWithException( - GatewayProtocolException("Pre-registration failed, received wrong reply"), - ) - return@sendMessage - } interactor.unbind() - cont.resume(replyMessage.data.getByteArray("auth")!!) + when (replyMessage.what) { + REGISTRATION_AUTHORIZATION -> { + cont.resume(replyMessage.data.getByteArray("auth")!!) + } + GATEWAY_NOT_REGISTERED -> { + cont.resumeWithException( + GatewayUnregisteredException("Gateway not registered"), + ) + } + else -> { + cont.resumeWithException( + GatewayProtocolException( + "Pre-registration failed, received wrong reply", + ), + ) + } + } } } } @@ -209,6 +220,7 @@ internal constructor( internal companion object { internal const val PREREGISTRATION_REQUEST = 1 internal const val REGISTRATION_AUTHORIZATION = 2 + internal const val GATEWAY_NOT_REGISTERED = 4 } } @@ -230,6 +242,12 @@ public open class GatewayProtocolException(message: String, cause: Throwable? = public class GatewayBindingException(message: String, cause: Throwable? = null) : GatewayException(message, cause) +/** + * The gateway isn't yet registered with its Internet peer. + */ +public class GatewayUnregisteredException(message: String, cause: Throwable? = null) : + GatewayException(message, cause) + /** * Failure to register a first-party endpoint. */ diff --git a/lib/src/main/java/tech/relaycorp/awaladroid/endpoint/FirstPartyEndpoint.kt b/lib/src/main/java/tech/relaycorp/awaladroid/endpoint/FirstPartyEndpoint.kt index 840dc776..57001eb7 100644 --- a/lib/src/main/java/tech/relaycorp/awaladroid/endpoint/FirstPartyEndpoint.kt +++ b/lib/src/main/java/tech/relaycorp/awaladroid/endpoint/FirstPartyEndpoint.kt @@ -3,6 +3,7 @@ package tech.relaycorp.awaladroid.endpoint import tech.relaycorp.awaladroid.Awala import tech.relaycorp.awaladroid.AwaladroidException import tech.relaycorp.awaladroid.GatewayProtocolException +import tech.relaycorp.awaladroid.GatewayUnregisteredException import tech.relaycorp.awaladroid.RegistrationFailedException import tech.relaycorp.awaladroid.SetupPendingException import tech.relaycorp.awaladroid.common.Logging.logger @@ -153,6 +154,7 @@ internal constructor( @Throws( RegistrationFailedException::class, GatewayProtocolException::class, + GatewayUnregisteredException::class, PersistenceException::class, SetupPendingException::class, ) @@ -228,6 +230,7 @@ internal constructor( @Throws( RegistrationFailedException::class, GatewayProtocolException::class, + GatewayUnregisteredException::class, PersistenceException::class, SetupPendingException::class, ) diff --git a/lib/src/main/java/tech/relaycorp/awaladroid/endpoint/HandleGatewayCertificateChange.kt b/lib/src/main/java/tech/relaycorp/awaladroid/endpoint/HandleGatewayCertificateChange.kt index 9b210e9c..d9fd6fa0 100644 --- a/lib/src/main/java/tech/relaycorp/awaladroid/endpoint/HandleGatewayCertificateChange.kt +++ b/lib/src/main/java/tech/relaycorp/awaladroid/endpoint/HandleGatewayCertificateChange.kt @@ -1,5 +1,6 @@ package tech.relaycorp.awaladroid.endpoint +import tech.relaycorp.awaladroid.GatewayUnregisteredException import tech.relaycorp.relaynet.keystores.PrivateKeyStore import tech.relaycorp.relaynet.wrappers.nodeId @@ -7,6 +8,7 @@ internal class HandleGatewayCertificateChange( private val privateKeyStore: PrivateKeyStore, ) { + @Throws(GatewayUnregisteredException::class) suspend operator fun invoke() { privateKeyStore.retrieveAllIdentityKeys() .mapNotNull { FirstPartyEndpoint.load(it.nodeId) } diff --git a/lib/src/main/java/tech/relaycorp/awaladroid/endpoint/RenewExpiringCertificates.kt b/lib/src/main/java/tech/relaycorp/awaladroid/endpoint/RenewExpiringCertificates.kt index 2e35f620..b654c328 100644 --- a/lib/src/main/java/tech/relaycorp/awaladroid/endpoint/RenewExpiringCertificates.kt +++ b/lib/src/main/java/tech/relaycorp/awaladroid/endpoint/RenewExpiringCertificates.kt @@ -1,5 +1,6 @@ package tech.relaycorp.awaladroid.endpoint +import tech.relaycorp.awaladroid.GatewayUnregisteredException import tech.relaycorp.relaynet.keystores.PrivateKeyStore import tech.relaycorp.relaynet.wrappers.nodeId import tech.relaycorp.relaynet.wrappers.x509.Certificate @@ -11,6 +12,7 @@ internal class RenewExpiringCertificates( private val firstPartyEndpointLoader: suspend (String) -> FirstPartyEndpoint?, ) { + @Throws(GatewayUnregisteredException::class) suspend operator fun invoke() { privateKeyStore.retrieveAllIdentityKeys() .mapNotNull { firstPartyEndpointLoader(it.nodeId) } diff --git a/lib/src/test/java/tech/relaycorp/awaladroid/GatewayClientImplTest.kt b/lib/src/test/java/tech/relaycorp/awaladroid/GatewayClientImplTest.kt index c1db7f70..e3bf41c1 100644 --- a/lib/src/test/java/tech/relaycorp/awaladroid/GatewayClientImplTest.kt +++ b/lib/src/test/java/tech/relaycorp/awaladroid/GatewayClientImplTest.kt @@ -170,6 +170,19 @@ internal class GatewayClientImplTest : MockContextTestCase() { gatewayClient.registerEndpoint(KeyPairSet.PRIVATE_ENDPOINT) } + @Test(expected = GatewayUnregisteredException::class) + internal fun registerEndpoint_withFailedRegistrationDueToGatewayUnregistered() = + coroutineScope.runTest { + val replyMessage = Message.obtain(null, GatewayClientImpl.GATEWAY_NOT_REGISTERED) + whenever(serviceInteractor.sendMessage(any(), any())).thenAnswer { + it.getArgument<((Message) -> Unit)?>(1)(replyMessage) + } + + pdcClient = MockPDCClient() + + gatewayClient.registerEndpoint(KeyPairSet.PRIVATE_ENDPOINT) + } + private fun buildPnra() = PrivateNodeRegistrationAuthorization( ZonedDateTime.now().plusDays(1), PDACertPath.PRIVATE_GW.serialize(),