HSM signing of network parameters (#363)

Add HSM signing of network parameters
This commit is contained in:
Katarzyna Streich 2018-01-18 12:05:18 +00:00 committed by GitHub
parent af21f6065d
commit f2f803ecd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 144 additions and 92 deletions

View File

@ -3,6 +3,7 @@ package com.r3.corda.networkmanage.common.persistence
import com.r3.corda.networkmanage.common.utils.SignedNetworkMap import com.r3.corda.networkmanage.common.utils.SignedNetworkMap
import com.r3.corda.networkmanage.common.utils.SignedNetworkParameters import com.r3.corda.networkmanage.common.utils.SignedNetworkParameters
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.internal.DigitalSignatureWithCert
import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.NetworkParameters
/** /**
@ -31,26 +32,28 @@ interface NetworkMapStorage {
fun saveNetworkMap(signedNetworkMap: SignedNetworkMap) fun saveNetworkMap(signedNetworkMap: SignedNetworkMap)
/** /**
* Return the signed network parameters object which matches the given hash. The hash is that of the underlying * Retrieve the signed with certificate network parameters by their hash. The hash is that of the underlying
* [NetworkParameters] object and not the `SignedData<NetworkParameters>` object that's returned. * [NetworkParameters] object and not the `SignedWithCert<NetworkParameters>` object that's returned.
* @return signed network parameters corresponding to the given hash or null if it does not exist (parameters don't exist or they haven't been signed yet)
*/ */
fun getSignedNetworkParameters(hash: SecureHash): SignedNetworkParameters? fun getSignedNetworkParameters(hash: SecureHash): SignedNetworkParameters?
/** /**
* Retrieve network map parameters. * Retrieve network map parameters.
* @return current network map parameters or null if they don't exist * @return signed current network map parameters or null if they don't exist
*/ */
fun getCurrentNetworkParameters(): NetworkParameters? fun getCurrentSignedNetworkParameters(): SignedNetworkParameters?
/** /**
* Persists given network parameters. * Persists given network parameters with signature if provided.
* @return hash corresponding to newly create network parameters entry * @return hash corresponding to newly created network parameters entry
*/ */
fun saveNetworkParameters(networkParameters: NetworkParameters): SecureHash fun saveNetworkParameters(networkParameters: NetworkParameters, sig: DigitalSignatureWithCert?): SecureHash
/** /**
* Retrieves the latest (i.e. most recently inserted) network parameters * Retrieves the latest (i.e. most recently inserted) network parameters
* Note that they may not have been signed up yet.
* @return latest network parameters * @return latest network parameters
*/ */
fun getLatestNetworkParameters(): NetworkParameters fun getLatestUnsignedNetworkParameters(): NetworkParameters
} }

View File

