Merge remote-tracking branch 'remotes/open/master' into mnesbit-merge-20180530

# Conflicts:
#	docs/source/changelog.rst
This commit is contained in:
Matthew Nesbit 2018-05-30 11:12:25 +01:00
commit a3c6ef351d
6 changed files with 99 additions and 17 deletions

View File

@ -907,6 +907,13 @@ object Crypto {
return signatureScheme.schemeCodeName in signatureSchemeMap
}
/**
* Check if a public key satisfies algorithm specs.
* For instance, an ECC key should lie on the curve and not being point-at-infinity.
*/
@JvmStatic
fun validatePublicKey(key: PublicKey): Boolean = validatePublicKey(findSignatureScheme(key), key)
// Validate a key, by checking its algorithmic params.
private fun validateKey(signatureScheme: SignatureScheme, key: Key): Boolean {
return when (key) {
@ -920,7 +927,8 @@ object Crypto {
private fun validatePublicKey(signatureScheme: SignatureScheme, key: PublicKey): Boolean {
return when (key) {
is BCECPublicKey, is EdDSAPublicKey -> publicKeyOnCurve(signatureScheme, key)
is BCRSAPublicKey, is BCSphincs256PublicKey -> true // TODO: Check if non-ECC keys satisfy params (i.e. approved/valid RSA modulus size).
is BCRSAPublicKey -> key.modulus.bitLength() >= 2048 // Although the recommended RSA key size is 3072, we accept any key >= 2048bits.
is BCSphincs256PublicKey -> true
else -> throw IllegalArgumentException("Unsupported key type: ${key::class}")
}
}

View File

@ -8,6 +8,8 @@ Unreleased
==========
* Introduced a hierarchy of ``DatabaseMigrationException``s, allowing ``NodeStartup`` to gracefully inform users of problems related to database migrations before exiting with a non-zero code.
* Shell now kills an ongoing flow when CTRL+C is pressed in the terminal.
* ``ServiceHub`` and ``CordaRPCOps`` can now safely be used from multiple threads without incurring in database transaction problems.
* Doorman and NetworkMap url's can now be configured individually rather than being assumed to be
@ -105,6 +107,15 @@ Unreleased
(even if JPA annotation nullable=false was absent).
In case your Cordapps use this entity class to persist data in own custom tables as non Primary Key columns refer to :doc:`upgrade-notes` for upgrade instructions.
* Adding a public method to check if a public key satisfies Corda recommended algorithm specs, `Crypto.validatePublicKey(java.security.PublicKey)`.
For instance, this method will check if an ECC key lies on a valid curve or if an RSA key is >= 2048bits. This might
be required for extra key validation checks, e.g., for Doorman to check that a CSR key meets the minimum security requirements.
.. _changelog_v3.1:
Version 3.1
-----------
* Update the fast-classpath-scanner dependent library version from 2.0.21 to 2.12.3
* Added `database.hibernateDialect` node configuration option

View File

@ -13,4 +13,8 @@ package net.corda.nodeapi.internal.protonwrapper.netty
import java.net.InetSocketAddress
import java.security.cert.X509Certificate
data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509Certificate?, val connected: Boolean, val badCert: Boolean)
data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509Certificate?, val connected: Boolean, val badCert: Boolean) {
override fun toString(): String {
return "ConnectionChange remoteAddress: $remoteAddress connected state: $connected cert subject: ${remoteCert?.subjectDN} cert ok: ${!badCert}"
}
}

View File

@ -141,9 +141,10 @@ class NodeVaultService(
fun makeUpdate(tx: WireTransaction): Vault.Update<ContractState>? {
val ourNewStates = when (statesToRecord) {
StatesToRecord.NONE -> throw AssertionError("Should not reach here")
StatesToRecord.ONLY_RELEVANT -> tx.outputs.filter { isRelevant(it.data, keyManagementService.filterMyKeys(tx.outputs.flatMap { it.data.participants.map { it.owningKey } }).toSet()) }
StatesToRecord.ALL_VISIBLE -> tx.outputs
}.map { tx.outRef<ContractState>(it.data) }
StatesToRecord.ONLY_RELEVANT -> tx.outputs.withIndex().filter {
isRelevant(it.value.data, keyManagementService.filterMyKeys(tx.outputs.flatMap { it.data.participants.map { it.owningKey } }).toSet()) }
StatesToRecord.ALL_VISIBLE -> tx.outputs.withIndex()
}.map { tx.outRef<ContractState>(it.index) }
// Retrieve all unconsumed states for this transaction's inputs
val consumedStates = loadStates(tx.inputs)

View File

@ -14,10 +14,7 @@ import co.paralleluniverse.fibers.Suspendable
import com.nhaarman.mockito_kotlin.argThat
import com.nhaarman.mockito_kotlin.doNothing
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.Amount
import net.corda.core.contracts.Issued
import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.*
import net.corda.core.crypto.NullKeys
import net.corda.core.crypto.generateKeyPair
import net.corda.core.identity.*
@ -697,4 +694,50 @@ class NodeVaultServiceTest {
}
assertEquals(currentCashStates + 1, countCash())
}
@Test
fun `insert equal cash states issued by single transaction`() {
val nodeIdentity = MEGA_CORP
val coins = listOf(1.DOLLARS, 1.DOLLARS).map { it.issuedBy(nodeIdentity.ref(1)) }
//create single transaction with 2 'identical' cash outputs
val txb = TransactionBuilder(DUMMY_NOTARY)
coins.map { txb.addOutputState(TransactionState(Cash.State(it, nodeIdentity), Cash.PROGRAM_ID, DUMMY_NOTARY)) }
txb.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey)
val issueTx = txb.toWireTransaction(services)
// ensure transaction contract state is persisted in DBStorage
val signedIssuedTx = services.signInitialTransaction(txb)
(services.validatedTransactions as WritableTransactionStorage).addTransaction(signedIssuedTx)
database.transaction { vaultService.notify(StatesToRecord.ONLY_RELEVANT, issueTx) }
val recordedStates = database.transaction {
vaultService.queryBy<Cash.State>().states.size
}
assertThat(recordedStates).isEqualTo(coins.size)
}
@Test
fun `insert different cash states issued by single transaction`() {
val nodeIdentity = MEGA_CORP
val coins = listOf(2.DOLLARS, 1.DOLLARS).map { it.issuedBy(nodeIdentity.ref(1)) }
//create single transaction with 2 'identical' cash outputs
val txb = TransactionBuilder(DUMMY_NOTARY)
coins.map { txb.addOutputState(TransactionState(Cash.State(it, nodeIdentity), Cash.PROGRAM_ID, DUMMY_NOTARY)) }
txb.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey)
val issueTx = txb.toWireTransaction(services)
// ensure transaction contract state is persisted in DBStorage
val signedIssuedTx = services.signInitialTransaction(txb)
(services.validatedTransactions as WritableTransactionStorage).addTransaction(signedIssuedTx)
database.transaction { vaultService.notify(StatesToRecord.ONLY_RELEVANT, issueTx) }
val recordedStates = database.transaction {
vaultService.queryBy<Cash.State>().states.size
}
assertThat(recordedStates).isEqualTo(coins.size)
}
}

