mirror of
https://github.com/corda/corda.git
synced 2025-06-17 14:48:16 +00:00
ENT-4595 harmonize core and serialization (#5792)
* Harmonize serialization/core and deterministic counterparts * Fix test for changed private alias key behaviour * Detekt errors * roll back project.xml
This commit is contained in:
committed by
Rick Parker
parent
87b39bf515
commit
14050826e9
@ -77,7 +77,7 @@ buildscript {
|
|||||||
ext.djvm_version = constants.getProperty("djvmVersion")
|
ext.djvm_version = constants.getProperty("djvmVersion")
|
||||||
ext.deterministic_rt_version = constants.getProperty('deterministicRtVersion')
|
ext.deterministic_rt_version = constants.getProperty('deterministicRtVersion')
|
||||||
ext.okhttp_version = '3.14.2'
|
ext.okhttp_version = '3.14.2'
|
||||||
ext.netty_version = '4.1.22.Final'
|
ext.netty_version = '4.1.29.Final'
|
||||||
ext.typesafe_config_version = constants.getProperty("typesafeConfigVersion")
|
ext.typesafe_config_version = constants.getProperty("typesafeConfigVersion")
|
||||||
ext.fileupload_version = '1.4'
|
ext.fileupload_version = '1.4'
|
||||||
// Legacy JUnit 4 version
|
// Legacy JUnit 4 version
|
||||||
@ -95,10 +95,10 @@ buildscript {
|
|||||||
ext.jansi_version = '1.18'
|
ext.jansi_version = '1.18'
|
||||||
ext.hibernate_version = '5.4.3.Final'
|
ext.hibernate_version = '5.4.3.Final'
|
||||||
ext.h2_version = '1.4.199' // Update docs if renamed or removed.
|
ext.h2_version = '1.4.199' // Update docs if renamed or removed.
|
||||||
ext.postgresql_version = '42.2.5'
|
ext.postgresql_version = '42.2.8'
|
||||||
ext.rxjava_version = '1.3.8'
|
ext.rxjava_version = '1.3.8'
|
||||||
ext.dokka_version = '0.9.17'
|
ext.dokka_version = '0.9.17'
|
||||||
ext.eddsa_version = '0.2.0'
|
ext.eddsa_version = '0.3.0'
|
||||||
ext.dependency_checker_version = '5.2.0'
|
ext.dependency_checker_version = '5.2.0'
|
||||||
ext.commons_collections_version = '4.3'
|
ext.commons_collections_version = '4.3'
|
||||||
ext.beanutils_version = '1.9.3'
|
ext.beanutils_version = '1.9.3'
|
||||||
@ -120,6 +120,7 @@ buildscript {
|
|||||||
ext.class_graph_version = constants.getProperty('classgraphVersion')
|
ext.class_graph_version = constants.getProperty('classgraphVersion')
|
||||||
ext.jcabi_manifests_version = '1.1'
|
ext.jcabi_manifests_version = '1.1'
|
||||||
ext.picocli_version = '3.9.6'
|
ext.picocli_version = '3.9.6'
|
||||||
|
ext.commons_lang_version = '3.9'
|
||||||
ext.commons_io_version = '2.6'
|
ext.commons_io_version = '2.6'
|
||||||
ext.controlsfx_version = '8.40.15'
|
ext.controlsfx_version = '8.40.15'
|
||||||
if (JavaVersion.current() == JavaVersion.VERSION_11) {
|
if (JavaVersion.current() == JavaVersion.VERSION_11) {
|
||||||
|
@ -58,10 +58,13 @@ task patchCore(type: Zip, dependsOn: coreJarTask) {
|
|||||||
from(compileKotlin)
|
from(compileKotlin)
|
||||||
from(processResources)
|
from(processResources)
|
||||||
from(zipTree(originalJar)) {
|
from(zipTree(originalJar)) {
|
||||||
|
exclude 'net/corda/core/crypto/DelegatingSecureRandomService*.class'
|
||||||
|
exclude 'net/corda/core/crypto/SHA256DigestSupplier.class'
|
||||||
exclude 'net/corda/core/internal/*ToggleField*.class'
|
exclude 'net/corda/core/internal/*ToggleField*.class'
|
||||||
exclude 'net/corda/core/serialization/*SerializationFactory*.class'
|
exclude 'net/corda/core/serialization/*SerializationFactory*.class'
|
||||||
exclude 'net/corda/core/serialization/internal/CheckpointSerializationFactory*.class'
|
exclude 'net/corda/core/serialization/internal/CheckpointSerializationFactory*.class'
|
||||||
exclude 'net/corda/core/internal/rules/*.class'
|
exclude 'net/corda/core/internal/rules/*.class'
|
||||||
|
exclude 'net/corda/core/utilities/SgxSupport*.class'
|
||||||
}
|
}
|
||||||
|
|
||||||
reproducibleFileOrder = true
|
reproducibleFileOrder = true
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package net.corda.core.crypto
|
||||||
|
|
||||||
|
import java.security.Provider
|
||||||
|
import java.security.SecureRandomSpi
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
class DelegatingSecureRandomService(provider: CordaSecurityProvider)
|
||||||
|
: Provider.Service(provider, "SecureRandom", "dummy-algorithm", UnsupportedSecureRandomSpi::javaClass.name, null, null) {
|
||||||
|
private val instance: SecureRandomSpi = UnsupportedSecureRandomSpi(algorithm)
|
||||||
|
override fun newInstance(param: Any?) = instance
|
||||||
|
|
||||||
|
private class UnsupportedSecureRandomSpi(private val algorithm: String) : SecureRandomSpi() {
|
||||||
|
override fun engineSetSeed(seed: ByteArray) = unsupported()
|
||||||
|
override fun engineNextBytes(bytes: ByteArray) = unsupported()
|
||||||
|
override fun engineGenerateSeed(numBytes: Int) = unsupported()
|
||||||
|
|
||||||
|
private fun unsupported(): Nothing = throw UnsupportedOperationException("$algorithm not supported")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package net.corda.core.crypto
|
||||||
|
|
||||||
|
import java.security.MessageDigest
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
private class SHA256DigestSupplier : Supplier<MessageDigest> {
|
||||||
|
override fun get(): MessageDigest = MessageDigest.getInstance("SHA-256")
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package net.corda.core.utilities
|
||||||
|
|
||||||
|
import net.corda.core.KeepForDJVM
|
||||||
|
|
||||||
|
@KeepForDJVM
|
||||||
|
object SgxSupport {
|
||||||
|
@JvmStatic
|
||||||
|
val isInsideEnclave: Boolean = true
|
||||||
|
}
|
@ -60,7 +60,7 @@ class FinalityFlowTests : WithFinality {
|
|||||||
fun `allow use of the old API if the CorDapp target version is 3`() {
|
fun `allow use of the old API if the CorDapp target version is 3`() {
|
||||||
val oldBob = createBob(cordapps = listOf(tokenOldCordapp()))
|
val oldBob = createBob(cordapps = listOf(tokenOldCordapp()))
|
||||||
val stx = aliceNode.issuesCashTo(oldBob)
|
val stx = aliceNode.issuesCashTo(oldBob)
|
||||||
val resultFuture = CordappResolver.withCordapp(targetPlatformVersion = 3) {
|
val resultFuture = CordappResolver.withTestCordapp(targetPlatformVersion = 3) {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
aliceNode.startFlowAndRunNetwork(FinalityFlow(stx)).resultFuture
|
aliceNode.startFlowAndRunNetwork(FinalityFlow(stx)).resultFuture
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ dependencies {
|
|||||||
// RxJava: observable streams of events.
|
// RxJava: observable streams of events.
|
||||||
compile "io.reactivex:rxjava:$rxjava_version"
|
compile "io.reactivex:rxjava:$rxjava_version"
|
||||||
|
|
||||||
compile "org.apache.commons:commons-lang3:3.9"
|
compile "org.apache.commons:commons-lang3:$commons_lang_version"
|
||||||
|
|
||||||
// Java ed25519 implementation. See https://github.com/str4d/ed25519-java/
|
// Java ed25519 implementation. See https://github.com/str4d/ed25519-java/
|
||||||
compile "net.i2p.crypto:eddsa:$eddsa_version"
|
compile "net.i2p.crypto:eddsa:$eddsa_version"
|
||||||
@ -74,6 +74,9 @@ dependencies {
|
|||||||
// required to use @Type annotation
|
// required to use @Type annotation
|
||||||
compile "org.hibernate:hibernate-core:$hibernate_version"
|
compile "org.hibernate:hibernate-core:$hibernate_version"
|
||||||
|
|
||||||
|
// FastThreadLocal
|
||||||
|
compile "io.netty:netty-common:$netty_version"
|
||||||
|
|
||||||
compile group: "io.github.classgraph", name: "classgraph", version: class_graph_version
|
compile group: "io.github.classgraph", name: "classgraph", version: class_graph_version
|
||||||
|
|
||||||
testCompile "org.ow2.asm:asm:$asm_version"
|
testCompile "org.ow2.asm:asm:$asm_version"
|
||||||
|
@ -7,6 +7,8 @@ import net.corda.core.crypto.CordaObjectIdentifier.COMPOSITE_SIGNATURE
|
|||||||
import net.corda.core.crypto.internal.PlatformSecureRandomService
|
import net.corda.core.crypto.internal.PlatformSecureRandomService
|
||||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier
|
import org.bouncycastle.asn1.ASN1ObjectIdentifier
|
||||||
import java.security.Provider
|
import java.security.Provider
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
@KeepForDJVM
|
@KeepForDJVM
|
||||||
@Suppress("DEPRECATION") // JDK11: should replace with Provider(String name, double version, String info) (since 9)
|
@Suppress("DEPRECATION") // JDK11: should replace with Provider(String name, double version, String info) (since 9)
|
||||||
@ -29,6 +31,40 @@ class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME secur
|
|||||||
private fun putPlatformSecureRandomService() {
|
private fun putPlatformSecureRandomService() {
|
||||||
putService(PlatformSecureRandomService(this))
|
putService(PlatformSecureRandomService(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getService(type: String, algorithm: String): Service? = serviceFactory(type, algorithm)
|
||||||
|
|
||||||
|
// Used to work around banning of ConcurrentHashMap in DJVM
|
||||||
|
@Suppress("TooGenericExceptionCaught")
|
||||||
|
private val serviceFactory: (String, String) -> Service? = try {
|
||||||
|
// Will throw UnsupportedOperationException in DJVM
|
||||||
|
makeCachingFactory()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
makeFactory()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun superGetService(type: String, algorithm: String): Service? = super.getService(type, algorithm)
|
||||||
|
|
||||||
|
@StubOutForDJVM
|
||||||
|
private fun makeCachingFactory(): Function2<String, String, Service?> {
|
||||||
|
return object : Function2<String, String, Service?> {
|
||||||
|
private val services = ConcurrentHashMap<Pair<String, String>, Optional<Service>>()
|
||||||
|
|
||||||
|
override fun invoke(type: String, algorithm: String): Service? {
|
||||||
|
return services.getOrPut(Pair(type, algorithm)) {
|
||||||
|
Optional.ofNullable(superGetService(type, algorithm))
|
||||||
|
}.orElse(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeFactory(): Function2<String, String, Service?> {
|
||||||
|
return object : Function2<String, String, Service?> {
|
||||||
|
override fun invoke(type: String, algorithm: String): Service? {
|
||||||
|
return superGetService(type, algorithm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@KeepForDJVM
|
@KeepForDJVM
|
||||||
|
@ -1,9 +1,16 @@
|
|||||||
package net.corda.core.crypto
|
package net.corda.core.crypto
|
||||||
|
|
||||||
|
import net.corda.core.CordaOID
|
||||||
import net.corda.core.DeleteForDJVM
|
import net.corda.core.DeleteForDJVM
|
||||||
import net.corda.core.KeepForDJVM
|
import net.corda.core.KeepForDJVM
|
||||||
import net.corda.core.StubOutForDJVM
|
import net.corda.core.StubOutForDJVM
|
||||||
import net.corda.core.crypto.internal.*
|
import net.corda.core.crypto.internal.AliasPrivateKey
|
||||||
|
import net.corda.core.crypto.internal.Instances.withSignature
|
||||||
|
import net.corda.core.crypto.internal.`id-Curve25519ph`
|
||||||
|
import net.corda.core.crypto.internal.bouncyCastlePQCProvider
|
||||||
|
import net.corda.core.crypto.internal.cordaBouncyCastleProvider
|
||||||
|
import net.corda.core.crypto.internal.cordaSecurityProvider
|
||||||
|
import net.corda.core.crypto.internal.providerMap
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.i2p.crypto.eddsa.EdDSAEngine
|
import net.i2p.crypto.eddsa.EdDSAEngine
|
||||||
import net.i2p.crypto.eddsa.EdDSAPrivateKey
|
import net.i2p.crypto.eddsa.EdDSAPrivateKey
|
||||||
@ -14,7 +21,9 @@ import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable
|
|||||||
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
|
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
|
||||||
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
|
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
|
||||||
import org.bouncycastle.asn1.ASN1Integer
|
import org.bouncycastle.asn1.ASN1Integer
|
||||||
|
import org.bouncycastle.asn1.ASN1ObjectIdentifier
|
||||||
import org.bouncycastle.asn1.DERNull
|
import org.bouncycastle.asn1.DERNull
|
||||||
|
import org.bouncycastle.asn1.DERUTF8String
|
||||||
import org.bouncycastle.asn1.DLSequence
|
import org.bouncycastle.asn1.DLSequence
|
||||||
import org.bouncycastle.asn1.bc.BCObjectIdentifiers
|
import org.bouncycastle.asn1.bc.BCObjectIdentifiers
|
||||||
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers
|
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers
|
||||||
@ -42,7 +51,15 @@ import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey
|
|||||||
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey
|
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey
|
||||||
import org.bouncycastle.pqc.jcajce.spec.SPHINCS256KeyGenParameterSpec
|
import org.bouncycastle.pqc.jcajce.spec.SPHINCS256KeyGenParameterSpec
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.security.*
|
import java.security.InvalidKeyException
|
||||||
|
import java.security.Key
|
||||||
|
import java.security.KeyFactory
|
||||||
|
import java.security.KeyPair
|
||||||
|
import java.security.KeyPairGenerator
|
||||||
|
import java.security.PrivateKey
|
||||||
|
import java.security.Provider
|
||||||
|
import java.security.PublicKey
|
||||||
|
import java.security.SignatureException
|
||||||
import java.security.spec.InvalidKeySpecException
|
import java.security.spec.InvalidKeySpecException
|
||||||
import java.security.spec.PKCS8EncodedKeySpec
|
import java.security.spec.PKCS8EncodedKeySpec
|
||||||
import java.security.spec.X509EncodedKeySpec
|
import java.security.spec.X509EncodedKeySpec
|
||||||
@ -289,11 +306,21 @@ object Crypto {
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun decodePrivateKey(encodedKey: ByteArray): PrivateKey {
|
fun decodePrivateKey(encodedKey: ByteArray): PrivateKey {
|
||||||
val keyInfo = PrivateKeyInfo.getInstance(encodedKey)
|
val keyInfo = PrivateKeyInfo.getInstance(encodedKey)
|
||||||
|
if (keyInfo.privateKeyAlgorithm.algorithm == ASN1ObjectIdentifier(CordaOID.ALIAS_PRIVATE_KEY)) {
|
||||||
|
return decodeAliasPrivateKey(keyInfo)
|
||||||
|
}
|
||||||
val signatureScheme = findSignatureScheme(keyInfo.privateKeyAlgorithm)
|
val signatureScheme = findSignatureScheme(keyInfo.privateKeyAlgorithm)
|
||||||
val keyFactory = keyFactory(signatureScheme)
|
val keyFactory = keyFactory(signatureScheme)
|
||||||
return keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey))
|
return keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun decodeAliasPrivateKey(keyInfo: PrivateKeyInfo): PrivateKey {
|
||||||
|
val encodable = keyInfo.parsePrivateKey() as DLSequence
|
||||||
|
val derutF8String = encodable.getObjectAt(0)
|
||||||
|
val alias = (derutF8String as DERUTF8String).string
|
||||||
|
return AliasPrivateKey(alias)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode a PKCS8 encoded key to its [PrivateKey] object based on the input scheme code name.
|
* Decode a PKCS8 encoded key to its [PrivateKey] object based on the input scheme code name.
|
||||||
* This should be used when the type key is known, e.g. during deserialisation or with key caches or key managers.
|
* This should be used when the type key is known, e.g. during deserialisation or with key caches or key managers.
|
||||||
@ -436,7 +463,7 @@ object Crypto {
|
|||||||
"Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}"
|
"Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}"
|
||||||
}
|
}
|
||||||
require(clearData.isNotEmpty()) { "Signing of an empty array is not permitted!" }
|
require(clearData.isNotEmpty()) { "Signing of an empty array is not permitted!" }
|
||||||
val signature = Instances.getSignatureInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName])
|
return withSignature(signatureScheme) { signature ->
|
||||||
// Note that deterministic signature schemes, such as EdDSA, original SPHINCS-256 and RSA PKCS#1, do not require
|
// Note that deterministic signature schemes, such as EdDSA, original SPHINCS-256 and RSA PKCS#1, do not require
|
||||||
// extra randomness, but we have to ensure that non-deterministic algorithms (i.e., ECDSA) use non-blocking
|
// extra randomness, but we have to ensure that non-deterministic algorithms (i.e., ECDSA) use non-blocking
|
||||||
// SecureRandom implementation. Also, SPHINCS-256 implementation in BouncyCastle 1.60 fails with
|
// SecureRandom implementation. Also, SPHINCS-256 implementation in BouncyCastle 1.60 fails with
|
||||||
@ -452,7 +479,8 @@ object Crypto {
|
|||||||
signature.initSign(privateKey, newSecureRandom())
|
signature.initSign(privateKey, newSecureRandom())
|
||||||
}
|
}
|
||||||
signature.update(clearData)
|
signature.update(clearData)
|
||||||
return signature.sign()
|
signature.sign()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -640,10 +668,11 @@ object Crypto {
|
|||||||
require(isSupportedSignatureScheme(signatureScheme)) {
|
require(isSupportedSignatureScheme(signatureScheme)) {
|
||||||
"Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}"
|
"Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}"
|
||||||
}
|
}
|
||||||
val signature = Instances.getSignatureInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName])
|
return withSignature(signatureScheme) { signature ->
|
||||||
signature.initVerify(publicKey)
|
signature.initVerify(publicKey)
|
||||||
signature.update(clearData)
|
signature.update(clearData)
|
||||||
return signature.verify(signatureData)
|
signature.verify(signatureData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
@file:Suppress("MatchingDeclarationName")
|
||||||
@file:KeepForDJVM
|
@file:KeepForDJVM
|
||||||
@file:JvmName("CryptoUtils")
|
@file:JvmName("CryptoUtils")
|
||||||
|
|
||||||
@ -14,7 +15,14 @@ import net.corda.core.utilities.toBase58
|
|||||||
import net.corda.core.utilities.toSHA256Bytes
|
import net.corda.core.utilities.toSHA256Bytes
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.security.*
|
import java.security.InvalidKeyException
|
||||||
|
import java.security.KeyPair
|
||||||
|
import java.security.NoSuchAlgorithmException
|
||||||
|
import java.security.PrivateKey
|
||||||
|
import java.security.PublicKey
|
||||||
|
import java.security.SecureRandom
|
||||||
|
import java.security.SecureRandomSpi
|
||||||
|
import java.security.SignatureException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility to simplify the act of signing a byte array.
|
* Utility to simplify the act of signing a byte array.
|
||||||
@ -116,6 +124,7 @@ val PublicKey.keys: Set<PublicKey> get() = (this as? CompositeKey)?.leafKeys ?:
|
|||||||
|
|
||||||
/** Return true if [otherKey] fulfils the requirements of this [PublicKey]. */
|
/** Return true if [otherKey] fulfils the requirements of this [PublicKey]. */
|
||||||
fun PublicKey.isFulfilledBy(otherKey: PublicKey): Boolean = isFulfilledBy(setOf(otherKey))
|
fun PublicKey.isFulfilledBy(otherKey: PublicKey): Boolean = isFulfilledBy(setOf(otherKey))
|
||||||
|
|
||||||
/** Return true if [otherKeys] fulfil the requirements of this [PublicKey]. */
|
/** Return true if [otherKeys] fulfil the requirements of this [PublicKey]. */
|
||||||
fun PublicKey.isFulfilledBy(otherKeys: Iterable<PublicKey>): Boolean = (this as? CompositeKey)?.isFulfilledBy(otherKeys) ?: (this in otherKeys)
|
fun PublicKey.isFulfilledBy(otherKeys: Iterable<PublicKey>): Boolean = (this as? CompositeKey)?.isFulfilledBy(otherKeys) ?: (this in otherKeys)
|
||||||
|
|
||||||
@ -137,6 +146,7 @@ fun Iterable<TransactionSignature>.byKeys() = map { it.by }.toSet()
|
|||||||
// val (private, public) = keyPair
|
// val (private, public) = keyPair
|
||||||
/* The [PrivateKey] of this [KeyPair]. */
|
/* The [PrivateKey] of this [KeyPair]. */
|
||||||
operator fun KeyPair.component1(): PrivateKey = this.private
|
operator fun KeyPair.component1(): PrivateKey = this.private
|
||||||
|
|
||||||
/* The [PublicKey] of this [KeyPair]. */
|
/* The [PublicKey] of this [KeyPair]. */
|
||||||
operator fun KeyPair.component2(): PublicKey = this.public
|
operator fun KeyPair.component2(): PublicKey = this.public
|
||||||
|
|
||||||
@ -190,6 +200,29 @@ fun KeyPair.verify(signatureData: ByteArray, clearData: ByteArray): Boolean = Cr
|
|||||||
@Throws(NoSuchAlgorithmException::class)
|
@Throws(NoSuchAlgorithmException::class)
|
||||||
fun secureRandomBytes(numOfBytes: Int): ByteArray = ByteArray(numOfBytes).apply { newSecureRandom().nextBytes(this) }
|
fun secureRandomBytes(numOfBytes: Int): ByteArray = ByteArray(numOfBytes).apply { newSecureRandom().nextBytes(this) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a hack added because during deserialisation when no-param constructors are called sometimes default values
|
||||||
|
* generate random numbers, which fail in SGX.
|
||||||
|
* TODO remove this once deserialisation is figured out.
|
||||||
|
*/
|
||||||
|
private class DummySecureRandomSpi : SecureRandomSpi() {
|
||||||
|
override fun engineSetSeed(bytes: ByteArray?) {
|
||||||
|
Exception("DummySecureRandomSpi.engineSetSeed called").printStackTrace(System.out)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun engineNextBytes(bytes: ByteArray?) {
|
||||||
|
Exception("DummySecureRandomSpi.engineNextBytes called").printStackTrace(System.out)
|
||||||
|
bytes?.fill(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun engineGenerateSeed(numberOfBytes: Int): ByteArray {
|
||||||
|
Exception("DummySecureRandomSpi.engineGenerateSeed called").printStackTrace(System.out)
|
||||||
|
return ByteArray(numberOfBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object DummySecureRandom : SecureRandom(DummySecureRandomSpi(), null)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an instance of [SecureRandom] to avoid blocking, due to waiting for additional entropy, when possible.
|
* Get an instance of [SecureRandom] to avoid blocking, due to waiting for additional entropy, when possible.
|
||||||
* In this version, the NativePRNGNonBlocking is exclusively used on Linux OS to utilize dev/urandom because in high traffic
|
* In this version, the NativePRNGNonBlocking is exclusively used on Linux OS to utilize dev/urandom because in high traffic
|
||||||
@ -254,3 +287,4 @@ fun <T : Any> serializedHash(x: T): SecureHash = x.serialize(context = Serializa
|
|||||||
* @return SHA256(SHA256(privacySalt || groupIndex || internalIndex))
|
* @return SHA256(SHA256(privacySalt || groupIndex || internalIndex))
|
||||||
*/
|
*/
|
||||||
fun computeNonce(privacySalt: PrivacySalt, groupIndex: Int, internalIndex: Int) = SecureHash.sha256Twice(privacySalt.bytes + ByteBuffer.allocate(8).putInt(groupIndex).putInt(internalIndex).array())
|
fun computeNonce(privacySalt: PrivacySalt, groupIndex: Int, internalIndex: Int) = SecureHash.sha256Twice(privacySalt.bytes + ByteBuffer.allocate(8).putInt(groupIndex).putInt(internalIndex).array())
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
@file:KeepForDJVM
|
@file:KeepForDJVM
|
||||||
package net.corda.core.crypto
|
package net.corda.core.crypto
|
||||||
|
|
||||||
|
import io.netty.util.concurrent.FastThreadLocal
|
||||||
import net.corda.core.DeleteForDJVM
|
import net.corda.core.DeleteForDJVM
|
||||||
import net.corda.core.KeepForDJVM
|
import net.corda.core.KeepForDJVM
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
@ -9,6 +10,7 @@ import net.corda.core.utilities.parseAsHex
|
|||||||
import net.corda.core.utilities.toHexString
|
import net.corda.core.utilities.toHexString
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container for a cryptographically secure hash value.
|
* Container for a cryptographically secure hash value.
|
||||||
@ -69,12 +71,14 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
|
|||||||
} ?: throw IllegalArgumentException("Provided string is null")
|
} ?: throw IllegalArgumentException("Provided string is null")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val sha256MessageDigest = SHA256DigestSupplier()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the SHA-256 hash value of the [ByteArray].
|
* Computes the SHA-256 hash value of the [ByteArray].
|
||||||
* @param bytes The [ByteArray] to hash.
|
* @param bytes The [ByteArray] to hash.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun sha256(bytes: ByteArray) = SHA256(MessageDigest.getInstance("SHA-256").digest(bytes))
|
fun sha256(bytes: ByteArray) = SHA256(sha256MessageDigest.get().digest(bytes))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the SHA-256 hash of the [ByteArray], and then computes the SHA-256 hash of the hash.
|
* Computes the SHA-256 hash of the [ByteArray], and then computes the SHA-256 hash of the hash.
|
||||||
@ -139,4 +143,17 @@ fun ByteArray.sha256(): SecureHash.SHA256 = SecureHash.sha256(this)
|
|||||||
*/
|
*/
|
||||||
fun OpaqueBytes.sha256(): SecureHash.SHA256 = SecureHash.sha256(this.bytes)
|
fun OpaqueBytes.sha256(): SecureHash.SHA256 = SecureHash.sha256(this.bytes)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the [FastThreadLocal] class behind a [Supplier] interface
|
||||||
|
* so that we can remove it for core-deterministic.
|
||||||
|
*/
|
||||||
|
private class SHA256DigestSupplier : Supplier<MessageDigest> {
|
||||||
|
private val threadLocalSha256MessageDigest = LocalSHA256Digest()
|
||||||
|
override fun get(): MessageDigest = threadLocalSha256MessageDigest.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declaring this as "object : FastThreadLocal<>" would have
|
||||||
|
// created an extra public class in the API definition.
|
||||||
|
private class LocalSHA256Digest : FastThreadLocal<MessageDigest>() {
|
||||||
|
override fun initialValue(): MessageDigest = MessageDigest.getInstance("SHA-256")
|
||||||
|
}
|
||||||
|
@ -1,12 +1,73 @@
|
|||||||
package net.corda.core.crypto.internal
|
package net.corda.core.crypto.internal
|
||||||
|
|
||||||
|
import net.corda.core.DeleteForDJVM
|
||||||
|
import net.corda.core.StubOutForDJVM
|
||||||
|
import net.corda.core.crypto.SignatureScheme
|
||||||
|
import net.corda.core.internal.LazyPool
|
||||||
import java.security.Provider
|
import java.security.Provider
|
||||||
import java.security.Signature
|
import java.security.Signature
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a collection of crypto related getInstance methods that tend to be quite inefficient and we want to be able to
|
* This is a collection of crypto related getInstance methods that tend to be quite inefficient and we want to be able to
|
||||||
* optimise them en masse.
|
* optimise them en masse.
|
||||||
*/
|
*/
|
||||||
object Instances {
|
object Instances {
|
||||||
fun getSignatureInstance(algorithm: String, provider: Provider?) = Signature.getInstance(algorithm, provider)
|
fun <A> withSignature(signatureScheme: SignatureScheme, func: (signature: Signature) -> A): A {
|
||||||
|
val signature = getSignatureInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName])
|
||||||
|
try {
|
||||||
|
return func(signature)
|
||||||
|
} finally {
|
||||||
|
releaseSignatureInstance(signature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSignatureInstance(algorithm: String, provider: Provider?) = signatureFactory.borrow(algorithm, provider)
|
||||||
|
fun releaseSignatureInstance(sig: Signature) = signatureFactory.release(sig)
|
||||||
|
|
||||||
|
// Used to work around banning of ConcurrentHashMap in DJVM
|
||||||
|
private val signatureFactory: SignatureFactory = try {
|
||||||
|
makeCachingFactory()
|
||||||
|
} catch (e: UnsupportedOperationException) {
|
||||||
|
// Thrown by DJVM for method stubbed out below.
|
||||||
|
makeFactory()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The provider itself is a very bad key class as hashCode() is expensive and contended. So use name and version instead.
|
||||||
|
private data class SignatureKey(val algorithm: String, val providerName: String?, val providerVersion: Double?) {
|
||||||
|
constructor(algorithm: String, provider: Provider?) : this(algorithm, provider?.name,
|
||||||
|
@Suppress("DEPRECATION") provider?.version) // JDK11: should replace with getVersionStr() (since 9)
|
||||||
|
}
|
||||||
|
|
||||||
|
@StubOutForDJVM
|
||||||
|
private fun makeCachingFactory(): SignatureFactory {
|
||||||
|
return CachingSignatureFactory()
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteForDJVM
|
||||||
|
private class CachingSignatureFactory : SignatureFactory {
|
||||||
|
private val signatureInstances = ConcurrentHashMap<SignatureKey, LazyPool<Signature>>()
|
||||||
|
|
||||||
|
override fun borrow(algorithm: String, provider: Provider?): Signature {
|
||||||
|
return signatureInstances.getOrPut(SignatureKey(algorithm, provider)) {
|
||||||
|
LazyPool(newInstance = { Signature.getInstance(algorithm, provider) })
|
||||||
|
}.borrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun release(sig: Signature): Unit =
|
||||||
|
signatureInstances[SignatureKey(sig.algorithm, sig.provider)]?.release(sig)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeFactory(): SignatureFactory {
|
||||||
|
return object : SignatureFactory {
|
||||||
|
override fun borrow(algorithm: String, provider: Provider?): Signature {
|
||||||
|
return Signature.getInstance(algorithm, provider)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SignatureFactory {
|
||||||
|
fun borrow(algorithm: String, provider: Provider?): Signature
|
||||||
|
fun release(sig: Signature) {}
|
||||||
}
|
}
|
@ -2,9 +2,17 @@
|
|||||||
@file:DeleteForDJVM
|
@file:DeleteForDJVM
|
||||||
package net.corda.core.crypto.internal
|
package net.corda.core.crypto.internal
|
||||||
|
|
||||||
|
import io.netty.util.concurrent.FastThreadLocal
|
||||||
import net.corda.core.DeleteForDJVM
|
import net.corda.core.DeleteForDJVM
|
||||||
import net.corda.core.crypto.newSecureRandom
|
import net.corda.core.crypto.DummySecureRandom
|
||||||
|
import net.corda.core.utilities.SgxSupport
|
||||||
|
import net.corda.core.utilities.loggerFor
|
||||||
import org.apache.commons.lang3.SystemUtils
|
import org.apache.commons.lang3.SystemUtils
|
||||||
|
import java.io.DataInputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
import java.security.Provider
|
import java.security.Provider
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.security.SecureRandomSpi
|
import java.security.SecureRandomSpi
|
||||||
@ -13,11 +21,13 @@ import java.security.SecureRandomSpi
|
|||||||
* This has been migrated into a separate class so that it
|
* This has been migrated into a separate class so that it
|
||||||
* is easier to delete from the core-deterministic module.
|
* is easier to delete from the core-deterministic module.
|
||||||
*/
|
*/
|
||||||
val platformSecureRandom: () -> SecureRandom = when {
|
internal val platformSecureRandom: () -> SecureRandom = when {
|
||||||
SystemUtils.IS_OS_LINUX -> {
|
SgxSupport.isInsideEnclave -> {
|
||||||
{ SecureRandom.getInstance("NativePRNGNonBlocking") }
|
{ DummySecureRandom }
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
{ sharedSecureRandom }
|
||||||
}
|
}
|
||||||
else -> SecureRandom::getInstanceStrong
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteForDJVM
|
@DeleteForDJVM
|
||||||
@ -26,17 +36,65 @@ class PlatformSecureRandomService(provider: Provider)
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val algorithm = "CordaPRNG"
|
const val algorithm = "CordaPRNG"
|
||||||
|
private val logger = loggerFor<PlatformSecureRandomService>()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val instance: SecureRandomSpi = if (SystemUtils.IS_OS_LINUX) tryAndUseLinuxSecureRandomSpi() else PlatformSecureRandomSpi()
|
||||||
|
|
||||||
|
@Suppress("TooGenericExceptionCaught", "TooGenericExceptionThrown")
|
||||||
|
private fun tryAndUseLinuxSecureRandomSpi(): SecureRandomSpi = try {
|
||||||
|
LinuxSecureRandomSpi()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.error("Unable to initialise LinuxSecureRandomSpi. The exception logged with this message might assist with diagnosis." +
|
||||||
|
" The process will now exit.", e)
|
||||||
|
System.exit(1)
|
||||||
|
throw RuntimeException("Never reached, but calms the compiler.")
|
||||||
}
|
}
|
||||||
|
|
||||||
private val instance: SecureRandomSpi = PlatformSecureRandomSpi()
|
|
||||||
override fun newInstance(constructorParameter: Any?) = instance
|
override fun newInstance(constructorParameter: Any?) = instance
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteForDJVM
|
@DeleteForDJVM
|
||||||
private class PlatformSecureRandomSpi : SecureRandomSpi() {
|
private class PlatformSecureRandomSpi : SecureRandomSpi() {
|
||||||
private val secureRandom: SecureRandom = newSecureRandom()
|
private val threadLocalSecureRandom = object : FastThreadLocal<SecureRandom>() {
|
||||||
|
override fun initialValue() = SecureRandom.getInstanceStrong()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val secureRandom: SecureRandom get() = threadLocalSecureRandom.get()
|
||||||
|
|
||||||
override fun engineSetSeed(seed: ByteArray) = secureRandom.setSeed(seed)
|
override fun engineSetSeed(seed: ByteArray) = secureRandom.setSeed(seed)
|
||||||
override fun engineNextBytes(bytes: ByteArray) = secureRandom.nextBytes(bytes)
|
override fun engineNextBytes(bytes: ByteArray) = secureRandom.nextBytes(bytes)
|
||||||
override fun engineGenerateSeed(numBytes: Int): ByteArray = secureRandom.generateSeed(numBytes)
|
override fun engineGenerateSeed(numBytes: Int): ByteArray = secureRandom.generateSeed(numBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteForDJVM
|
||||||
|
@Suppress("TooGenericExceptionCaught", "TooGenericExceptionThrown")
|
||||||
|
private class LinuxSecureRandomSpi : SecureRandomSpi() {
|
||||||
|
private fun openURandom(): InputStream {
|
||||||
|
try {
|
||||||
|
val file = File("/dev/urandom")
|
||||||
|
val stream = FileInputStream(file)
|
||||||
|
if (stream.read() == -1)
|
||||||
|
throw RuntimeException("/dev/urandom not readable?")
|
||||||
|
return stream
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw RuntimeException(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var urandom = DataInputStream(openURandom())
|
||||||
|
|
||||||
|
override fun engineSetSeed(seed: ByteArray) {}
|
||||||
|
override fun engineNextBytes(bytes: ByteArray) = try {
|
||||||
|
urandom.readFully(bytes)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
throw RuntimeException(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun engineGenerateSeed(numBytes: Int): ByteArray = ByteArray(numBytes).apply { engineNextBytes(this) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is safe to share because of the underlying implementation of SecureRandomSpi
|
||||||
|
private val sharedSecureRandom: SecureRandom by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||||
|
SecureRandom.getInstance(PlatformSecureRandomService.algorithm)
|
||||||
|
}
|
||||||
|
@ -26,13 +26,19 @@ abstract class BackpressureAwareTimedFlow<ResultType> : FlowLogic<ResultType>(),
|
|||||||
val unwrapped = wrappedResult.fromUntrustedWorld
|
val unwrapped = wrappedResult.fromUntrustedWorld
|
||||||
when (unwrapped) {
|
when (unwrapped) {
|
||||||
is WaitTimeUpdate -> {
|
is WaitTimeUpdate -> {
|
||||||
logger.info("Counterparty [${session.counterparty}] is busy - TimedFlow $runId has been asked to wait for an additional ${unwrapped.waitTime} seconds for completion.")
|
applyWaitTimeUpdate(session, unwrapped)
|
||||||
stateMachine.updateTimedFlowTimeout(unwrapped.waitTime.seconds)
|
|
||||||
}
|
}
|
||||||
is ReceiveType -> @Suppress("UNCHECKED_CAST") // The compiler doesn't understand it's checked in the line above
|
is ReceiveType -> @Suppress("UNCHECKED_CAST") // The compiler doesn't understand it's checked in the line above
|
||||||
return wrappedResult as UntrustworthyData<ReceiveType>
|
return wrappedResult as UntrustworthyData<ReceiveType>
|
||||||
else -> throw throw IllegalArgumentException("We were expecting a ${ReceiveType::class.java.name} or WaitTimeUpdate but we instead got a ${unwrapped.javaClass.name} ($unwrapped)")
|
else -> throw throw IllegalArgumentException("We were expecting a ${ReceiveType::class.java.name} or WaitTimeUpdate but " +
|
||||||
|
"we instead got a ${unwrapped.javaClass.name} ($unwrapped)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun applyWaitTimeUpdate(session: FlowSession, update: WaitTimeUpdate) {
|
||||||
|
logger.info("Counterparty [${session.counterparty}] is busy - TimedFlow $runId has been asked to wait for an additional " +
|
||||||
|
"${update.waitTime} for completion.")
|
||||||
|
stateMachine.updateTimedFlowTimeout(update.waitTime.seconds)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
package net.corda.core.internal
|
package net.corda.core.internal
|
||||||
|
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
|
||||||
import io.github.classgraph.ClassGraph
|
import io.github.classgraph.ClassGraph
|
||||||
import io.github.classgraph.ScanResult
|
import io.github.classgraph.ScanResult
|
||||||
import net.corda.core.DeleteForDJVM
|
import net.corda.core.DeleteForDJVM
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
import kotlin.concurrent.withLock
|
import kotlin.concurrent.withLock
|
||||||
|
|
||||||
private val pooledScanMutex = ReentrantLock()
|
private val pooledScanMutex = ReentrantLock()
|
||||||
|
@ -43,4 +43,5 @@ interface FlowStateMachine<FLOWRETURN> {
|
|||||||
val context: InvocationContext
|
val context: InvocationContext
|
||||||
val ourIdentity: Party
|
val ourIdentity: Party
|
||||||
val ourSenderUUID: String?
|
val ourSenderUUID: String?
|
||||||
|
val creationTime: Long
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import java.lang.reflect.Modifier
|
|||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.HttpURLConnection.HTTP_OK
|
import java.net.HttpURLConnection.HTTP_OK
|
||||||
|
import java.net.Proxy
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
@ -426,15 +427,17 @@ val DEFAULT_HTTP_CONNECT_TIMEOUT = 30.seconds.toMillis()
|
|||||||
val DEFAULT_HTTP_READ_TIMEOUT = 30.seconds.toMillis()
|
val DEFAULT_HTTP_READ_TIMEOUT = 30.seconds.toMillis()
|
||||||
|
|
||||||
@DeleteForDJVM
|
@DeleteForDJVM
|
||||||
fun URL.openHttpConnection(): HttpURLConnection = openConnection().also {
|
fun URL.openHttpConnection(proxy: Proxy? = null): HttpURLConnection = (
|
||||||
|
if (proxy == null) openConnection()
|
||||||
|
else openConnection(proxy)).also {
|
||||||
// The default values are 0 which means infinite timeout.
|
// The default values are 0 which means infinite timeout.
|
||||||
it.connectTimeout = DEFAULT_HTTP_CONNECT_TIMEOUT.toInt()
|
it.connectTimeout = DEFAULT_HTTP_CONNECT_TIMEOUT.toInt()
|
||||||
it.readTimeout = DEFAULT_HTTP_READ_TIMEOUT.toInt()
|
it.readTimeout = DEFAULT_HTTP_READ_TIMEOUT.toInt()
|
||||||
} as HttpURLConnection
|
} as HttpURLConnection
|
||||||
|
|
||||||
@DeleteForDJVM
|
@DeleteForDJVM
|
||||||
fun URL.post(serializedData: OpaqueBytes, vararg properties: Pair<String, String>): ByteArray {
|
fun URL.post(serializedData: OpaqueBytes, vararg properties: Pair<String, String>, proxy: Proxy? = null): ByteArray {
|
||||||
return openHttpConnection().run {
|
return openHttpConnection(proxy).run {
|
||||||
doOutput = true
|
doOutput = true
|
||||||
requestMethod = "POST"
|
requestMethod = "POST"
|
||||||
properties.forEach { (key, value) -> setRequestProperty(key, value) }
|
properties.forEach { (key, value) -> setRequestProperty(key, value) }
|
||||||
|
@ -15,19 +15,52 @@ class LifeCycle<S : Enum<S>>(initial: S) {
|
|||||||
private val lock = ReentrantReadWriteLock()
|
private val lock = ReentrantReadWriteLock()
|
||||||
private var state = initial
|
private var state = initial
|
||||||
|
|
||||||
/** Assert that the lifecycle in the [requiredState]. */
|
/**
|
||||||
fun requireState(requiredState: S) {
|
* Assert that the lifecycle in the [requiredState]. Optionally runs [block], for the duration of which the
|
||||||
requireState({ "Required state to be $requiredState, was $it" }) { it == requiredState }
|
* lifecycle is guaranteed to stay in [requiredState].
|
||||||
|
*/
|
||||||
|
fun <A> requireState(
|
||||||
|
requiredState: S,
|
||||||
|
block: () -> A
|
||||||
|
): A {
|
||||||
|
return requireState(
|
||||||
|
errorMessage = { "Required state to be $requiredState, was $it" },
|
||||||
|
predicate = { it == requiredState },
|
||||||
|
block = block
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requireState(requiredState: S) = requireState(requiredState) {}
|
||||||
|
|
||||||
|
fun <A> requireState(
|
||||||
|
requiredState: S,
|
||||||
|
throwable: Throwable,
|
||||||
|
block: () -> A
|
||||||
|
): A {
|
||||||
|
return lock.readLock().withLock {
|
||||||
|
if (requiredState != state) {
|
||||||
|
throw throwable
|
||||||
|
}
|
||||||
|
block()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Assert something about the current state atomically. */
|
/** Assert something about the current state atomically. */
|
||||||
|
fun <A> requireState(
|
||||||
|
errorMessage: (S) -> String,
|
||||||
|
predicate: (S) -> Boolean,
|
||||||
|
block: () -> A
|
||||||
|
): A {
|
||||||
|
return lock.readLock().withLock {
|
||||||
|
require(predicate(state)) { errorMessage(state) }
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
fun requireState(
|
fun requireState(
|
||||||
errorMessage: (S) -> String = { "Predicate failed on state $it" },
|
errorMessage: (S) -> String = { "Predicate failed on state $it" },
|
||||||
predicate: (S) -> Boolean
|
predicate: (S) -> Boolean
|
||||||
) {
|
) {
|
||||||
lock.readLock().withLock {
|
requireState(errorMessage, predicate) {}
|
||||||
require(predicate(state)) { errorMessage(state) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Transition the state from [from] to [to]. */
|
/** Transition the state from [from] to [to]. */
|
||||||
|
@ -327,6 +327,7 @@ abstract class Verifier(val ltx: LedgerTransaction, protected val transactionCla
|
|||||||
*
|
*
|
||||||
* @throws TransactionVerificationException if the constraints fail to verify
|
* @throws TransactionVerificationException if the constraints fail to verify
|
||||||
*/
|
*/
|
||||||
|
@Suppress("NestedBlockDepth", "MagicNumber")
|
||||||
private fun verifyConstraints(contractAttachmentsByContract: Map<ContractClassName, ContractAttachment>) {
|
private fun verifyConstraints(contractAttachmentsByContract: Map<ContractClassName, ContractAttachment>) {
|
||||||
// For each contract/constraint pair check that the relevant attachment is valid.
|
// For each contract/constraint pair check that the relevant attachment is valid.
|
||||||
allStates.map { it.contract to it.constraint }.toSet().forEach { (contract, constraint) ->
|
allStates.map { it.contract to it.constraint }.toSet().forEach { (contract, constraint) ->
|
||||||
|
@ -35,7 +35,8 @@ data class CordappImpl(
|
|||||||
val notaryService: Class<out NotaryService>? = null,
|
val notaryService: Class<out NotaryService>? = null,
|
||||||
/** Indicates whether the CorDapp is loaded from external sources, or generated on node startup (virtual). */
|
/** Indicates whether the CorDapp is loaded from external sources, or generated on node startup (virtual). */
|
||||||
val isLoaded: Boolean = true,
|
val isLoaded: Boolean = true,
|
||||||
private val explicitCordappClasses: List<String> = emptyList()
|
private val explicitCordappClasses: List<String> = emptyList(),
|
||||||
|
val isVirtual: Boolean = false
|
||||||
) : Cordapp {
|
) : Cordapp {
|
||||||
override val name: String = jarName(jarPath)
|
override val name: String = jarName(jarPath)
|
||||||
|
|
||||||
|
@ -88,20 +88,41 @@ object CordappResolver {
|
|||||||
*/
|
*/
|
||||||
val currentTargetVersion: Int get() = currentCordapp?.targetPlatformVersion ?: 1
|
val currentTargetVersion: Int get() = currentCordapp?.targetPlatformVersion ?: 1
|
||||||
|
|
||||||
|
// A list of extra CorDapps added to the current CorDapps list for testing purposes.
|
||||||
|
private var extraCordappsForTesting = listOf<Cordapp>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all the CorDapps that were involved in the call stack at the point the provided exception was generated.
|
||||||
|
*
|
||||||
|
* This is provided to allow splitting the cost of generating the exception and retrieving the CorDapps involved.
|
||||||
|
*/
|
||||||
|
fun cordappsFromException(exception: Exception): List<Cordapp> {
|
||||||
|
val apps = exception.stackTrace
|
||||||
|
.mapNotNull { cordappClasses[it.className] }
|
||||||
|
.flatten()
|
||||||
|
.distinct()
|
||||||
|
return (apps + extraCordappsForTesting)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Temporarily apply a fake CorDapp with the given parameters. For use in testing.
|
* Temporarily apply a fake CorDapp with the given parameters. For use in testing.
|
||||||
*/
|
*/
|
||||||
@Synchronized
|
@Synchronized
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
fun <T> withCordapp(minimumPlatformVersion: Int = 1, targetPlatformVersion: Int = PLATFORM_VERSION, block: () -> T): T {
|
fun <T> withTestCordapp(minimumPlatformVersion: Int = 1,
|
||||||
|
targetPlatformVersion: Int = PLATFORM_VERSION,
|
||||||
|
extraApps: List<CordappImpl> = listOf(),
|
||||||
|
block: () -> T): T {
|
||||||
val currentResolver = cordappResolver
|
val currentResolver = cordappResolver
|
||||||
cordappResolver = {
|
cordappResolver = {
|
||||||
CordappImpl.TEST_INSTANCE.copy(minimumPlatformVersion = minimumPlatformVersion, targetPlatformVersion = targetPlatformVersion)
|
CordappImpl.TEST_INSTANCE.copy(minimumPlatformVersion = minimumPlatformVersion, targetPlatformVersion = targetPlatformVersion)
|
||||||
}
|
}
|
||||||
|
extraCordappsForTesting = listOf(cordappResolver()!!) + extraApps
|
||||||
try {
|
try {
|
||||||
return block()
|
return block()
|
||||||
} finally {
|
} finally {
|
||||||
cordappResolver = currentResolver
|
cordappResolver = currentResolver
|
||||||
|
extraCordappsForTesting = listOf()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package net.corda.core.internal.utilities
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.system.measureNanoTime
|
||||||
|
|
||||||
|
fun measureMilliAndNanoTime(block: () -> Unit): Double {
|
||||||
|
return measureNanoTime(block).toDouble() / TimeUnit.MILLISECONDS.toNanos(1).toDouble()
|
||||||
|
}
|
@ -5,7 +5,10 @@ import net.corda.core.DoNotImplement
|
|||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.cordapp.CordappContext
|
import net.corda.core.cordapp.CordappContext
|
||||||
import net.corda.core.cordapp.CordappProvider
|
import net.corda.core.cordapp.CordappProvider
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.Crypto
|
||||||
|
import net.corda.core.crypto.SignableData
|
||||||
|
import net.corda.core.crypto.SignatureMetadata
|
||||||
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.flows.ContractUpgradeFlow
|
import net.corda.core.flows.ContractUpgradeFlow
|
||||||
import net.corda.core.node.services.*
|
import net.corda.core.node.services.*
|
||||||
import net.corda.core.serialization.SerializeAsToken
|
import net.corda.core.serialization.SerializeAsToken
|
||||||
|
@ -7,7 +7,6 @@ import net.corda.core.crypto.DigitalSignature
|
|||||||
import net.corda.core.crypto.SignableData
|
import net.corda.core.crypto.SignableData
|
||||||
import net.corda.core.crypto.TransactionSignature
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.node.ServiceHub
|
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
@ -9,7 +9,6 @@ import net.corda.core.node.services.vault.CollectionOperator.*
|
|||||||
import net.corda.core.node.services.vault.ColumnPredicate.*
|
import net.corda.core.node.services.vault.ColumnPredicate.*
|
||||||
import net.corda.core.node.services.vault.EqualityComparisonOperator.*
|
import net.corda.core.node.services.vault.EqualityComparisonOperator.*
|
||||||
import net.corda.core.node.services.vault.LikenessOperator.*
|
import net.corda.core.node.services.vault.LikenessOperator.*
|
||||||
import net.corda.core.schemas.PersistentState
|
|
||||||
import net.corda.core.schemas.StatePersistable
|
import net.corda.core.schemas.StatePersistable
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
|
@ -22,7 +22,8 @@ import net.corda.core.utilities.contextLogger
|
|||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.ArrayDeque
|
||||||
|
import java.util.UUID
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.collections.component1
|
import kotlin.collections.component1
|
||||||
|
@ -38,6 +38,9 @@ infix fun Long.exactAdd(b: Long): Long = Math.addExact(this, b)
|
|||||||
*/
|
*/
|
||||||
inline fun <reified T : Any> loggerFor(): Logger = LoggerFactory.getLogger(T::class.java)
|
inline fun <reified T : Any> loggerFor(): Logger = LoggerFactory.getLogger(T::class.java)
|
||||||
|
|
||||||
|
/** Returns the logger used for detailed logging. */
|
||||||
|
fun detailedLogger(): Logger = LoggerFactory.getLogger("DetailedInfo")
|
||||||
|
|
||||||
/** When called from a companion object, returns the logger for the enclosing class. */
|
/** When called from a companion object, returns the logger for the enclosing class. */
|
||||||
fun Any.contextLogger(): Logger = LoggerFactory.getLogger(javaClass.enclosingClass)
|
fun Any.contextLogger(): Logger = LoggerFactory.getLogger(javaClass.enclosingClass)
|
||||||
|
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package net.corda.core.utilities
|
||||||
|
|
||||||
|
object SgxSupport {
|
||||||
|
@JvmStatic
|
||||||
|
val isInsideEnclave: Boolean by lazy {
|
||||||
|
(System.getProperty("os.name") == "Linux") && (System.getProperty("java.vm.name") == "Avian (Corda)")
|
||||||
|
}
|
||||||
|
}
|
11
core/src/test/kotlin/net/corda/core/crypto/SecureHashTest.kt
Normal file
11
core/src/test/kotlin/net/corda/core/crypto/SecureHashTest.kt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package net.corda.core.crypto
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class SecureHashTest {
|
||||||
|
@Test
|
||||||
|
fun `sha256 does not retain state between same-thread invocations`() {
|
||||||
|
assertEquals(SecureHash.sha256("abc"), SecureHash.sha256("abc"))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package net.corda.core.crypto
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
import java.security.SecureRandom
|
||||||
|
|
||||||
|
class SecureRandomTest {
|
||||||
|
@Test(timeout = 1000)
|
||||||
|
fun `regular SecureRandom does not spend a lot of time seeding itself`() {
|
||||||
|
val bytes = ByteArray(1000)
|
||||||
|
repeat(10) {
|
||||||
|
val sr = SecureRandom()
|
||||||
|
repeat(100) {
|
||||||
|
sr.nextBytes(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -28,7 +28,7 @@ class CordappResolverTest {
|
|||||||
assertEquals(defaultTargetVersion, CordappResolver.currentTargetVersion)
|
assertEquals(defaultTargetVersion, CordappResolver.currentTargetVersion)
|
||||||
|
|
||||||
val expectedTargetVersion = 555
|
val expectedTargetVersion = 555
|
||||||
CordappResolver.withCordapp(targetPlatformVersion = expectedTargetVersion) {
|
CordappResolver.withTestCordapp(targetPlatformVersion = expectedTargetVersion) {
|
||||||
val actualTargetVersion = CordappResolver.currentTargetVersion
|
val actualTargetVersion = CordappResolver.currentTargetVersion
|
||||||
assertEquals(expectedTargetVersion, actualTargetVersion)
|
assertEquals(expectedTargetVersion, actualTargetVersion)
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@ import net.corda.core.contracts.StateRef
|
|||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.internal.declaredField
|
import net.corda.core.internal.declaredField
|
||||||
import org.assertj.core.api.Assertions.catchThrowable
|
import org.assertj.core.api.Assertions.catchThrowable
|
||||||
import org.junit.Assert
|
|
||||||
import org.junit.Assert.assertSame
|
import org.junit.Assert.assertSame
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ReadOnlyBufferException
|
import java.nio.ReadOnlyBufferException
|
||||||
@ -52,10 +52,10 @@ class ByteArraysTest {
|
|||||||
|
|
||||||
val privacySalt = net.corda.core.contracts.PrivacySalt()
|
val privacySalt = net.corda.core.contracts.PrivacySalt()
|
||||||
val privacySaltAsHexString = privacySalt.bytes.toHexString()
|
val privacySaltAsHexString = privacySalt.bytes.toHexString()
|
||||||
Assert.assertTrue(privacySaltAsHexString.matches(HEX_REGEX))
|
assertTrue(privacySaltAsHexString.matches(HEX_REGEX))
|
||||||
|
|
||||||
val stateRef = StateRef(SecureHash.randomSHA256(), 0)
|
val stateRef = StateRef(SecureHash.randomSHA256(), 0)
|
||||||
val txhashAsHexString = stateRef.txhash.bytes.toHexString()
|
val txhashAsHexString = stateRef.txhash.bytes.toHexString()
|
||||||
Assert.assertTrue(txhashAsHexString.matches(HEX_REGEX))
|
assertTrue(txhashAsHexString.matches(HEX_REGEX))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package net.corda.nodeapi.internal.crypto
|
|||||||
|
|
||||||
import net.corda.core.crypto.internal.AliasPrivateKey
|
import net.corda.core.crypto.internal.AliasPrivateKey
|
||||||
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
||||||
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.TemporaryFolder
|
import org.junit.rules.TemporaryFolder
|
||||||
@ -20,15 +19,18 @@ class AliasPrivateKeyTest {
|
|||||||
val alias = "01234567890"
|
val alias = "01234567890"
|
||||||
val aliasPrivateKey = AliasPrivateKey(alias)
|
val aliasPrivateKey = AliasPrivateKey(alias)
|
||||||
val certificatesDirectory = tempFolder.root.toPath()
|
val certificatesDirectory = tempFolder.root.toPath()
|
||||||
val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, "keystorepass").get(createNew = true)
|
val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(
|
||||||
signingCertStore.query { setPrivateKey(alias, aliasPrivateKey, listOf(NOT_YET_REGISTERED_MARKER_KEYS_AND_CERTS.ECDSAR1_CERT), "entrypassword") }
|
certificatesDirectory,
|
||||||
|
"keystorepass").get(createNew = true)
|
||||||
|
signingCertStore.query {
|
||||||
|
setPrivateKey(alias, aliasPrivateKey, listOf(NOT_YET_REGISTERED_MARKER_KEYS_AND_CERTS.ECDSAR1_CERT), "entrypassword")
|
||||||
|
}
|
||||||
// We can retrieve the certificate.
|
// We can retrieve the certificate.
|
||||||
assertTrue { signingCertStore.contains(alias) }
|
assertTrue { signingCertStore.contains(alias) }
|
||||||
// We can retrieve the certificate.
|
// We can retrieve the certificate.
|
||||||
assertEquals(NOT_YET_REGISTERED_MARKER_KEYS_AND_CERTS.ECDSAR1_CERT, signingCertStore[alias])
|
assertEquals(NOT_YET_REGISTERED_MARKER_KEYS_AND_CERTS.ECDSAR1_CERT, signingCertStore[alias])
|
||||||
// Although we can store an AliasPrivateKey, we cannot retrieve it. But, it's fine as we use certStore for storing/handling certs only.
|
// Although we can store an AliasPrivateKey, we cannot retrieve it. But, it's fine as we use certStore for storing/handling certs
|
||||||
assertThatIllegalArgumentException().isThrownBy {
|
// only.
|
||||||
signingCertStore.query { getPrivateKey(alias, "entrypassword") }
|
assertEquals(aliasPrivateKey, signingCertStore.query { getPrivateKey(alias, "entrypassword") })
|
||||||
}.withMessage("Unrecognised algorithm: 1.3.6.1.4.1.50530.1.2")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,8 @@ class TransientReference<out A>(@Transient val value: A)
|
|||||||
|
|
||||||
class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
|
class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
|
||||||
override val logic: FlowLogic<R>,
|
override val logic: FlowLogic<R>,
|
||||||
scheduler: FiberScheduler
|
scheduler: FiberScheduler,
|
||||||
|
override val creationTime: Long = System.currentTimeMillis()
|
||||||
) : Fiber<Unit>(id.toString(), scheduler), FlowStateMachine<R>, FlowFiber {
|
) : Fiber<Unit>(id.toString(), scheduler), FlowStateMachine<R>, FlowFiber {
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
|
@ -82,7 +82,7 @@ class FinalityHandlerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun TestStartedNode.finaliseWithOldApi(stx: SignedTransaction): CordaFuture<SignedTransaction> {
|
private fun TestStartedNode.finaliseWithOldApi(stx: SignedTransaction): CordaFuture<SignedTransaction> {
|
||||||
return CordappResolver.withCordapp(targetPlatformVersion = 3) {
|
return CordappResolver.withTestCordapp(targetPlatformVersion = 3) {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
services.startFlow(FinalityFlow(stx)).resultFuture.apply {
|
services.startFlow(FinalityFlow(stx)).resultFuture.apply {
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
|
@ -897,7 +897,7 @@ class NodeVaultServiceTest {
|
|||||||
|
|
||||||
fun List<StateAndRef<DummyState>>.getNumbers() = map { it.state.data.magicNumber }.toSet()
|
fun List<StateAndRef<DummyState>>.getNumbers() = map { it.state.data.magicNumber }.toSet()
|
||||||
|
|
||||||
CordappResolver.withCordapp(targetPlatformVersion = 3) {
|
CordappResolver.withTestCordapp(targetPlatformVersion = 3) {
|
||||||
services.recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(createTx(1, megaCorp.party)))
|
services.recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(createTx(1, megaCorp.party)))
|
||||||
services.recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(createTx(2, miniCorp.party)))
|
services.recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(createTx(2, miniCorp.party)))
|
||||||
services.recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(createTx(3, miniCorp.party, megaCorp.party)))
|
services.recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(createTx(3, miniCorp.party, megaCorp.party)))
|
||||||
|
@ -4,7 +4,6 @@ import com.google.common.reflect.TypeToken
|
|||||||
import net.corda.core.KeepForDJVM
|
import net.corda.core.KeepForDJVM
|
||||||
import net.corda.core.internal.isPublic
|
import net.corda.core.internal.isPublic
|
||||||
import net.corda.core.serialization.SerializableCalculatedProperty
|
import net.corda.core.serialization.SerializableCalculatedProperty
|
||||||
import net.corda.core.utilities.contextLogger
|
|
||||||
import net.corda.serialization.internal.amqp.MethodClassifier.*
|
import net.corda.serialization.internal.amqp.MethodClassifier.*
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
|
Reference in New Issue
Block a user