@ -3,9 +3,9 @@ package com.r3.corda.networkmanage.common.persistence
import com.r3.corda.networkmanage.common.persistence.entity.* import com.r3.corda.networkmanage.common.persistence.entity.*
import com.r3.corda.networkmanage.common.utils.SignedNetworkMap import com.r3.corda.networkmanage.common.utils.SignedNetworkMap
import com.r3.corda.networkmanage.common.utils.SignedNetworkParameters import com.r3.corda.networkmanage.common.utils.SignedNetworkParameters
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256 import net.corda.core.crypto.sha256
import net.corda.core.internal.DigitalSignatureWithCert
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
@ -16,7 +16,7 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence
/** /**
* Database implementation of the [NetworkMapStorage] interface * Database implementation of the [NetworkMapStorage] interface
*/ */
class PersistentNetworkMapStorage(private val database: CordaPersistence, private val localSigner: LocalSigner?) : NetworkMapStorage { class PersistentNetworkMapStorage(private val database: CordaPersistence) : NetworkMapStorage {
override fun getCurrentNetworkMap(): SignedNetworkMap? { override fun getCurrentNetworkMap(): SignedNetworkMap? {
return database.transaction { return database.transaction {
getCurrentNetworkMapEntity()?.let { getCurrentNetworkMapEntity()?.let {
@ -26,11 +26,11 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence, privat
} }
} }
override fun getCurrentNetworkParameters(): NetworkParameters? { override fun getCurrentSignedNetworkParameters(): SignedNetworkParameters? {
return database.transaction { return database.transaction {
getCurrentNetworkMapEntity()?.let { getCurrentNetworkMapEntity()?.let {
val netParamsHash = it.networkMap.deserialize<NetworkMap>().networkParameterHash val netParamsHash = it.networkMap.deserialize<NetworkMap>().networkParameterHash
getNetworkParametersEntity(netParamsHash.toString())?.networkParameters() getSignedNetworkParameters(netParamsHash)
} }
} }
} }
@ -46,12 +46,8 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence, privat
} }
} }
// TODO The signing cannot occur here as it won't work with an HSM. The signed network parameters needs to be persisted
// into the database.
override fun getSignedNetworkParameters(hash: SecureHash): SignedNetworkParameters? { override fun getSignedNetworkParameters(hash: SecureHash): SignedNetworkParameters? {
val netParamsBytes = getNetworkParametersEntity(hash.toString())?.parametersBytes ?: return null return getNetworkParametersEntity(hash.toString())?.signedParameters()
val sigWithCert = localSigner!!.signBytes(netParamsBytes)
return SignedNetworkParameters(SerializedBytes(netParamsBytes), sigWithCert)
} }
override fun getNodeInfoHashes(certificateStatus: CertificateStatus): List<SecureHash> { override fun getNodeInfoHashes(certificateStatus: CertificateStatus): List<SecureHash> {
@ -69,31 +65,32 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence, privat
} }
} }
override fun saveNetworkParameters(networkParameters: NetworkParameters): SecureHash { override fun saveNetworkParameters(networkParameters: NetworkParameters, sig: DigitalSignatureWithCert?): SecureHash {
return database.transaction { return database.transaction {
val bytes = networkParameters.serialize().bytes val bytes = networkParameters.serialize().bytes
val hash = bytes.sha256() val hash = bytes.sha256()
session.save(NetworkParametersEntity( session.saveOrUpdate(NetworkParametersEntity(
parametersBytes = bytes, parametersBytes = bytes,
parametersHash = hash.toString() parametersHash = hash.toString(),
signature = sig?.bytes,
certificate = sig?.by?.encoded
)) ))
hash hash
} }
} }
override fun getLatestNetworkParameters(): NetworkParameters = getLatestNetworkParametersEntity().networkParameters() override fun getLatestUnsignedNetworkParameters(): NetworkParameters = getLatestNetworkParametersEntity().networkParameters()
private fun getLatestNetworkParametersEntity(): NetworkParametersEntity { private fun getLatestNetworkParametersEntity(): NetworkParametersEntity {
return database.transaction { return database.transaction {
val builder = session.criteriaBuilder val builder = session.criteriaBuilder
val query = builder.createQuery(NetworkParametersEntity::class.java).run { val query = builder.createQuery(NetworkParametersEntity::class.java).run {
// TODO a limit of 1 since we only need the first result
from(NetworkParametersEntity::class.java).run { from(NetworkParametersEntity::class.java).run {
orderBy(builder.desc(get<String>(NetworkParametersEntity::version.name))) orderBy(builder.desc(get<String>(NetworkParametersEntity::created.name)))
} }
} }
// We just want the last signed entry // We just want the last entry
session.createQuery(query).resultList.first() session.createQuery(query).setMaxResults(1).resultList.singleOrNull() ?: throw IllegalArgumentException("No network parameters found in network map storage")
} }
} }

View File

@ -1,22 +1,47 @@
package com.r3.corda.networkmanage.common.persistence.entity package com.r3.corda.networkmanage.common.persistence.entity
import com.r3.corda.networkmanage.common.utils.SignedNetworkParameters
import net.corda.core.internal.DigitalSignatureWithCert
import net.corda.core.internal.SignedDataWithCert
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.NetworkParameters
import org.hibernate.annotations.CreationTimestamp
import java.time.Instant
import javax.persistence.* import javax.persistence.*
@Entity @Entity
@Table(name = "network_parameters") @Table(name = "network_parameters", indexes = arrayOf(Index(name = "IDX_NET_PARAMS_HASH", columnList = "hash")))
class NetworkParametersEntity( class NetworkParametersEntity(
@Id @Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
val version: Long? = null,
@Column(name = "hash", length = 64, unique = true) @Column(name = "hash", length = 64, unique = true)
val parametersHash: String, val parametersHash: String,
@CreationTimestamp
val created: Instant = Instant.now(),
@Lob @Lob
@Column(name = "bytes") @Column(name = "parameters_bytes")
val parametersBytes: ByteArray val parametersBytes: ByteArray,
// Both of the fields below are nullable, because of the way we sign network map data. NetworkParameters can be
// inserted into database without signature. Then signing service will sign them.
@Lob
@Column(name = "signature")
val signature: ByteArray?,
@Lob
@Column(name = "certificate")
val certificate: ByteArray?
) { ) {
fun networkParameters(): NetworkParameters = parametersBytes.deserialize() fun networkParameters(): NetworkParameters = parametersBytes.deserialize()
// Return signed network parameters or null if they haven't been signed yet.
fun signedParameters(): SignedNetworkParameters? {
return if (certificate != null && signature != null) {
val sigWithCert = DigitalSignatureWithCert(X509CertificateFactory().generateCertificate(certificate.inputStream()), signature)
SignedDataWithCert(SerializedBytes(parametersBytes), sigWithCert)
} else null
}
} }

View File