View File

@ -25,10 +25,18 @@ import net.corda.core.CordaException
import net.corda.core.concurrent.CordaFuture
import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.flows.FlowLogic
import net.corda.core.internal.*
import net.corda.core.internal.Emoji
import net.corda.core.internal.concurrent.doneFuture
import net.corda.core.internal.concurrent.openFuture
import net.corda.core.messaging.*
import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.core.internal.rootCause
import net.corda.core.internal.uncheckedCast
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.DataFeed
import net.corda.core.messaging.FlowProgressHandle
import net.corda.core.messaging.StateMachineUpdate
import net.corda.core.messaging.pendingFlowsCount
import net.corda.tools.shell.utlities.ANSIProgressRenderer
import net.corda.tools.shell.utlities.StdoutANSIProgressRenderer
import org.crsh.command.InvocationContext
@ -278,12 +286,19 @@ object InteractiveShell {
val latch = CountDownLatch(1)
ansiProgressRenderer.render(stateObservable, { latch.countDown() })
try {
// Wait for the flow to end and the progress tracker to notice. By the time the latch is released
// the tracker is done with the screen.
latch.await()
} catch (e: InterruptedException) {
// TODO: When the flow framework allows us to kill flows mid-flight, do so here.
// Wait for the flow to end and the progress tracker to notice. By the time the latch is released
// the tracker is done with the screen.
while (!Thread.currentThread().isInterrupted) {
try {
latch.await()
} catch (e: InterruptedException) {
try {
rpcOps.killFlow(stateObservable.id)
} finally {
Thread.currentThread().interrupt()
break
}
}
}
stateObservable.returnValue.get()?.apply {
if (this !is Throwable) {