mirror of
https://github.com/corda/corda.git
synced 2024-12-24 15:16:45 +00:00
Encrypted transaction signatures and dependencies stored
This commit is contained in:
parent
bba864d86d
commit
c7cc817386
@ -27,6 +27,7 @@ class MigrationNamedCacheFactory(private val metricRegistry: MetricRegistry?,
|
|||||||
"DBTransactionStorage_transactions" -> caffeine.maximumWeight(
|
"DBTransactionStorage_transactions" -> caffeine.maximumWeight(
|
||||||
nodeConfiguration?.transactionCacheSizeBytes ?: NodeConfiguration.defaultTransactionCacheSize
|
nodeConfiguration?.transactionCacheSizeBytes ?: NodeConfiguration.defaultTransactionCacheSize
|
||||||
)
|
)
|
||||||
|
"DBTransactionStorage_encrypted_transactions" -> caffeine.maximumSize(defaultCacheSize)
|
||||||
"PersistentIdentityService_keyToPartyAndCert" -> caffeine.maximumSize(defaultCacheSize)
|
"PersistentIdentityService_keyToPartyAndCert" -> caffeine.maximumSize(defaultCacheSize)
|
||||||
"PersistentIdentityService_nameToParty" -> caffeine.maximumSize(defaultCacheSize)
|
"PersistentIdentityService_nameToParty" -> caffeine.maximumSize(defaultCacheSize)
|
||||||
"PersistentIdentityService_keyToParty" -> caffeine.maximumSize(defaultCacheSize)
|
"PersistentIdentityService_keyToParty" -> caffeine.maximumSize(defaultCacheSize)
|
||||||
|
@ -130,7 +130,6 @@ object VaultMigrationSchema
|
|||||||
object VaultMigrationSchemaV1 : MappedSchema(schemaFamily = VaultMigrationSchema.javaClass, version = 1,
|
object VaultMigrationSchemaV1 : MappedSchema(schemaFamily = VaultMigrationSchema.javaClass, version = 1,
|
||||||
mappedTypes = listOf(
|
mappedTypes = listOf(
|
||||||
DBTransactionStorage.DBTransaction::class.java,
|
DBTransactionStorage.DBTransaction::class.java,
|
||||||
DBTransactionStorage.DBEncryptedTransaction::class.java,
|
|
||||||
PersistentIdentityService.PersistentPublicKeyHashToCertificate::class.java,
|
PersistentIdentityService.PersistentPublicKeyHashToCertificate::class.java,
|
||||||
PersistentIdentityService.PersistentPublicKeyHashToParty::class.java,
|
PersistentIdentityService.PersistentPublicKeyHashToParty::class.java,
|
||||||
BasicHSMKeyManagementService.PersistentKey::class.java,
|
BasicHSMKeyManagementService.PersistentKey::class.java,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package net.corda.node.services.persistence
|
package net.corda.node.services.persistence
|
||||||
|
|
||||||
import com.github.benmanes.caffeine.cache.Weigher
|
|
||||||
import net.corda.core.concurrent.CordaFuture
|
import net.corda.core.concurrent.CordaFuture
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.TransactionSignature
|
import net.corda.core.crypto.TransactionSignature
|
||||||
@ -77,7 +76,26 @@ class DBTransactionStorage(private val database: CordaPersistence, cacheFactory:
|
|||||||
val status: TransactionStatus,
|
val status: TransactionStatus,
|
||||||
|
|
||||||
@Column(name = "timestamp", nullable = false)
|
@Column(name = "timestamp", nullable = false)
|
||||||
val timestamp: Instant
|
val timestamp: Instant,
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
@CollectionTable(
|
||||||
|
name="${NODE_DATABASE_PREFIX}encrypted_transactions_dependencies",
|
||||||
|
joinColumns = [JoinColumn(name = "tx_id", referencedColumnName = "tx_id")],
|
||||||
|
foreignKey = ForeignKey(name = "FK__dependencies__encrytpedtx")
|
||||||
|
)
|
||||||
|
@Column(name="dependency")
|
||||||
|
val dependencies: List<String>,
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
@CollectionTable(
|
||||||
|
name="${NODE_DATABASE_PREFIX}encrypted_transactions_signatures",
|
||||||
|
joinColumns = [JoinColumn(name = "tx_id", referencedColumnName = "tx_id")],
|
||||||
|
foreignKey = ForeignKey(name = "FK__signers__encrytpedtx")
|
||||||
|
)
|
||||||
|
@Lob
|
||||||
|
@Column(name="signature")
|
||||||
|
val signatures: List<ByteArray>
|
||||||
)
|
)
|
||||||
|
|
||||||
enum class TransactionStatus {
|
enum class TransactionStatus {
|
||||||
@ -176,8 +194,8 @@ class DBTransactionStorage(private val database: CordaPersistence, cacheFactory:
|
|||||||
EncryptedTransaction(
|
EncryptedTransaction(
|
||||||
SecureHash.parse(it.txId),
|
SecureHash.parse(it.txId),
|
||||||
it.transaction,
|
it.transaction,
|
||||||
emptySet(),
|
it.dependencies.map { hashString -> SecureHash.parse(hashString) }.toSet(),
|
||||||
emptyList()
|
it.signatures.map { signature -> signature.deserialize<TransactionSignature>() }
|
||||||
),
|
),
|
||||||
it.status
|
it.status
|
||||||
)
|
)
|
||||||
@ -188,7 +206,9 @@ class DBTransactionStorage(private val database: CordaPersistence, cacheFactory:
|
|||||||
stateMachineRunId = FlowStateMachineImpl.currentStateMachine()?.id?.uuid?.toString(),
|
stateMachineRunId = FlowStateMachineImpl.currentStateMachine()?.id?.uuid?.toString(),
|
||||||
transaction = value.encryptedBytes,
|
transaction = value.encryptedBytes,
|
||||||
status = value.status,
|
status = value.status,
|
||||||
timestamp = clock.instant()
|
timestamp = clock.instant(),
|
||||||
|
dependencies = value.dependencies.map { it.toHexString() },
|
||||||
|
signatures = value.signatures.map { it.serialize().bytes }
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
persistentEntityClass = DBEncryptedTransaction::class.java
|
persistentEntityClass = DBEncryptedTransaction::class.java
|
||||||
@ -431,14 +451,18 @@ class DBTransactionStorage(private val database: CordaPersistence, cacheFactory:
|
|||||||
private data class TxCacheEncryptedValue(
|
private data class TxCacheEncryptedValue(
|
||||||
val id: SecureHash,
|
val id: SecureHash,
|
||||||
val encryptedBytes: ByteArray,
|
val encryptedBytes: ByteArray,
|
||||||
val status: TransactionStatus
|
val status: TransactionStatus,
|
||||||
|
val dependencies: Set<SecureHash>,
|
||||||
|
val signatures: List<TransactionSignature>
|
||||||
) {
|
) {
|
||||||
constructor(encryptedTransaction: EncryptedTransaction, status: TransactionStatus) : this(
|
constructor(encryptedTransaction: EncryptedTransaction, status: TransactionStatus) : this(
|
||||||
encryptedTransaction.id,
|
encryptedTransaction.id,
|
||||||
encryptedTransaction.encryptedBytes,
|
encryptedTransaction.encryptedBytes,
|
||||||
status)
|
status,
|
||||||
|
encryptedTransaction.dependencies,
|
||||||
|
encryptedTransaction.sigs)
|
||||||
|
|
||||||
fun toEncryptedTx() = EncryptedTransaction(id, encryptedBytes, emptySet(), emptyList())
|
fun toEncryptedTx() = EncryptedTransaction(id, encryptedBytes, dependencies, signatures)
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
|
@ -42,7 +42,7 @@ open class DefaultNamedCacheFactory protected constructor(private val metricRegi
|
|||||||
name == "SerializationScheme_attachmentClassloader" -> caffeine
|
name == "SerializationScheme_attachmentClassloader" -> caffeine
|
||||||
name == "HibernateConfiguration_sessionFactories" -> caffeine.maximumSize(database.mappedSchemaCacheSize)
|
name == "HibernateConfiguration_sessionFactories" -> caffeine.maximumSize(database.mappedSchemaCacheSize)
|
||||||
name == "DBTransactionStorage_transactions" -> caffeine.maximumWeight(transactionCacheSizeBytes)
|
name == "DBTransactionStorage_transactions" -> caffeine.maximumWeight(transactionCacheSizeBytes)
|
||||||
name == "DBTransactionStorage_encrypted_transactions" -> caffeine.maximumWeight(transactionCacheSizeBytes)
|
name == "DBTransactionStorage_encrypted_transactions" -> caffeine.maximumSize(defaultCacheSize)
|
||||||
name == "NodeAttachmentService_attachmentContent" -> caffeine.maximumWeight(attachmentContentCacheSizeBytes)
|
name == "NodeAttachmentService_attachmentContent" -> caffeine.maximumWeight(attachmentContentCacheSizeBytes)
|
||||||
name == "NodeAttachmentService_contractAttachmentVersions" -> caffeine.maximumSize(defaultCacheSize)
|
name == "NodeAttachmentService_contractAttachmentVersions" -> caffeine.maximumSize(defaultCacheSize)
|
||||||
name == "PersistentIdentityService_keyToPartyAndCert" -> caffeine.maximumSize(defaultCacheSize)
|
name == "PersistentIdentityService_keyToPartyAndCert" -> caffeine.maximumSize(defaultCacheSize)
|
||||||
|
@ -38,4 +38,6 @@
|
|||||||
|
|
||||||
<include file="migration/node-core.changelog-v21.xml"/>
|
<include file="migration/node-core.changelog-v21.xml"/>
|
||||||
|
|
||||||
|
<include file="migration/node-core.changelog-v22-encryption.xml"/>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
@ -24,7 +24,32 @@
|
|||||||
</createTable>
|
</createTable>
|
||||||
</changeSet>
|
</changeSet>
|
||||||
<changeSet author="R3.Corda" id="encrypted_transactions_add_primary_key">
|
<changeSet author="R3.Corda" id="encrypted_transactions_add_primary_key">
|
||||||
<addPrimaryKey columnNames="tx_id" constraintName="node_encrypted_transactions_pkey" tableName="node_encrypted_transactions"
|
<addPrimaryKey columnNames="tx_id" constraintName="node_encrypted_transactions_pkey" tableName="node_encrypted_transactions"/>
|
||||||
clustered="false"/>
|
|
||||||
</changeSet>
|
</changeSet>
|
||||||
</databaseChangeLog>
|
<changeSet author="R3.Corda" id="create_encrypted_transactions_dependencies">
|
||||||
|
<createTable tableName="node_encrypted_transactions_dependencies">
|
||||||
|
<column name="tx_id" type="NVARCHAR(64)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="dependency" type="NVARCHAR(64)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
<addForeignKeyConstraint baseColumnNames="tx_id" baseTableName="node_encrypted_transactions_dependencies"
|
||||||
|
constraintName="FK__dependencies__encrytpedtx"
|
||||||
|
referencedColumnNames="tx_id" referencedTableName="node_encrypted_transactions"/>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet author="R3.Corda" id="create_encrypted_transactions_signatures">
|
||||||
|
<createTable tableName="node_encrypted_transactions_signatures">
|
||||||
|
<column name="tx_id" type="NVARCHAR(64)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="signature" type="blob">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
<addForeignKeyConstraint baseColumnNames="tx_id" baseTableName="node_encrypted_transactions_signatures"
|
||||||
|
constraintName="FK__signers__encrytpedtx"
|
||||||
|
referencedColumnNames="tx_id" referencedTableName="node_encrypted_transactions"/>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
|
@ -5,8 +5,11 @@ import net.corda.core.concurrent.CordaFuture
|
|||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.SignableData
|
||||||
import net.corda.core.crypto.SignatureMetadata
|
import net.corda.core.crypto.SignatureMetadata
|
||||||
import net.corda.core.crypto.TransactionSignature
|
import net.corda.core.crypto.TransactionSignature
|
||||||
|
import net.corda.core.crypto.sign
|
||||||
|
import net.corda.core.internal.dependencies
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.serialization.SerializationDefaults
|
import net.corda.core.serialization.SerializationDefaults
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
@ -430,18 +433,36 @@ class DBTransactionStorageTests {
|
|||||||
encryptionCipher.init(Cipher.ENCRYPT_MODE, key, iv)
|
encryptionCipher.init(Cipher.ENCRYPT_MODE, key, iv)
|
||||||
|
|
||||||
val encryptedTxBytes = encryptionCipher.doFinal(transaction.serialize(context = contextToUse().withEncoding(CordaSerializationEncoding.SNAPPY)).bytes)
|
val encryptedTxBytes = encryptionCipher.doFinal(transaction.serialize(context = contextToUse().withEncoding(CordaSerializationEncoding.SNAPPY)).bytes)
|
||||||
val encryptedTx = EncryptedTransaction(transaction.id, encryptedTxBytes, emptySet(), emptyList())
|
|
||||||
|
// let's sign with 2 keys to ensure we can store multiple sigs
|
||||||
|
val enclaveKeyPairs = (0..1).map {
|
||||||
|
Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
|
||||||
|
}
|
||||||
|
|
||||||
|
val signatures = enclaveKeyPairs.map {
|
||||||
|
val signatureMetadata = SignatureMetadata(
|
||||||
|
platformVersion = 1,
|
||||||
|
schemeNumberID = Crypto.findSignatureScheme(it.public).schemeNumberID
|
||||||
|
)
|
||||||
|
val signableData = SignableData(transaction.id, signatureMetadata)
|
||||||
|
it.sign(signableData)
|
||||||
|
}
|
||||||
|
|
||||||
|
val encryptedTx = EncryptedTransaction(transaction.id, encryptedTxBytes, transaction.dependencies, signatures)
|
||||||
|
|
||||||
transactionStorage.addVerifiedEncryptedTransaction(encryptedTx)
|
transactionStorage.addVerifiedEncryptedTransaction(encryptedTx)
|
||||||
|
|
||||||
val storedTx = transactionStorage.getEncryptedTransaction(transaction.id)
|
|
||||||
|
|
||||||
val decryptionCipher = Cipher.getInstance(cipherTransformation)
|
val decryptionCipher = Cipher.getInstance(cipherTransformation)
|
||||||
decryptionCipher.init(Cipher.DECRYPT_MODE, key, iv)
|
decryptionCipher.init(Cipher.DECRYPT_MODE, key, iv)
|
||||||
|
|
||||||
assertNotNull(storedTx, "Could not find stored encrypted message")
|
val storedTx = transactionStorage.getEncryptedTransaction(transaction.id) ?:
|
||||||
|
throw IllegalStateException("Could not find stored encrypted message")
|
||||||
|
|
||||||
val decryptedTx = decryptionCipher.doFinal(storedTx!!.encryptedBytes).deserialize<SignedTransaction>(context = contextToUse())
|
val decryptedTx = decryptionCipher.doFinal(storedTx.encryptedBytes).deserialize<SignedTransaction>(context = contextToUse())
|
||||||
|
|
||||||
|
assertEquals(transaction.dependencies, storedTx.dependencies)
|
||||||
|
assertEquals(signatures.toSet(), storedTx.sigs.toSet())
|
||||||
|
|
||||||
assertEquals(decryptedTx, transaction)
|
assertEquals(decryptedTx, transaction)
|
||||||
|
|
||||||
@ -465,7 +486,7 @@ class DBTransactionStorageTests {
|
|||||||
|
|
||||||
private fun newTransaction(): SignedTransaction {
|
private fun newTransaction(): SignedTransaction {
|
||||||
val wtx = createWireTransaction(
|
val wtx = createWireTransaction(
|
||||||
inputs = listOf(StateRef(SecureHash.randomSHA256(), 0)),
|
inputs = listOf(StateRef(SecureHash.randomSHA256(), 0), StateRef(SecureHash.randomSHA256(), 0)),
|
||||||
attachments = emptyList(),
|
attachments = emptyList(),
|
||||||
outputs = emptyList(),
|
outputs = emptyList(),
|
||||||
commands = listOf(dummyCommand()),
|
commands = listOf(dummyCommand()),
|
||||||
|
Loading…
Reference in New Issue
Block a user