@ -5,19 +5,33 @@ import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
import net.corda.core.internal.SignedDataWithCert import net.corda.core.internal.SignedDataWithCert
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.NetworkParameters
class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private val signer: Signer) { class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private val signer: Signer) {
/** /**
* Signs the network map. * Signs the network map and latest network parameters if they haven't been signed yet.
*/ */
fun signNetworkMap() { fun signNetworkMap() {
// TODO There is no network parameters update process in place yet. We assume that latest parameters are to be used
// in current network map.
val latestNetworkParameters = networkMapStorage.getLatestUnsignedNetworkParameters()
val currentNetworkParameters = networkMapStorage.getCurrentSignedNetworkParameters()
if (currentNetworkParameters?.verified() != latestNetworkParameters)
signNetworkParameters(latestNetworkParameters)
val currentSignedNetworkMap = networkMapStorage.getCurrentNetworkMap() val currentSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
val nodeInfoHashes = networkMapStorage.getNodeInfoHashes(CertificateStatus.VALID) val nodeInfoHashes = networkMapStorage.getNodeInfoHashes(CertificateStatus.VALID)
val networkParameters = networkMapStorage.getLatestNetworkParameters() val serialisedNetworkMap = NetworkMap(nodeInfoHashes, latestNetworkParameters.serialize().hash).serialize()
val serialisedNetworkMap = NetworkMap(nodeInfoHashes, networkParameters.serialize().hash).serialize()
if (serialisedNetworkMap != currentSignedNetworkMap?.raw) { if (serialisedNetworkMap != currentSignedNetworkMap?.raw) {
val newSignedNetworkMap = SignedDataWithCert(serialisedNetworkMap, signer.signBytes(serialisedNetworkMap.bytes)) val newSignedNetworkMap = SignedDataWithCert(serialisedNetworkMap, signer.signBytes(serialisedNetworkMap.bytes))
networkMapStorage.saveNetworkMap(newSignedNetworkMap) networkMapStorage.saveNetworkMap(newSignedNetworkMap)
} }
} }
/**
* Signs latest inserted network parameters.
*/
fun signNetworkParameters(networkParameters: NetworkParameters) {
val digitalSignature = signer.signObject(networkParameters).sig
networkMapStorage.saveNetworkParameters(networkParameters, digitalSignature)
}
} }

View File

