From 93aba9360a10477218a058a645cc388184ebaa72 Mon Sep 17 00:00:00 2001 From: lemjclarke Date: Thu, 10 Mar 2022 08:43:53 +0000 Subject: [PATCH] Add verification --- .../encryptedtx/enclave/EncryptedTxEnclave.kt | 33 ++++++++-- .../encryptedtx/enclave/LedgerTxHelper.kt | 66 +++++++++++++++++++ 2 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 common/mock-enclave/src/main/kotlin/com/r3/conclave/encryptedtx/enclave/LedgerTxHelper.kt diff --git a/common/mock-enclave/src/main/kotlin/com/r3/conclave/encryptedtx/enclave/EncryptedTxEnclave.kt b/common/mock-enclave/src/main/kotlin/com/r3/conclave/encryptedtx/enclave/EncryptedTxEnclave.kt index 938939e613..abfb3d61ac 100644 --- a/common/mock-enclave/src/main/kotlin/com/r3/conclave/encryptedtx/enclave/EncryptedTxEnclave.kt +++ b/common/mock-enclave/src/main/kotlin/com/r3/conclave/encryptedtx/enclave/EncryptedTxEnclave.kt @@ -7,7 +7,9 @@ import net.corda.core.crypto.Crypto import net.corda.core.crypto.SignableData import net.corda.core.crypto.SignatureMetadata import net.corda.core.crypto.sign +import net.corda.core.internal.dependencies import net.corda.core.transactions.EncryptedTransaction +import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.ByteSequence import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme import net.corda.serialization.internal.AMQP_P2P_CONTEXT @@ -17,10 +19,10 @@ import net.corda.serialization.internal.amqp.SerializerFactory class EncryptedTxEnclave { - private val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256") + private val enclaveKeyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256") private val signatureMetadata = SignatureMetadata( platformVersion = 1, - schemeNumberID = Crypto.findSignatureScheme(keyPair.public).schemeNumberID + schemeNumberID = Crypto.findSignatureScheme(enclaveKeyPair.public).schemeNumberID ) private val serializerFactoriesForContexts = Caffeine.newBuilder() @@ -49,12 +51,12 @@ class EncryptedTxEnclave { val signedTransaction = ledgerTxModel.signedTransaction val signableData = SignableData(signedTransaction.id, signatureMetadata) - val transactionSignature = keyPair.sign(signableData) + val transactionSignature = enclaveKeyPair.sign(signableData) return EncryptedTransaction( id = signedTransaction.id, encryptedBytes = ledgerTxBytes, - dependencies = emptySet(), + dependencies = signedTransaction.dependencies, sigs = listOf(transactionSignature) ) } @@ -65,6 +67,27 @@ class EncryptedTxEnclave { clazz = VerifiableTxAndDependencies::class.java, context = AMQP_P2P_CONTEXT) - // TODO: add verification + val signedTransaction = txAndDependencies.conclaveLedgerTxModel.signedTransaction + signedTransaction.verifyRequiredSignatures() + + val dependencies = decryptDependencies(txAndDependencies.dependencies) + dependencies.forEach { + it.verifyRequiredSignatures() + } + + val ledgerTransaction = LedgerTxHelper.toLedgerTxInternal(txAndDependencies.conclaveLedgerTxModel, dependencies) + ledgerTransaction.verify() + } + + private fun decryptDependencies(dependencies: Set): Set { + // simply deserialize for this "mock enclave" + return dependencies.map { + val conclaveLedgerTxModel = serializationFactoryImpl.deserialize( + byteSequence = ByteSequence.of(it.encryptedBytes, 0, it.encryptedBytes.size), + clazz = ConclaveLedgerTxModel::class.java, + context = AMQP_P2P_CONTEXT) + + conclaveLedgerTxModel.signedTransaction + }.toSet() } } \ No newline at end of file diff --git a/common/mock-enclave/src/main/kotlin/com/r3/conclave/encryptedtx/enclave/LedgerTxHelper.kt b/common/mock-enclave/src/main/kotlin/com/r3/conclave/encryptedtx/enclave/LedgerTxHelper.kt new file mode 100644 index 0000000000..4d3a0232fc --- /dev/null +++ b/common/mock-enclave/src/main/kotlin/com/r3/conclave/encryptedtx/enclave/LedgerTxHelper.kt @@ -0,0 +1,66 @@ +package com.r3.conclave.encryptedtx.enclave + +import com.r3.conclave.encryptedtx.dto.ConclaveLedgerTxModel +import net.corda.core.contracts.CommandWithParties +import net.corda.core.contracts.ComponentGroupEnum +import net.corda.core.contracts.ContractState +import net.corda.core.contracts.StateRef +import net.corda.core.contracts.TransactionResolutionException +import net.corda.core.contracts.TransactionState +import net.corda.core.crypto.SecureHash +import net.corda.core.internal.SerializedStateAndRef +import net.corda.core.serialization.SerializedBytes +import net.corda.core.transactions.LedgerTransaction +import net.corda.core.transactions.SignedTransaction + +class LedgerTxHelper { + companion object { + @JvmStatic + fun toLedgerTxInternal(conclaveLedgerTxModel: ConclaveLedgerTxModel, dependencies: Set): LedgerTransaction { + val wireTransaction = conclaveLedgerTxModel.signedTransaction.tx + val dependencyMap = dependencies.associateBy { it.tx.id } + + val serializedResolvedInputs = conclaveLedgerTxModel.inputStates.map { + resolveStateRefBinaryComponent(it.ref, dependencyMap) ?: throw TransactionResolutionException(it.ref.txhash) + } + + val serializedResolvedReferences = conclaveLedgerTxModel.references.map { + resolveStateRefBinaryComponent(it.ref, dependencyMap) ?: throw TransactionResolutionException(it.ref.txhash) + } + + return LedgerTransaction.createForConclaveVerify( + inputs = conclaveLedgerTxModel.inputStates.toList(), + outputs = wireTransaction.outputs, + commands = wireTransaction.commands.map { + CommandWithParties(it.signers, emptyList(), it.value) + }, + attachments = conclaveLedgerTxModel.attachments.toList(), + id = wireTransaction.id, + notary = wireTransaction.notary, + timeWindow = wireTransaction.timeWindow, + privacySalt = wireTransaction.privacySalt, + networkParameters = conclaveLedgerTxModel.networkParameters, + references = conclaveLedgerTxModel.references.toList(), + componentGroups = wireTransaction.componentGroups, + serializedInputs = serializedResolvedInputs, + serializedReferences = serializedResolvedReferences, + digestService = wireTransaction.digestService + ) + } + + @JvmStatic + fun resolveStateRefBinaryComponent(stateRef: StateRef, dependencyMap: Map) + : SerializedStateAndRef? { + + val wireTransaction = dependencyMap[stateRef.txhash]?.tx + ?: throw TransactionResolutionException(stateRef.txhash) + + val resolvedState = wireTransaction.componentGroups + .firstOrNull { it.groupIndex == ComponentGroupEnum.OUTPUTS_GROUP.ordinal } + ?.components + ?.get(stateRef.index) as SerializedBytes>? + + return resolvedState?.let { SerializedStateAndRef(it, stateRef) } + } + } +} \ No newline at end of file