Skip to content

Commit

Permalink
Merge branch 'release/os/5.2' into yift/core-18720/introduce-session-…
Browse files Browse the repository at this point in the history
…state

# Conflicts:
#	gradle.properties
  • Loading branch information
yift-r3 committed Dec 19, 2023
2 parents aac95df + 8d934f0 commit 6fb2301
Show file tree
Hide file tree
Showing 34 changed files with 1,440 additions and 46 deletions.
1 change: 1 addition & 0 deletions components/crypto/crypto-client-impl/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ dependencies {
implementation project(':libs:crypto:cipher-suite-impl')
implementation project(':libs:crypto:crypto-core')
implementation project(":libs:messaging:messaging")
implementation project(':libs:platform-info')
implementation project(":libs:utilities")

testImplementation project(":components:crypto:crypto-component-test-utils")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package net.corda.crypto.client.impl

import net.corda.crypto.core.ApiNames.DECRYPT_PATH
import net.corda.crypto.core.ApiNames.ENCRYPT_PATH
import net.corda.crypto.core.CryptoConsts
import net.corda.crypto.core.CryptoTenants
import net.corda.data.crypto.wire.ops.encryption.request.DecryptRpcCommand
import net.corda.data.crypto.wire.ops.encryption.request.EncryptRpcCommand
import net.corda.data.crypto.wire.ops.encryption.response.CryptoDecryptionResult
import net.corda.data.crypto.wire.ops.encryption.response.CryptoEncryptionResult
import net.corda.data.crypto.wire.ops.encryption.response.DecryptionOpsResponse
import net.corda.data.crypto.wire.ops.encryption.response.EncryptionOpsError
import net.corda.data.crypto.wire.ops.encryption.response.EncryptionOpsResponse
import net.corda.libs.configuration.SmartConfig
import net.corda.libs.platform.PlatformInfoProvider
import net.corda.messaging.api.publisher.HttpRpcClient
import net.corda.messaging.api.publisher.send
import net.corda.schema.configuration.BootConfig.CRYPTO_WORKER_REST_ENDPOINT
import net.corda.v5.base.exceptions.CordaRuntimeException
import net.corda.v5.crypto.exceptions.CryptoException
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.net.URI
import java.nio.ByteBuffer

@Suppress("ThrowsCount")
class SessionEncryptionImpl(
private val sender: HttpRpcClient,
platformInfoProvider: PlatformInfoProvider,
messagingConfig: SmartConfig,
) {
private companion object {
val logger: Logger = LoggerFactory.getLogger(this::class.java.enclosingClass)
}

fun encryptSessionData(plainBytes: ByteArray, alias: String?): ByteArray {
logger.info(
"Sending '{}'(alias={})",
EncryptRpcCommand::class.java.simpleName,
alias,
)
val request = EncryptRpcCommand(
CryptoConsts.Categories.ENCRYPTION_SECRET,
alias,
ByteBuffer.wrap(plainBytes),
)

val response = sender.send<EncryptionOpsResponse>(
getRequestUrl(ENCRYPT_PATH),
request,
)?.response ?: throw CordaRuntimeException(
"Received empty response for ${request::class.java.name} for tenant '${CryptoTenants.P2P}'.",
)

return when (response) {
is CryptoEncryptionResult -> response.cipherBytes.array()
is EncryptionOpsError -> throw CryptoException(
"${response.errorMessage.errorType} - ${response.errorMessage.errorMessage}",
)
else -> throw CordaRuntimeException(
"Unexpected response type ${response::class.java} for ${request::class.java.name}.",
)
}
}

fun decryptSessionData(cipherBytes: ByteArray, alias: String?): ByteArray {
logger.info(
"Sending '{}'(alias={})",
DecryptRpcCommand::class.java.simpleName,
alias,
)
val request = DecryptRpcCommand(
CryptoConsts.Categories.ENCRYPTION_SECRET,
alias,
ByteBuffer.wrap(cipherBytes),
)
val response = sender.send<DecryptionOpsResponse>(
getRequestUrl(DECRYPT_PATH),
request,
)?.response ?: throw CordaRuntimeException(
"Received empty response for ${request::class.java.name} for tenant '${CryptoTenants.P2P}'.",
)

return when (response) {
is CryptoDecryptionResult -> response.plainBytes.array()
is EncryptionOpsError -> throw CryptoException(
"${response.errorMessage.errorType} - ${response.errorMessage.errorMessage}",
)
else -> throw CordaRuntimeException(
"Unexpected response type ${response::class.java} for ${request::class.java.name}.",
)
}
}

private val baseUrl by lazy {
val platformVersion = platformInfoProvider.localWorkerSoftwareShortVersion
"http://${messagingConfig.getString(CRYPTO_WORKER_REST_ENDPOINT)}/api/$platformVersion"
}

private fun getRequestUrl(path: String): URI {
return URI.create("$baseUrl$path")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package net.corda.crypto.client.impl

import net.corda.configuration.read.ConfigChangedEvent
import net.corda.configuration.read.ConfigurationReadService
import net.corda.crypto.client.SessionEncryptionOpsClient
import net.corda.crypto.component.impl.AbstractConfigurableComponent
import net.corda.crypto.component.impl.DependenciesTracker
import net.corda.libs.configuration.helper.getConfig
import net.corda.libs.platform.PlatformInfoProvider
import net.corda.lifecycle.LifecycleCoordinatorFactory
import net.corda.lifecycle.LifecycleCoordinatorName
import net.corda.messaging.api.publisher.factory.PublisherFactory
import net.corda.schema.configuration.ConfigKeys.MESSAGING_CONFIG
import org.osgi.service.component.annotations.Activate
import org.osgi.service.component.annotations.Component
import org.osgi.service.component.annotations.Reference

@Component(service = [SessionEncryptionOpsClient::class])
class SessionEncryptionOpsClientImpl @Activate constructor(
@Reference(service = LifecycleCoordinatorFactory::class)
coordinatorFactory: LifecycleCoordinatorFactory,
@Reference(service = PublisherFactory::class)
private val publisherFactory: PublisherFactory,
@Reference(service = ConfigurationReadService::class)
configurationReadService: ConfigurationReadService,
@Reference(service = PlatformInfoProvider::class)
val platformInfoProvider: PlatformInfoProvider,
) : AbstractConfigurableComponent<SessionEncryptionOpsClientImpl.Impl>(
coordinatorFactory = coordinatorFactory,
myName = LifecycleCoordinatorName.forComponent<SessionEncryptionOpsClient>(),
configurationReadService = configurationReadService,
upstream = DependenciesTracker.Default(
setOf(
LifecycleCoordinatorName.forComponent<ConfigurationReadService>(),
),
),
configKeys = setOf(MESSAGING_CONFIG),
),
SessionEncryptionOpsClient {
override fun encryptSessionData(plainBytes: ByteArray, alias: String?): ByteArray =
impl.ops.encryptSessionData(plainBytes, alias)

override fun decryptSessionData(cipherBytes: ByteArray, alias: String?): ByteArray =
impl.ops.decryptSessionData(cipherBytes, alias)

override fun createActiveImpl(event: ConfigChangedEvent): Impl =
Impl(publisherFactory, platformInfoProvider, event)

class Impl(
publisherFactory: PublisherFactory,
platformInfoProvider: PlatformInfoProvider,
event: ConfigChangedEvent,
) : AbstractImpl {
val ops = SessionEncryptionImpl(
publisherFactory.createHttpRpcClient(),
platformInfoProvider,
event.config.getConfig(MESSAGING_CONFIG),
)
override val downstream = DependenciesTracker.AlwaysUp()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package net.corda.crypto.client.impl

import net.corda.configuration.read.ConfigChangedEvent
import net.corda.configuration.read.ConfigurationReadService
import net.corda.data.crypto.wire.ops.encryption.request.DecryptRpcCommand
import net.corda.data.crypto.wire.ops.encryption.request.EncryptRpcCommand
import net.corda.data.crypto.wire.ops.encryption.response.CryptoDecryptionResult
import net.corda.data.crypto.wire.ops.encryption.response.CryptoEncryptionResult
import net.corda.data.crypto.wire.ops.encryption.response.EncryptionOpsResponse
import net.corda.data.crypto.wire.ops.encryption.response.DecryptionOpsResponse
import net.corda.libs.configuration.SmartConfig
import net.corda.libs.platform.PlatformInfoProvider
import net.corda.lifecycle.LifecycleCoordinator
import net.corda.lifecycle.LifecycleCoordinatorFactory
import net.corda.lifecycle.LifecycleEventHandler
import net.corda.lifecycle.LifecycleStatus
import net.corda.lifecycle.RegistrationHandle
import net.corda.lifecycle.RegistrationStatusChangeEvent
import net.corda.lifecycle.Resource
import net.corda.lifecycle.StartEvent
import net.corda.messaging.api.publisher.HttpRpcClient
import net.corda.messaging.api.publisher.factory.PublisherFactory
import net.corda.schema.configuration.BootConfig
import net.corda.schema.configuration.ConfigKeys
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import java.nio.ByteBuffer

class SessionEncryptionOpsClientComponentTest {
private val handler = argumentCaptor<LifecycleEventHandler>()
private val registrationHandle = mock<RegistrationHandle>()
private val coordinator = mock<LifecycleCoordinator>() {
on { status } doReturn LifecycleStatus.UP
on { start() } doAnswer {
handler.firstValue.processEvent(StartEvent(), mock)
}
on { followStatusChangesByName(any()) } doReturn registrationHandle
}
private val coordinatorFactory = mock<LifecycleCoordinatorFactory> {
on { createCoordinator(any(), handler.capture()) } doReturn coordinator
}
private val encryptionResponse = mock<EncryptionOpsResponse>()
private val decryptionResponse = mock<DecryptionOpsResponse>()
private val httpRpcClient = mock<HttpRpcClient> {
on { send(any(), any<EncryptRpcCommand>(), eq(EncryptionOpsResponse::class.java)) } doReturn encryptionResponse
on { send(any(), any<DecryptRpcCommand>(), eq(DecryptionOpsResponse::class.java)) } doReturn decryptionResponse
}
private val publisherFactory = mock<PublisherFactory> {
on { createHttpRpcClient() } doReturn httpRpcClient
}
private val messagingConfig = mock<SmartConfig> {
on { getString(BootConfig.CRYPTO_WORKER_REST_ENDPOINT) } doReturn "localhost:1231"
}
private val configurationReadService = mock<ConfigurationReadService> {
on { registerComponentForUpdates(any(), any()) } doAnswer {
val resource = mock<Resource>()
handler.firstValue.processEvent(
ConfigChangedEvent(
setOf(ConfigKeys.MESSAGING_CONFIG),
mapOf(ConfigKeys.MESSAGING_CONFIG to messagingConfig)
),
coordinator,
)
resource
}
}
private val platformInfoProvider = mock<PlatformInfoProvider>()
private val component = SessionEncryptionOpsClientImpl(
coordinatorFactory,
publisherFactory,
configurationReadService,
platformInfoProvider,
)

private fun start() {
component.start()
handler.firstValue.processEvent(
RegistrationStatusChangeEvent(
registrationHandle,
LifecycleStatus.UP,
),
coordinator,
)
}

@Test
fun `encryptSessionData send data to the client`() {
start()
val data = "data".toByteArray()
val results = CryptoEncryptionResult(ByteBuffer.wrap(data))
whenever(encryptionResponse.response).doReturn(results)

val encrypted = component.encryptSessionData("hello".toByteArray())

assertThat(encrypted).isEqualTo(data)
}

@Test
fun `decryptSessionData send data to the client`() {
start()
val data = "data".toByteArray()
val results = CryptoDecryptionResult(ByteBuffer.wrap(data))
whenever(decryptionResponse.response).doReturn(results)

val encrypted = component.decryptSessionData("hello".toByteArray())

assertThat(encrypted).isEqualTo(data)
}
}
Loading

0 comments on commit 6fb2301

Please sign in to comment.