Updated EnclaveClient interface

This commit is contained in:
adam.houston 2022-03-18 10:53:26 +00:00
parent fac5db73f4
commit 95adf626f9
9 changed files with 91 additions and 66 deletions

View File

@ -2,6 +2,7 @@ package com.r3.conclave.encryptedtx.enclave
import com.github.benmanes.caffeine.cache.Caffeine
import net.corda.core.conclave.common.EnclaveClient
import net.corda.core.conclave.common.FlowIdAndPayload
import net.corda.core.conclave.common.LedgerTxHelper
import net.corda.core.conclave.common.dto.ConclaveLedgerTxModel
import net.corda.core.conclave.common.dto.EncryptedVerifiableTxAndDependencies
@ -13,12 +14,6 @@ import net.corda.core.crypto.SignatureMetadata
import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.sign
import net.corda.core.internal.dependencies
import net.corda.core.node.AppServiceHub
import net.corda.core.node.ServiceHub
import net.corda.core.node.services.CordaService
import net.corda.core.serialization.ConstructorForDeserialization
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.transactions.EncryptedTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ByteSequence
@ -44,6 +39,8 @@ class EncryptedTxEnclaveClient() : EnclaveClient {
private val serializationFactoryImpl: SerializationFactoryImpl
var rejectAttestation = false
init {
val serverScheme = AMQPServerSerializationScheme(emptyList(), serializerFactoriesForContexts)
val clientScheme = AMQPServerSerializationScheme(emptyList(), serializerFactoriesForContexts)
@ -60,22 +57,27 @@ class EncryptedTxEnclaveClient() : EnclaveClient {
return byteArrayOf()
}
override fun enclaveVerifyAndEncrypt(txAndDependencies: VerifiableTxAndDependencies, checkSufficientSignatures: Boolean): EncryptedTransaction {
override fun registerRemoteEnclaveInstanceInfo(flowIdAndRemoteAttestation: FlowIdAndPayload<ByteArray>) {
// for testing
if (rejectAttestation) {
throw EnclaveClient.RemoteAttestationException("Invalid attestation")
}
}
verifyTx(txAndDependencies, checkSufficientSignatures)
override fun enclaveVerifyWithoutSignatures(txAndDependencies: VerifiableTxAndDependencies) {
verifyTx(txAndDependencies, false)
}
override fun enclaveVerifyWithSignatures(txAndDependencies: VerifiableTxAndDependencies): EncryptedTransaction {
verifyTx(txAndDependencies, true)
val ledgerTx = txAndDependencies.conclaveLedgerTxModel
val transactionSignature = getSignature(ledgerTx.signedTransaction.id)
return encrypt(ledgerTx).addSignature(transactionSignature)
}
override fun encryptTransactionForLocal(encryptedTransaction: EncryptedTransaction): EncryptedTransaction {
// no re-encryption in this mock enclave, in a real one we'd need to decrypt from the remote then re-encrypt with whatever key
// we want to use for long term storage
return encryptedTransaction
}
override fun enclaveVerify(encryptedTxAndDependencies: EncryptedVerifiableTxAndDependencies): EncryptedTransaction {
override fun enclaveVerifyWithSignatures(encryptedTxAndDependencies: EncryptedVerifiableTxAndDependencies): EncryptedTransaction {
val decrypted = decrypt(encryptedTxAndDependencies.encryptedTransaction)
val verifiableTxAndDependencies = VerifiableTxAndDependencies(
@ -90,18 +92,24 @@ class EncryptedTxEnclaveClient() : EnclaveClient {
return encrypt(decrypted).addSignature(transactionSignature)
}
override fun encryptTransactionForRemote(conclaveLedgerTxModel: ConclaveLedgerTxModel, remoteAttestation: ByteArray): EncryptedTransaction {
// just serialise in this mock enclave, in a real one we'd need to encrypt for the remote party
return encrypt(conclaveLedgerTxModel)
}
override fun encryptTransactionForRemote(encryptedTransaction: EncryptedTransaction, remoteAttestation: ByteArray): EncryptedTransaction {
override fun encryptTransactionForLocal(encryptedTransaction: EncryptedTransaction): EncryptedTransaction {
// no re-encryption in this mock enclave, in a real one we'd need to decrypt from the remote then re-encrypt with whatever key
// we want to use for long term storage
return encryptedTransaction
}
override fun encryptConclaveLedgerTxForRemote(flowIdWithConclaveLedgerTx: FlowIdAndPayload<ConclaveLedgerTxModel>) : EncryptedTransaction {
// just serialise in this mock enclave, in a real one we'd need to encrypt for the remote party
val conclaveLedgerTxModel = flowIdWithConclaveLedgerTx.payload
return encrypt(conclaveLedgerTxModel)
}
override fun encryptEncryptedTransactionForRemote(flowIdWithLocallyEncryptedTx: FlowIdAndPayload<EncryptedTransaction>): EncryptedTransaction {
// no re-encryption in this mock enclave, in a real one we'd need to decrypt from the remote then re-encrypt with whatever key
// we want to use for long term storage
return flowIdWithLocallyEncryptedTx.payload
}
private fun getSignature(transactionId : SecureHash) : TransactionSignature {
val signableData = SignableData(transactionId, signatureMetadata)
return enclaveKeyPair.sign(signableData)

View File

@ -141,25 +141,31 @@ class DummyEnclaveClient: EnclaveClient, SingletonSerializeAsToken() {
throw UnsupportedOperationException("Add your custom enclave client implementation")
}
override fun enclaveVerifyAndEncrypt(txAndDependencies: VerifiableTxAndDependencies, checkSufficientSignatures: Boolean): EncryptedTransaction {
override fun registerRemoteEnclaveInstanceInfo(flowIdAndRemoteAttestation: FlowIdAndPayload<ByteArray>) {
throw UnsupportedOperationException("Add your custom enclave client implementation")
}
override fun enclaveVerify(encryptedTxAndDependencies: EncryptedVerifiableTxAndDependencies) : EncryptedTransaction {
override fun enclaveVerifyWithoutSignatures(txAndDependencies: VerifiableTxAndDependencies) {
throw UnsupportedOperationException("Add your custom enclave client implementation")
}
override fun encryptTransactionForLocal(encryptedTransaction: EncryptedTransaction): EncryptedTransaction {
override fun enclaveVerifyWithSignatures(txAndDependencies: VerifiableTxAndDependencies): EncryptedTransaction {
throw UnsupportedOperationException("Add your custom enclave client implementation")
}
override fun encryptTransactionForRemote(conclaveLedgerTxModel: ConclaveLedgerTxModel,
remoteAttestation: ByteArray): EncryptedTransaction {
override fun enclaveVerifyWithSignatures(encryptedTxAndDependencies: EncryptedVerifiableTxAndDependencies): EncryptedTransaction {
throw UnsupportedOperationException("Add your custom enclave client implementation")
}
override fun encryptTransactionForRemote(encryptedTransaction: EncryptedTransaction,
remoteAttestation: ByteArray): EncryptedTransaction {
override fun encryptTransactionForLocal(remoteEncryptedTransaction: EncryptedTransaction): EncryptedTransaction {
throw UnsupportedOperationException("Add your custom enclave client implementation")
}
override fun encryptConclaveLedgerTxForRemote(flowIdWithConclaveLedgerTx: FlowIdAndPayload<ConclaveLedgerTxModel>): EncryptedTransaction {
throw UnsupportedOperationException("Add your custom enclave client implementation")
}
override fun encryptEncryptedTransactionForRemote(flowIdWithLocallyEncryptedTx: FlowIdAndPayload<EncryptedTransaction>): EncryptedTransaction {
throw UnsupportedOperationException("Add your custom enclave client implementation")
}
}

View File

@ -312,11 +312,13 @@ abstract class SignTransactionFlow @JvmOverloads constructor(val otherSideSessio
validatedTxSvc.getTransaction(it)
}.toSet()
encryptionService.enclaveVerifyAndEncrypt(VerifiableTxAndDependencies(
encryptionService.enclaveVerifyWithoutSignatures(
VerifiableTxAndDependencies(
conclaveLedgerTxModel!!,
signedTxs,
encryptedTxs
), false)
)
)
} else {
stx.tx.toLedgerTransaction(serviceHub).verify()

View File

@ -8,9 +8,14 @@ class ExchangeAttestationFlow(private val session : FlowSession) : FlowLogic<Byt
@Suspendable
override fun call() : ByteArray {
val ourAttestationBytes = serviceHub.encryptedTransactionService.getEnclaveInstance()
val encSvc = serviceHub.encryptedTransactionService
val ourAttestationBytes = encSvc.getEnclaveInstance()
return session.sendAndReceive<ByteArray>(ourAttestationBytes).unwrap { it }
val theirAttestationBytes = session.sendAndReceive<ByteArray>(ourAttestationBytes).unwrap { it }
// this can throw if the enclave does not believe the attestation to be valid
encSvc.registerRemoteEnclaveInstanceInfo(runId.uuid, theirAttestationBytes)
return theirAttestationBytes
}
}
@ -19,9 +24,12 @@ class ExchangeAttestationFlowHandler(private val otherSideSession: FlowSession)
@Suspendable
override fun call(): ByteArray {
val ourAttestationBytes = serviceHub.encryptedTransactionService.getEnclaveInstance()
val encSvc = serviceHub.encryptedTransactionService
val ourAttestationBytes = encSvc.getEnclaveInstance()
val theirAttestationBytes = otherSideSession.receive<ByteArray>().unwrap { it }
// this can throw if the enclave does not believe the attestation to be valid
encSvc.registerRemoteEnclaveInstanceInfo(runId.uuid, theirAttestationBytes)
otherSideSession.send(ourAttestationBytes)

View File

@ -120,10 +120,12 @@ abstract class ReceiveTransactionFlowBase<T> @JvmOverloads constructor(private v
signedTxs,
encryptedTxs
)
val encryptedAndVerifiedTx = serviceHub.encryptedTransactionService.enclaveVerifyAndEncrypt(verifiableTx, checkSufficientSignatures)
if (checkSufficientSignatures) {
val encryptedAndVerifiedTx = serviceHub.encryptedTransactionService.enclaveVerifyWithSignatures(verifiableTx)
serviceHub.recordEncryptedTransactions(listOf(encryptedAndVerifiedTx))
} else {
serviceHub.encryptedTransactionService.enclaveVerifyWithoutSignatures(verifiableTx)
}
it

View File

@ -93,7 +93,6 @@ open class DataVendingFlow(val otherSideSession: FlowSession, val payload: Any,
override fun call(): Void? {
val encryptSvc = serviceHub.encryptedTransactionService
var remoteAttestation : ByteArray? = null
val networkMaxMessageSize = serviceHub.networkParameters.maxMessageSize
val maxPayloadSize = networkMaxMessageSize / 2
@ -122,8 +121,9 @@ open class DataVendingFlow(val otherSideSession: FlowSession, val payload: Any,
}
if (encrypted) {
// The first step in an encrypted exchange, is to request an exchange of attestations
remoteAttestation = subFlow(ExchangeAttestationFlow(otherSideSession))
// The first step in an encrypted exchange, is to request an exchange of attestations. This will also cache the attestation
// in the enclave for future use
subFlow(ExchangeAttestationFlow(otherSideSession))
// also send the ledger transaction
if (payload is SignedTransaction) {
@ -168,18 +168,15 @@ open class DataVendingFlow(val otherSideSession: FlowSession, val payload: Any,
}
if (encrypted) {
val remoteAtt = remoteAttestation ?:
throw IllegalStateException("Cannot share encrypted backchain without a remote attestation")
val flowId = runId.uuid
val encryptedTx = serviceHub.validatedTransactions.getEncryptedTransaction(txId)
val encryptedTransactionToSend = if (encryptedTx != null) {
encryptSvc.encryptTransactionForRemote(encryptedTx, remoteAtt)
encryptSvc.encryptTransactionForRemote(flowId, encryptedTx)
} else {
val tx = serviceHub.validatedTransactions.getTransaction(txId)
?: throw FetchDataFlow.HashNotFound(txId)
encryptSvc.encryptTransactionForRemote(tx.toLedgerTxModel(serviceHub), remoteAtt)
encryptSvc.encryptTransactionForRemote(flowId, tx.toLedgerTxModel(serviceHub))
}
authorisedTransactions.removeAuthorised(txId)

View File

@ -48,8 +48,7 @@ import java.util.*
sealed class FetchDataFlow<T : NamedByHash, in W : Any>(
protected val requests: Set<SecureHash>,
protected val otherSideSession: FlowSession,
protected val dataType: DataType,
protected val remoteAttestation: ByteArray? = null) : FlowLogic<FetchDataFlow.Result<T>>() {
protected val dataType: DataType) : FlowLogic<FetchDataFlow.Result<T>>() {
@CordaSerializable
class DownloadedVsRequestedDataMismatch(val requested: SecureHash, val got: SecureHash) : IllegalArgumentException()
@ -275,17 +274,12 @@ class FetchTransactionsFlow(requests: Set<SecureHash>, otherSide: FlowSession) :
override fun load(txid: SecureHash): SignedTransaction? = serviceHub.validatedTransactions.getTransaction(txid)
}
class FetchEncryptedTransactionsFlow(requests: Set<SecureHash>, otherSide: FlowSession, remoteAttestation: ByteArray) :
FetchDataFlow<EncryptedTransaction, EncryptedTransaction>(requests, otherSide, DataType.TRANSACTION, remoteAttestation) {
class FetchEncryptedTransactionsFlow(requests: Set<SecureHash>, otherSide: FlowSession) :
FetchDataFlow<EncryptedTransaction, EncryptedTransaction>(requests, otherSide, DataType.TRANSACTION) {
override fun load(txid: SecureHash): EncryptedTransaction? {
require(remoteAttestation != null) {
"FetchEncryptedTransactionsFlow requires a remoteAttestation"
}
return serviceHub.validatedTransactions.getEncryptedTransaction(txid)?.let {
serviceHub.encryptedTransactionService.encryptTransactionForRemote(it, remoteAttestation!!)
serviceHub.encryptedTransactionService.encryptTransactionForRemote(runId.uuid,it)
}
}
}

View File

@ -2,11 +2,13 @@ package net.corda.core.node.services
import net.corda.core.conclave.common.DummyEnclaveClient
import net.corda.core.conclave.common.EnclaveClient
import net.corda.core.conclave.common.FlowIdAndPayload
import net.corda.core.conclave.common.dto.ConclaveLedgerTxModel
import net.corda.core.conclave.common.dto.EncryptedVerifiableTxAndDependencies
import net.corda.core.conclave.common.dto.VerifiableTxAndDependencies
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.transactions.EncryptedTransaction
import java.util.*
class EncryptedTransactionService(val enclaveClient: EnclaveClient = DummyEnclaveClient()) : SingletonSerializeAsToken() {
@ -14,25 +16,31 @@ class EncryptedTransactionService(val enclaveClient: EnclaveClient = DummyEnclav
return enclaveClient.getEnclaveInstanceInfo()
}
fun enclaveVerifyAndEncrypt(txAndDependencies : VerifiableTxAndDependencies, checkSufficientSignatures: Boolean = true): EncryptedTransaction {
return enclaveClient.enclaveVerifyAndEncrypt(txAndDependencies, checkSufficientSignatures)
fun registerRemoteEnclaveInstanceInfo(flowId : UUID, remoteAttestation: ByteArray) {
enclaveClient.registerRemoteEnclaveInstanceInfo(FlowIdAndPayload(flowId, remoteAttestation))
}
fun enclaveVerifyAndEncrypt(encryptedTxAndDependencies: EncryptedVerifiableTxAndDependencies) : EncryptedTransaction {
return enclaveClient.enclaveVerify(encryptedTxAndDependencies)
fun enclaveVerifyWithoutSignatures(txAndDependencies : VerifiableTxAndDependencies) {
return enclaveClient.enclaveVerifyWithoutSignatures(txAndDependencies)
}
fun enclaveVerifyWithSignatures(txAndDependencies : VerifiableTxAndDependencies) : EncryptedTransaction {
return enclaveClient.enclaveVerifyWithSignatures(txAndDependencies)
}
fun enclaveVerifyWithSignatures(encryptedTxAndDependencies: EncryptedVerifiableTxAndDependencies) : EncryptedTransaction {
return enclaveClient.enclaveVerifyWithSignatures(encryptedTxAndDependencies)
}
fun encryptTransactionForLocal(encryptedTransaction: EncryptedTransaction): EncryptedTransaction {
return enclaveClient.encryptTransactionForLocal(encryptedTransaction)
}
fun encryptTransactionForRemote(conclaveLedgerTxModel: ConclaveLedgerTxModel,
remoteAttestation: ByteArray): EncryptedTransaction {
return enclaveClient.encryptTransactionForRemote(conclaveLedgerTxModel, remoteAttestation)
fun encryptTransactionForRemote(flowId: UUID, conclaveLedgerTxModel: ConclaveLedgerTxModel): EncryptedTransaction {
return enclaveClient.encryptConclaveLedgerTxForRemote(FlowIdAndPayload(flowId,conclaveLedgerTxModel))
}
fun encryptTransactionForRemote(encryptedTransaction: EncryptedTransaction,
remoteAttestation: ByteArray): EncryptedTransaction {
return enclaveClient.encryptTransactionForRemote(encryptedTransaction, remoteAttestation)
fun encryptTransactionForRemote(flowId: UUID, encryptedTransaction: EncryptedTransaction): EncryptedTransaction {
return enclaveClient.encryptEncryptedTransactionForRemote(FlowIdAndPayload(flowId, encryptedTransaction))
}
}

View File

@ -195,7 +195,7 @@ class DbTransactionsResolver(private val flow: ResolveTransactionsFlow) : Transa
val signedTransactions = tx.dependencies.mapNotNull { transactionStorage.getTransaction(it) }.toSet()
val encryptedTransactions = tx.dependencies.mapNotNull { transactionStorage.getEncryptedTransaction(it) }.toSet()
val verifiedTransaction = encryptSvc.enclaveVerifyAndEncrypt(
val verifiedTransaction = encryptSvc.enclaveVerifyWithSignatures(
EncryptedVerifiableTxAndDependencies(
tx,
signedTransactions,
@ -229,7 +229,7 @@ class DbTransactionsResolver(private val flow: ResolveTransactionsFlow) : Transa
val remoteAttestation = flow.remoteAttestation ?:
throw IllegalStateException("fetchEncryptedRequiredTransactions requires a remoteAttestation")
val requestedTxs = flow.subFlow(FetchEncryptedTransactionsFlow(requests, flow.otherSide, remoteAttestation))
val requestedTxs = flow.subFlow(FetchEncryptedTransactionsFlow(requests, flow.otherSide))
return Pair(requestedTxs.fromDisk.map { it.id }, requestedTxs.downloaded)
}