@ -9,7 +9,7 @@ import net.corda.core.serialization.serialize
*/ */
interface Signer { interface Signer {
/** /**
* Signs given bytes. The signing key selction strategy is left to the implementing class. * Signs given bytes. The signing key selection strategy is left to the implementing class.
* @return [DigitalSignatureWithCert] that encapsulates the signature and the certificate path used in the signing process. * @return [DigitalSignatureWithCert] that encapsulates the signature and the certificate path used in the signing process.
* @throws [AuthenticationException] if fails authentication * @throws [AuthenticationException] if fails authentication
*/ */

View File

@ -52,14 +52,15 @@ class NetworkManagementServer : Closeable {
} }
private fun getNetworkMapService(config: NetworkMapConfig, database: CordaPersistence, signer: LocalSigner?, updateNetworkParameters: NetworkParameters?): NodeInfoWebService { private fun getNetworkMapService(config: NetworkMapConfig, database: CordaPersistence, signer: LocalSigner?, updateNetworkParameters: NetworkParameters?): NodeInfoWebService {
val networkMapStorage = PersistentNetworkMapStorage(database, signer) val networkMapStorage = PersistentNetworkMapStorage(database)
val nodeInfoStorage = PersistentNodeInfoStorage(database) val nodeInfoStorage = PersistentNodeInfoStorage(database)
val localNetworkMapSigner = if (signer != null) NetworkMapSigner(networkMapStorage, signer) else null
updateNetworkParameters?.let { updateNetworkParameters?.let {
// Persisting new network parameters // Persisting new network parameters
val currentNetworkParameters = networkMapStorage.getCurrentNetworkParameters() val currentNetworkParameters = networkMapStorage.getCurrentSignedNetworkParameters()
if (currentNetworkParameters == null) { if (currentNetworkParameters == null) {
networkMapStorage.saveNetworkParameters(it) localNetworkMapSigner?.signNetworkParameters(it) ?: networkMapStorage.saveNetworkParameters(it, null)
} else { } else {
throw UnsupportedOperationException("Network parameters already exist. Updating them via the file config is not supported yet.") throw UnsupportedOperationException("Network parameters already exist. Updating them via the file config is not supported yet.")
} }
@ -67,21 +68,19 @@ class NetworkManagementServer : Closeable {
// This call will fail if parameter is null in DB. // This call will fail if parameter is null in DB.
try { try {
val latestParameter = networkMapStorage.getLatestNetworkParameters() val latestParameter = networkMapStorage.getLatestUnsignedNetworkParameters()
logger.info("Starting network map service with network parameters : $latestParameter") logger.info("Starting network map service with network parameters : $latestParameter")
} catch (e: NoSuchElementException) { } catch (e: NoSuchElementException) {
logger.error("No network parameter found, please upload new network parameter before starting network map service. The server will now exit.") logger.error("No network parameter found, please upload new network parameter before starting network map service. The server will now exit.")
exitProcess(-1) exitProcess(-1)
} }
val networkMapSigner = if (signer != null) NetworkMapSigner(networkMapStorage, signer) else null
// Thread sign network map in case of change (i.e. a new node info has been added or a node info has been removed). // Thread sign network map in case of change (i.e. a new node info has been added or a node info has been removed).
if (networkMapSigner != null) { if (localNetworkMapSigner != null) {
val scheduledExecutor = Executors.newScheduledThreadPool(1) val scheduledExecutor = Executors.newScheduledThreadPool(1)
val signingThread = Runnable { val signingThread = Runnable {
try { try {
networkMapSigner.signNetworkMap() localNetworkMapSigner.signNetworkMap()
} catch (e: Exception) { } catch (e: Exception) {
// Log the error and carry on. // Log the error and carry on.
logger.error("Error encountered when processing node info changes.", e) logger.error("Error encountered when processing node info changes.", e)
@ -141,7 +140,6 @@ class NetworkManagementServer : Closeable {
val services = mutableListOf<Any>() val services = mutableListOf<Any>()
val serverStatus = NetworkManagementServerStatus() val serverStatus = NetworkManagementServerStatus()
// TODO: move signing to signing server.
startNetworkMap?.let { services += getNetworkMapService(it.config, database, it.signer, it.updateNetworkParameters) } startNetworkMap?.let { services += getNetworkMapService(it.config, database, it.signer, it.updateNetworkParameters) }
doormanServiceParameter?.let { services += getDoormanService(it, database, doormanSigner, serverStatus) } doormanServiceParameter?.let { services += getDoormanService(it, database, doormanSigner, serverStatus) }

View File

@ -7,6 +7,7 @@ import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
import com.r3.corda.networkmanage.common.persistence.NodeInfoWithSigned import com.r3.corda.networkmanage.common.persistence.NodeInfoWithSigned
import com.r3.corda.networkmanage.common.utils.SignedNetworkMap import com.r3.corda.networkmanage.common.utils.SignedNetworkMap
import com.r3.corda.networkmanage.common.utils.SignedNetworkParameters
import com.r3.corda.networkmanage.doorman.NetworkMapConfig import com.r3.corda.networkmanage.doorman.NetworkMapConfig
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService.Companion.NETWORK_MAP_PATH import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService.Companion.NETWORK_MAP_PATH
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
@ -41,7 +42,7 @@ class NodeInfoWebService(private val nodeInfoStorage: NodeInfoStorage,
private val networkMapCache: LoadingCache<Boolean, Pair<SignedNetworkMap?, NetworkParameters?>> = CacheBuilder.newBuilder() private val networkMapCache: LoadingCache<Boolean, Pair<SignedNetworkMap?, NetworkParameters?>> = CacheBuilder.newBuilder()
.expireAfterWrite(config.cacheTimeout, TimeUnit.MILLISECONDS) .expireAfterWrite(config.cacheTimeout, TimeUnit.MILLISECONDS)
.build(CacheLoader.from { _ -> Pair(networkMapStorage.getCurrentNetworkMap(), networkMapStorage.getCurrentNetworkParameters()) }) .build(CacheLoader.from { _ -> Pair(networkMapStorage.getCurrentNetworkMap(), networkMapStorage.getCurrentSignedNetworkParameters()?.verified()) })
@POST @POST
@Path("publish") @Path("publish")

View File

@ -46,8 +46,7 @@ fun run(parameters: Parameters) {
checkNotNull(dataSourceProperties) checkNotNull(dataSourceProperties)
val database = configureDatabase(dataSourceProperties, databaseConfig) val database = configureDatabase(dataSourceProperties, databaseConfig)
val csrStorage = DBSignedCertificateRequestStorage(database) val csrStorage = DBSignedCertificateRequestStorage(database)
// TODO Remove the dependency for a local signer to make signing of network parameters work with an HSM val networkMapStorage = PersistentNetworkMapStorage(database)
val networkMapStorage = PersistentNetworkMapStorage(database, localSigner = null)
val hsmNetworkMapSigningThread = HsmNetworkMapSigner( val hsmNetworkMapSigningThread = HsmNetworkMapSigner(
networkMapStorage, networkMapStorage,
networkMapCertificateName, networkMapCertificateName,

View File

@ -93,11 +93,13 @@
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1513267683777-8"> <changeSet author="R3.Corda" id="1513267683777-8">
<createTable tableName="network_parameters"> <createTable tableName="network_parameters">
<column name="version" type="BIGINT"> <column name="hash" type="NVARCHAR(64)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="bytes" type="BLOB"/> <column name="certificate" type="BLOB"/>
<column name="hash" type="NVARCHAR(64)"/> <column name="created" type="TIMESTAMP"/>
<column name="parameters_bytes" type="BLOB"/>
<column name="signature" type="BLOB"/>
</createTable> </createTable>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1513267683777-9"> <changeSet author="R3.Corda" id="1513267683777-9">
@ -120,7 +122,7 @@
</createTable> </createTable>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1513267683777-11"> <changeSet author="R3.Corda" id="1513267683777-11">
<addPrimaryKey columnNames="version" constraintName="CONSTRAINT_3" tableName="network_parameters"/> <addPrimaryKey columnNames="hash" constraintName="CONSTRAINT_3" tableName="network_parameters"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1513267683777-12"> <changeSet author="R3.Corda" id="1513267683777-12">
<addPrimaryKey columnNames="id" constraintName="CONSTRAINT_7" tableName="certificate_data"/> <addPrimaryKey columnNames="id" constraintName="CONSTRAINT_7" tableName="certificate_data"/>
@ -167,6 +169,11 @@
<column name="public_key_hash"/> <column name="public_key_hash"/>
</createIndex> </createIndex>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="network-parameters-idx">
<createIndex indexName="IDX_NET_PARAMS_HASH" tableName="network_parameters">
<column name="hash"/>
</createIndex>
</changeSet>
<changeSet author="R3.Corda" id="1513267683777-24"> <changeSet author="R3.Corda" id="1513267683777-24">
<addForeignKeyConstraint baseColumnNames="rev" baseTableName="certificate_signing_request_AUD" <addForeignKeyConstraint baseColumnNames="rev" baseTableName="certificate_signing_request_AUD"
constraintName="FK5g5cagcrx7siu8lwtavirunxd" constraintName="FK5g5cagcrx7siu8lwtavirunxd"

View File

@ -1,7 +1,6 @@
package com.r3.corda.networkmanage.common.persistence package com.r3.corda.networkmanage.common.persistence
import com.r3.corda.networkmanage.TestBase import com.r3.corda.networkmanage.TestBase
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.signWithCert import net.corda.core.internal.signWithCert
import net.corda.nodeapi.internal.createDevNetworkMapCa import net.corda.nodeapi.internal.createDevNetworkMapCa
@ -37,7 +36,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
rootCaCert = rootCa.certificate rootCaCert = rootCa.certificate
networkMapCa = createDevNetworkMapCa(rootCa) networkMapCa = createDevNetworkMapCa(rootCa)
persistence = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true)) persistence = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true))
networkMapStorage = PersistentNetworkMapStorage(persistence, LocalSigner(networkMapCa.keyPair, arrayOf(networkMapCa.certificate, rootCaCert))) networkMapStorage = PersistentNetworkMapStorage(persistence)
nodeInfoStorage = PersistentNodeInfoStorage(persistence) nodeInfoStorage = PersistentNodeInfoStorage(persistence)
requestStorage = PersistentCertificateRequestStorage(persistence) requestStorage = PersistentCertificateRequestStorage(persistence)
} }
@ -48,15 +47,16 @@ class PersistentNetworkMapStorageTest : TestBase() {
} }
@Test @Test
fun `signNetworkMap creates current network map`() { fun `saveNetworkMap and saveNetworkParameters create current network map and parameters`() {
// given // given
// Create node info. // Create node info.
val signedNodeInfo = createValidSignedNodeInfo("Test") val signedNodeInfo = createValidSignedNodeInfo("Test")
val nodeInfoHash = nodeInfoStorage.putNodeInfo(signedNodeInfo) val nodeInfoHash = nodeInfoStorage.putNodeInfo(signedNodeInfo)
val networkParameters = testNetworkParameters(emptyList())
val parametersSignature = networkParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate).sig
// Create network parameters // Create network parameters
val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList())) val networkParametersHash = networkMapStorage.saveNetworkParameters(networkParameters, parametersSignature)
val networkMap = NetworkMap(listOf(nodeInfoHash), networkParametersHash) val networkMap = NetworkMap(listOf(nodeInfoHash), networkParametersHash)
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate) val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
@ -65,20 +65,26 @@ class PersistentNetworkMapStorageTest : TestBase() {
// then // then
val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap() val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
val persistedSignedParameters = networkMapStorage.getCurrentSignedNetworkParameters()
assertEquals(networkParameters, persistedSignedParameters?.verifiedNetworkMapCert(rootCaCert))
assertEquals(parametersSignature, persistedSignedParameters?.sig)
assertEquals(signedNetworkMap.sig, persistedSignedNetworkMap?.sig) assertEquals(signedNetworkMap.sig, persistedSignedNetworkMap?.sig)
assertEquals(signedNetworkMap.verifiedNetworkMapCert(rootCaCert), persistedSignedNetworkMap?.verifiedNetworkMapCert(rootCaCert)) assertEquals(signedNetworkMap.verifiedNetworkMapCert(rootCaCert), persistedSignedNetworkMap?.verifiedNetworkMapCert(rootCaCert))
assertEquals(signedNetworkMap.verifiedNetworkMapCert(rootCaCert).networkParameterHash, persistedSignedParameters?.raw?.hash)
} }
@Test @Test
fun `getLatestNetworkParameters returns last inserted`() { fun `getLatestNetworkParameters returns last inserted`() {
val params1 = testNetworkParameters(emptyList(), minimumPlatformVersion = 1)
val params2 = testNetworkParameters(emptyList(), minimumPlatformVersion = 2)
// given // given
networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList(), minimumPlatformVersion = 1)) networkMapStorage.saveNetworkParameters(params1, params1.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate).sig)
networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList(), minimumPlatformVersion = 2)) // We may have not signed them yet.
networkMapStorage.saveNetworkParameters(params2, null)
// when // when
val latest = networkMapStorage.getLatestNetworkParameters() val latest = networkMapStorage.getLatestUnsignedNetworkParameters()
// then // then
assertEquals(2, latest.minimumPlatformVersion) assertEquals(2, latest.minimumPlatformVersion)
} }
@ -87,7 +93,8 @@ class PersistentNetworkMapStorageTest : TestBase() {
fun `getCurrentNetworkParameters returns current network map parameters`() { fun `getCurrentNetworkParameters returns current network map parameters`() {
// given // given
// Create network parameters // Create network parameters
val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList())) val testParameters1 = testNetworkParameters(emptyList())
val networkParametersHash = networkMapStorage.saveNetworkParameters(testParameters1, testParameters1.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate).sig)
// Create empty network map // Create empty network map
// Sign network map making it current network map // Sign network map making it current network map
@ -96,25 +103,16 @@ class PersistentNetworkMapStorageTest : TestBase() {
networkMapStorage.saveNetworkMap(signedNetworkMap) networkMapStorage.saveNetworkMap(signedNetworkMap)
// Create new network parameters // Create new network parameters
networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList(), minimumPlatformVersion = 2)) val testParameters2 = testNetworkParameters(emptyList(), minimumPlatformVersion = 2)
networkMapStorage.saveNetworkParameters(testParameters2, testParameters2.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate).sig)
// when // when
val result = networkMapStorage.getCurrentNetworkParameters() val result = networkMapStorage.getCurrentSignedNetworkParameters()?.verifiedNetworkMapCert(rootCaCert)
// then // then
assertEquals(1, result?.minimumPlatformVersion) assertEquals(1, result?.minimumPlatformVersion)
} }
// This test will probably won't be needed when we remove the explicit use of LocalSigner
@Test
fun `getSignedNetworkParameters uses the local signer to return a signed object`() {
val networkParameters = testNetworkParameters(emptyList())
val netParamsHash = networkMapStorage.saveNetworkParameters(networkParameters)
val signedNetworkParameters = networkMapStorage.getSignedNetworkParameters(netParamsHash)
assertThat(signedNetworkParameters?.verifiedNetworkMapCert(rootCaCert)).isEqualTo(networkParameters)
assertThat(signedNetworkParameters?.sig?.by).isEqualTo(networkMapCa.certificate)
}
@Test @Test
fun `getValidNodeInfoHashes returns only valid and signed node info hashes`() { fun `getValidNodeInfoHashes returns only valid and signed node info hashes`() {
// given // given
@ -127,7 +125,8 @@ class PersistentNetworkMapStorageTest : TestBase() {
val nodeInfoHashB = nodeInfoStorage.putNodeInfo(signedNodeInfoB) val nodeInfoHashB = nodeInfoStorage.putNodeInfo(signedNodeInfoB)
// Create network parameters // Create network parameters
val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList())) val testParameters = testNetworkParameters(emptyList())
val networkParametersHash = networkMapStorage.saveNetworkParameters(testParameters, testParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate).sig)
val networkMap = NetworkMap(listOf(nodeInfoHashA), networkParametersHash) val networkMap = NetworkMap(listOf(nodeInfoHashA), networkParametersHash)
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate) val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)

