mirror of
https://github.com/corda/corda.git
synced 2025-05-02 08:43:15 +00:00
[CORDA-2225] Use platform's non-blocking PRNG when invoking SecureRandom() (#4234)
This commit is contained in:
parent
e1e5d13941
commit
b8327ddf98
@ -4,6 +4,7 @@ import net.corda.core.KeepForDJVM
|
|||||||
import net.corda.core.StubOutForDJVM
|
import net.corda.core.StubOutForDJVM
|
||||||
import net.corda.core.crypto.CordaObjectIdentifier.COMPOSITE_KEY
|
import net.corda.core.crypto.CordaObjectIdentifier.COMPOSITE_KEY
|
||||||
import net.corda.core.crypto.CordaObjectIdentifier.COMPOSITE_SIGNATURE
|
import net.corda.core.crypto.CordaObjectIdentifier.COMPOSITE_SIGNATURE
|
||||||
|
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
|
||||||
|
|
||||||
@ -18,6 +19,12 @@ class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME secur
|
|||||||
put("Signature.${CompositeSignature.SIGNATURE_ALGORITHM}", CompositeSignature::class.java.name)
|
put("Signature.${CompositeSignature.SIGNATURE_ALGORITHM}", CompositeSignature::class.java.name)
|
||||||
put("Alg.Alias.Signature.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM)
|
put("Alg.Alias.Signature.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM)
|
||||||
put("Alg.Alias.Signature.OID.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM)
|
put("Alg.Alias.Signature.OID.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM)
|
||||||
|
putPlatformSecureRandomService()
|
||||||
|
}
|
||||||
|
|
||||||
|
@StubOutForDJVM
|
||||||
|
private fun putPlatformSecureRandomService() {
|
||||||
|
putService(PlatformSecureRandomService(this))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,16 +3,40 @@
|
|||||||
package net.corda.core.crypto.internal
|
package net.corda.core.crypto.internal
|
||||||
|
|
||||||
import net.corda.core.DeleteForDJVM
|
import net.corda.core.DeleteForDJVM
|
||||||
|
import net.corda.core.crypto.newSecureRandom
|
||||||
import org.apache.commons.lang.SystemUtils
|
import org.apache.commons.lang.SystemUtils
|
||||||
|
import java.security.Provider
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
|
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.
|
||||||
*/
|
*/
|
||||||
internal val platformSecureRandom: () -> SecureRandom = when {
|
val platformSecureRandom: () -> SecureRandom = when {
|
||||||
SystemUtils.IS_OS_LINUX -> {
|
SystemUtils.IS_OS_LINUX -> {
|
||||||
{ SecureRandom.getInstance("NativePRNGNonBlocking") }
|
{ SecureRandom.getInstance("NativePRNGNonBlocking") }
|
||||||
}
|
}
|
||||||
else -> SecureRandom::getInstanceStrong
|
else -> SecureRandom::getInstanceStrong
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteForDJVM
|
||||||
|
class PlatformSecureRandomService(provider: Provider)
|
||||||
|
: Provider.Service(provider, "SecureRandom", algorithm, PlatformSecureRandomSpi::javaClass.name, null, null) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val algorithm = "CordaPRNG"
|
||||||
|
}
|
||||||
|
|
||||||
|
private val instance: SecureRandomSpi = PlatformSecureRandomSpi()
|
||||||
|
override fun newInstance(constructorParameter: Any?) = instance
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteForDJVM
|
||||||
|
private class PlatformSecureRandomSpi : SecureRandomSpi() {
|
||||||
|
private val secureRandom: SecureRandom = newSecureRandom()
|
||||||
|
|
||||||
|
override fun engineSetSeed(seed: ByteArray) = secureRandom.setSeed(seed)
|
||||||
|
override fun engineNextBytes(bytes: ByteArray) = secureRandom.nextBytes(bytes)
|
||||||
|
override fun engineGenerateSeed(numBytes: Int): ByteArray = secureRandom.generateSeed(numBytes)
|
||||||
|
}
|
||||||
|
@ -18,12 +18,18 @@ import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider
|
|||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.security.Security
|
import java.security.Security
|
||||||
|
|
||||||
internal val cordaSecurityProvider = CordaSecurityProvider().also {
|
val cordaSecurityProvider = CordaSecurityProvider().also {
|
||||||
|
// Among the others, we should register [CordaSecurityProvider] as the first provider, to ensure that when invoking [SecureRandom()]
|
||||||
|
// the [platformSecureRandom] is returned (which is registered in CordaSecurityProvider).
|
||||||
|
// Note that internally, [SecureRandom()] will look through all registered providers.
|
||||||
|
// Then it returns the first PRNG algorithm of the first provider that has registered a SecureRandom
|
||||||
|
// implementation (in our case [CordaSecurityProvider]), or null if none of the registered providers supplies
|
||||||
|
// a SecureRandom implementation.
|
||||||
Security.insertProviderAt(it, 1) // The position is 1-based.
|
Security.insertProviderAt(it, 1) // The position is 1-based.
|
||||||
}
|
}
|
||||||
// OID taken from https://tools.ietf.org/html/draft-ietf-curdle-pkix-00
|
// OID taken from https://tools.ietf.org/html/draft-ietf-curdle-pkix-00
|
||||||
internal val `id-Curve25519ph` = ASN1ObjectIdentifier("1.3.101.112")
|
val `id-Curve25519ph` = ASN1ObjectIdentifier("1.3.101.112")
|
||||||
internal val cordaBouncyCastleProvider = BouncyCastleProvider().apply {
|
val cordaBouncyCastleProvider = BouncyCastleProvider().apply {
|
||||||
putAll(EdDSASecurityProvider())
|
putAll(EdDSASecurityProvider())
|
||||||
// Override the normal EdDSA engine with one which can handle X509 keys.
|
// Override the normal EdDSA engine with one which can handle X509 keys.
|
||||||
put("Signature.${EdDSAEngine.SIGNATURE_ALGORITHM}", X509EdDSAEngine::class.java.name)
|
put("Signature.${EdDSAEngine.SIGNATURE_ALGORITHM}", X509EdDSAEngine::class.java.name)
|
||||||
@ -38,7 +44,7 @@ internal val cordaBouncyCastleProvider = BouncyCastleProvider().apply {
|
|||||||
// TODO: Find a way to make JKS work with bouncy castle provider or implement our own provide so we don't have to register bouncy castle provider.
|
// TODO: Find a way to make JKS work with bouncy castle provider or implement our own provide so we don't have to register bouncy castle provider.
|
||||||
Security.addProvider(it)
|
Security.addProvider(it)
|
||||||
}
|
}
|
||||||
internal val bouncyCastlePQCProvider = BouncyCastlePQCProvider().apply {
|
val bouncyCastlePQCProvider = BouncyCastlePQCProvider().apply {
|
||||||
require(name == "BCPQC") // The constant it comes from is not final.
|
require(name == "BCPQC") // The constant it comes from is not final.
|
||||||
}.also {
|
}.also {
|
||||||
Security.addProvider(it)
|
Security.addProvider(it)
|
||||||
@ -47,7 +53,7 @@ internal val bouncyCastlePQCProvider = BouncyCastlePQCProvider().apply {
|
|||||||
// that could cause unexpected and suspicious behaviour.
|
// that could cause unexpected and suspicious behaviour.
|
||||||
// i.e. if someone removes a Provider and then he/she adds a new one with the same name.
|
// i.e. if someone removes a Provider and then he/she adds a new one with the same name.
|
||||||
// The val is private to avoid any harmful state changes.
|
// The val is private to avoid any harmful state changes.
|
||||||
internal val providerMap = listOf(cordaBouncyCastleProvider, cordaSecurityProvider, bouncyCastlePQCProvider).map { it.name to it }.toMap()
|
val providerMap = listOf(cordaBouncyCastleProvider, cordaSecurityProvider, bouncyCastlePQCProvider).map { it.name to it }.toMap()
|
||||||
|
|
||||||
@DeleteForDJVM
|
@DeleteForDJVM
|
||||||
internal fun platformSecureRandomFactory(): SecureRandom = platformSecureRandom() // To minimise diff of CryptoUtils against open-source.
|
fun platformSecureRandomFactory(): SecureRandom = platformSecureRandom() // To minimise diff of CryptoUtils against open-source.
|
||||||
|
@ -6,6 +6,7 @@ import net.corda.core.crypto.Crypto.ECDSA_SECP256R1_SHA256
|
|||||||
import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512
|
import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512
|
||||||
import net.corda.core.crypto.Crypto.RSA_SHA256
|
import net.corda.core.crypto.Crypto.RSA_SHA256
|
||||||
import net.corda.core.crypto.Crypto.SPHINCS256_SHA256
|
import net.corda.core.crypto.Crypto.SPHINCS256_SHA256
|
||||||
|
import net.corda.core.crypto.internal.PlatformSecureRandomService
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.i2p.crypto.eddsa.EdDSAKey
|
import net.i2p.crypto.eddsa.EdDSAKey
|
||||||
import net.i2p.crypto.eddsa.EdDSAPrivateKey
|
import net.i2p.crypto.eddsa.EdDSAPrivateKey
|
||||||
@ -16,10 +17,7 @@ import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable
|
|||||||
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
|
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
|
||||||
import org.apache.commons.lang.ArrayUtils.EMPTY_BYTE_ARRAY
|
import org.apache.commons.lang.ArrayUtils.EMPTY_BYTE_ARRAY
|
||||||
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo
|
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
|
||||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
|
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
|
||||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
|
|
||||||
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder
|
|
||||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
|
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
|
||||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
|
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
|
||||||
import org.bouncycastle.jce.ECNamedCurveTable
|
import org.bouncycastle.jce.ECNamedCurveTable
|
||||||
@ -31,9 +29,9 @@ import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey
|
|||||||
import org.junit.Assert.assertNotEquals
|
import org.junit.Assert.assertNotEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.security.KeyPair
|
|
||||||
import java.security.KeyPairGenerator
|
import java.security.KeyPairGenerator
|
||||||
import java.security.cert.X509Certificate
|
import java.security.SecureRandom
|
||||||
|
import java.security.Security
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
@ -934,26 +932,25 @@ class CryptoUtilsTest {
|
|||||||
this.outputStream.close()
|
this.outputStream.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createCert(signer: ContentSigner, keyPair: KeyPair): X509Certificate {
|
@Test
|
||||||
val dname = X500Name("CN=TestEntity")
|
fun `test default SecureRandom uses platformSecureRandom`() {
|
||||||
val startDate = Calendar.getInstance().let { cal ->
|
// Note than in Corda, [CordaSecurityProvider] is registered as the first provider.
|
||||||
cal.time = Date()
|
|
||||||
cal.add(Calendar.HOUR, -1)
|
// Remove [CordaSecurityProvider] in case it is already registered.
|
||||||
cal.time
|
Security.removeProvider(CordaSecurityProvider.PROVIDER_NAME)
|
||||||
}
|
// Try after removing CordaSecurityProvider.
|
||||||
val endDate = Calendar.getInstance().let { cal ->
|
val secureRandomNotRegisteredCordaProvider = SecureRandom()
|
||||||
cal.time = startDate
|
assertNotEquals(PlatformSecureRandomService.algorithm, secureRandomNotRegisteredCordaProvider.algorithm)
|
||||||
cal.add(Calendar.YEAR, 1)
|
|
||||||
cal.time
|
// Now register CordaSecurityProvider as last Provider.
|
||||||
}
|
Security.addProvider(CordaSecurityProvider())
|
||||||
val certificate = JcaX509v3CertificateBuilder(
|
val secureRandomRegisteredLastCordaProvider = SecureRandom()
|
||||||
dname,
|
assertNotEquals(PlatformSecureRandomService.algorithm, secureRandomRegisteredLastCordaProvider.algorithm)
|
||||||
BigInteger.TEN,
|
|
||||||
startDate,
|
// Remove Corda Provider again and add it as the first Provider entry.
|
||||||
endDate,
|
Security.removeProvider(CordaSecurityProvider.PROVIDER_NAME)
|
||||||
dname,
|
Security.insertProviderAt(CordaSecurityProvider(), 1) // This is base-1.
|
||||||
keyPair.public
|
val secureRandomRegisteredFirstCordaProvider = SecureRandom()
|
||||||
).build(signer)
|
assertEquals(PlatformSecureRandomService.algorithm, secureRandomRegisteredFirstCordaProvider.algorithm)
|
||||||
return JcaX509CertificateConverter().getCertificate(certificate)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user