View File

@ -6,9 +6,7 @@ import com.r3.corda.networkmanage.common.utils.hashString
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.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.node.NodeInfo
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509CertificateFactory
@ -33,7 +31,6 @@ class PersistentNodeInfoStorageTest : TestBase() {
private lateinit var requestStorage: CertificationRequestStorage private lateinit var requestStorage: CertificationRequestStorage
private lateinit var nodeInfoStorage: PersistentNodeInfoStorage private lateinit var nodeInfoStorage: PersistentNodeInfoStorage
private lateinit var persistence: CordaPersistence private lateinit var persistence: CordaPersistence
private lateinit var rootCaCert: X509Certificate private lateinit var rootCaCert: X509Certificate
private lateinit var intermediateCa: CertificateAndKeyPair private lateinit var intermediateCa: CertificateAndKeyPair

View File

@ -8,6 +8,7 @@ import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256 import net.corda.core.crypto.sha256
import net.corda.core.internal.DigitalSignatureWithCert import net.corda.core.internal.DigitalSignatureWithCert
import net.corda.core.internal.SignedDataWithCert
import net.corda.core.internal.signWithCert import net.corda.core.internal.signWithCert
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.createDevNetworkMapCa import net.corda.nodeapi.internal.createDevNetworkMapCa
@ -16,10 +17,10 @@ import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.verifiedNetworkMapCert import net.corda.nodeapi.internal.network.verifiedNetworkMapCert
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.createDevIntermediateCaCertPath
import java.security.cert.X509Certificate
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.security.cert.X509Certificate
import kotlin.test.assertEquals import kotlin.test.assertEquals
class NetworkMapSignerTest : TestBase() { class NetworkMapSignerTest : TestBase() {
@ -41,18 +42,24 @@ class NetworkMapSignerTest : TestBase() {
} }
@Test @Test
fun `signNetworkMap builds and signs network map`() { fun `signNetworkMap builds and signs network map and network parameters`() {
// given // given
val signedNodeInfoHashes = listOf(SecureHash.randomSHA256(), SecureHash.randomSHA256()) val signedNodeInfoHashes = listOf(SecureHash.randomSHA256(), SecureHash.randomSHA256())
val networkParameters = testNetworkParameters(emptyList()) val currentParameters = testNetworkParameters(emptyList(), minimumPlatformVersion = 1)
val networkMap = NetworkMap(signedNodeInfoHashes, SecureHash.randomSHA256()) val latestNetworkParameters = testNetworkParameters(emptyList(), minimumPlatformVersion = 2)
val networkMap = NetworkMap(signedNodeInfoHashes, currentParameters.serialize().hash)
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate) val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap) whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes) whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes)
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters) whenever(networkMapStorage.getLatestUnsignedNetworkParameters()).thenReturn(latestNetworkParameters)
whenever(networkMapStorage.getCurrentSignedNetworkParameters()).thenReturn(currentParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
whenever(signer.signBytes(any())).then { whenever(signer.signBytes(any())).then {
DigitalSignatureWithCert(networkMapCa.certificate, Crypto.doSign(networkMapCa.keyPair.private, it.arguments[0] as ByteArray)) DigitalSignatureWithCert(networkMapCa.certificate, Crypto.doSign(networkMapCa.keyPair.private, it.arguments[0] as ByteArray))
} }
whenever(signer.signObject(latestNetworkParameters)).then {
val serialised = latestNetworkParameters.serialize()
SignedDataWithCert(serialised, signer.signBytes(serialised.bytes))
}
// when // when
networkMapSigner.signNetworkMap() networkMapSigner.signNetworkMap()
@ -60,11 +67,12 @@ class NetworkMapSignerTest : TestBase() {
// then // then
// Verify networkMapStorage calls // Verify networkMapStorage calls
verify(networkMapStorage).getNodeInfoHashes(any()) verify(networkMapStorage).getNodeInfoHashes(any())
verify(networkMapStorage).getLatestNetworkParameters() verify(networkMapStorage).getLatestUnsignedNetworkParameters()
verify(networkMapStorage).getCurrentSignedNetworkParameters()
argumentCaptor<SignedNetworkMap>().apply { argumentCaptor<SignedNetworkMap>().apply {
verify(networkMapStorage).saveNetworkMap(capture()) verify(networkMapStorage).saveNetworkMap(capture())
val capturedNetworkMap = firstValue.verifiedNetworkMapCert(rootCaCert) val capturedNetworkMap = firstValue.verifiedNetworkMapCert(rootCaCert)
assertEquals(networkParameters.serialize().hash, capturedNetworkMap.networkParameterHash) assertEquals(latestNetworkParameters.serialize().hash, capturedNetworkMap.networkParameterHash)
assertEquals(signedNodeInfoHashes.size, capturedNetworkMap.nodeInfoHashes.size) assertEquals(signedNodeInfoHashes.size, capturedNetworkMap.nodeInfoHashes.size)
assertThat(capturedNetworkMap.nodeInfoHashes).containsAll(signedNodeInfoHashes) assertThat(capturedNetworkMap.nodeInfoHashes).containsAll(signedNodeInfoHashes)
} }
@ -79,7 +87,8 @@ class NetworkMapSignerTest : TestBase() {
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate) val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap) whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList()) whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters) whenever(networkMapStorage.getLatestUnsignedNetworkParameters()).thenReturn(networkParameters)
whenever(networkMapStorage.getCurrentSignedNetworkParameters()).thenReturn(networkParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
// when // when
networkMapSigner.signNetworkMap() networkMapSigner.signNetworkMap()
@ -95,17 +104,21 @@ class NetworkMapSignerTest : TestBase() {
val networkParameters = testNetworkParameters(emptyList()) val networkParameters = testNetworkParameters(emptyList())
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(null) whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(null)
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList()) whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters) whenever(networkMapStorage.getLatestUnsignedNetworkParameters()).thenReturn(networkParameters)
whenever(signer.signBytes(any())).then { whenever(signer.signBytes(any())).then {
DigitalSignatureWithCert(networkMapCa.certificate, Crypto.doSign(networkMapCa.keyPair.private, it.arguments[0] as ByteArray)) DigitalSignatureWithCert(networkMapCa.certificate, Crypto.doSign(networkMapCa.keyPair.private, it.arguments[0] as ByteArray))
} }
whenever(signer.signObject(networkParameters)).then {
val serialised = networkParameters.serialize()
SignedDataWithCert(serialised, signer.signBytes(serialised.bytes))
}
// when // when
networkMapSigner.signNetworkMap() networkMapSigner.signNetworkMap()
// then // then
// Verify networkMapStorage calls // Verify networkMapStorage calls
verify(networkMapStorage).getNodeInfoHashes(any()) verify(networkMapStorage).getNodeInfoHashes(any())
verify(networkMapStorage).getLatestNetworkParameters() verify(networkMapStorage).getLatestUnsignedNetworkParameters()
argumentCaptor<SignedNetworkMap>().apply { argumentCaptor<SignedNetworkMap>().apply {
verify(networkMapStorage).saveNetworkMap(capture()) verify(networkMapStorage).saveNetworkMap(capture())
val networkMap = firstValue.verifiedNetworkMapCert(rootCaCert) val networkMap = firstValue.verifiedNetworkMapCert(rootCaCert)

View File

@ -56,7 +56,7 @@ class NodeInfoWebServiceTest {
@Test @Test
fun `submit nodeInfo`() { fun `submit nodeInfo`() {
val networkMapStorage: NetworkMapStorage = mock { val networkMapStorage: NetworkMapStorage = mock {
on { getCurrentNetworkParameters() }.thenReturn(testNetworkParameters(emptyList())) on { getCurrentSignedNetworkParameters() }.thenReturn(testNetworkParameters(emptyList()).signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
} }
// Create node info. // Create node info.
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB")) val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"))
@ -72,7 +72,7 @@ class NodeInfoWebServiceTest {
@Test @Test
fun `submit old nodeInfo`() { fun `submit old nodeInfo`() {
val networkMapStorage: NetworkMapStorage = mock { val networkMapStorage: NetworkMapStorage = mock {
on { getCurrentNetworkParameters() }.thenReturn(testNetworkParameters(emptyList(), minimumPlatformVersion = 2)) on { getCurrentSignedNetworkParameters() }.thenReturn(testNetworkParameters(emptyList(), minimumPlatformVersion = 2).signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
} }
// Create node info. // Create node info.
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"), platformVersion = 1) val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"), platformVersion = 1)
@ -88,7 +88,7 @@ class NodeInfoWebServiceTest {
@Test @Test
fun `submit nodeInfo when no network parameters`() { fun `submit nodeInfo when no network parameters`() {
val networkMapStorage: NetworkMapStorage = mock { val networkMapStorage: NetworkMapStorage = mock {
on { getCurrentNetworkParameters() }.thenReturn(null) on { getCurrentSignedNetworkParameters() }.thenReturn(null)
} }
// Create node info. // Create node info.
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"), platformVersion = 1) val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"), platformVersion = 1)
@ -155,7 +155,6 @@ class NodeInfoWebServiceTest {
verify(networkMapStorage, times(1)).getSignedNetworkParameters(networkParametersHash) verify(networkMapStorage, times(1)).getSignedNetworkParameters(networkParametersHash)
assertThat(netParamsResponse.verified()).isEqualTo(networkParameters) assertThat(netParamsResponse.verified()).isEqualTo(networkParameters)
assertThat(netParamsResponse.sig.by).isEqualTo(networkMapCa.certificate) assertThat(netParamsResponse.sig.by).isEqualTo(networkMapCa.certificate)
assertThatExceptionOfType(IOException::class.java) assertThatExceptionOfType(IOException::class.java)
.isThrownBy { it.doGet<SignedNetworkParameters>("network-parameters/${randomSHA256()}") } .isThrownBy { it.doGet<SignedNetworkParameters>("network-parameters/${randomSHA256()}") }
.withMessageContaining("404") .withMessageContaining("404")

View File

@ -41,9 +41,9 @@ import javax.ws.rs.core.MediaType
import kotlin.test.assertEquals import kotlin.test.assertEquals
class RegistrationWebServiceTest : TestBase() { class RegistrationWebServiceTest : TestBase() {
private lateinit var webServer: NetworkManagementWebServer
private lateinit var rootCaCert: X509Certificate private lateinit var rootCaCert: X509Certificate
private lateinit var intermediateCa: CertificateAndKeyPair private lateinit var intermediateCa: CertificateAndKeyPair
private lateinit var webServer: NetworkManagementWebServer
@Before @Before
fun init() { fun init() {