diff --git a/.ci/api-current.txt b/.ci/api-current.txt index 72eea8da0a..d4f9301255 100644 --- a/.ci/api-current.txt +++ b/.ci/api-current.txt @@ -8290,11 +8290,6 @@ public static final class net.corda.core.utilities.ProgressTracker$UNSTARTED ext public interface net.corda.core.utilities.PropertyDelegate public abstract T getValue(Object, kotlin.reflect.KProperty) ## -public final class net.corda.core.utilities.SgxSupport extends java.lang.Object - public static final boolean isInsideEnclave() - @NotNull - public static final net.corda.core.utilities.SgxSupport INSTANCE -## public final class net.corda.core.utilities.ThreadDumpUtilsKt extends java.lang.Object @NotNull public static final String asString(management.ThreadInfo, int) diff --git a/build.gradle b/build.gradle index 94ce6dbe9b..34fe923b28 100644 --- a/build.gradle +++ b/build.gradle @@ -90,7 +90,6 @@ buildscript { ext.h2_version = constants.getProperty("h2Version") ext.rxjava_version = constants.getProperty("rxjavaVersion") ext.dokka_version = constants.getProperty("dokkaVersion") - ext.eddsa_version = constants.getProperty("eddsaVersion") ext.dependency_checker_version = constants.getProperty("dependencyCheckerVersion") ext.commons_collections_version = constants.getProperty("commonsCollectionsVersion") ext.beanutils_version = constants.getProperty("beanutilsVersion") @@ -178,7 +177,6 @@ buildscript { classpath "com.guardsquare:proguard-gradle:$proguard_version" classpath 'com.github.ben-manes:gradle-versions-plugin:0.15.0' classpath "org.jetbrains.dokka:dokka-base:$dokka_version" - classpath "net.i2p.crypto:eddsa:$eddsa_version" // Needed for ServiceIdentityGenerator in the build environment. classpath "org.owasp:dependency-check-gradle:$dependency_checker_version" classpath "org.jfrog.buildinfo:build-info-extractor-gradle:$artifactory_plugin_version" // Capsule gradle plugin forked and maintained locally to support Gradle 5.x diff --git a/constants.properties b/constants.properties index 3880d9f178..efd2246e17 100644 --- a/constants.properties +++ b/constants.properties @@ -21,7 +21,7 @@ guavaVersion=28.0-jre quasarVersion=0.9.0_r3 dockerJavaVersion=3.2.5 proguardVersion=7.3.1 -// bouncy castle version must not be changed on a patch release. Needs a full release test cycle to flush out any issues. +# Bouncy Castle version must not be changed on a patch release. Needs a full release test cycle to flush out any issues. bouncycastleVersion=1.75 classgraphVersion=4.8.135 disruptorVersion=3.4.2 @@ -79,7 +79,6 @@ hibernateVersion=5.6.14.Final h2Version=2.2.224 rxjavaVersion=1.3.8 dokkaVersion=1.8.20 -eddsaVersion=0.3.0 dependencyCheckerVersion=5.2.0 commonsCollectionsVersion=4.3 beanutilsVersion=1.9.4 diff --git a/core-tests/build.gradle b/core-tests/build.gradle index 80c9b5c6ab..16347f88e2 100644 --- a/core-tests/build.gradle +++ b/core-tests/build.gradle @@ -184,7 +184,6 @@ quasar { "io.github.classgraph**", "io.netty*", "liquibase**", - "net.i2p.crypto.**", "nonapi.io.github.classgraph.**", "org.apiguardian.**", "org.bouncycastle**", diff --git a/core/build.gradle b/core/build.gradle index 427179cb67..b1c5e1d7dd 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -36,8 +36,6 @@ dependencies { // For caches rather than guava implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version" implementation "org.apache.commons:commons-lang3:$commons_lang3_version" - // Java ed25519 implementation. See https://github.com/str4d/ed25519-java/ - implementation "net.i2p.crypto:eddsa:$eddsa_version" // Bouncy castle support needed for X509 certificate manipulation implementation "org.bouncycastle:bcprov-jdk18on:${bouncycastle_version}" // required to use @Type annotation @@ -93,19 +91,7 @@ processTestResources { } } -compileTestJava { - options.compilerArgs += [ - '--add-exports', 'java.base/sun.security.util=ALL-UNNAMED', - '--add-exports', 'java.base/sun.security.x509=ALL-UNNAMED' - ] -} - test { - // TODO This obscures whether any Corda client APIs need these JVM flags as well (which they shouldn't do) - jvmArgs += [ - '--add-exports', 'java.base/sun.security.util=ALL-UNNAMED', - '--add-exports', 'java.base/sun.security.x509=ALL-UNNAMED' - ] maxParallelForks = (System.env.CORDA_CORE_TESTING_FORKS == null) ? 1 : "$System.env.CORDA_CORE_TESTING_FORKS".toInteger() } @@ -129,7 +115,6 @@ quasar { "io.github.classgraph**", "io.netty*", "liquibase**", - "net.i2p.crypto.**", "nonapi.io.github.classgraph.**", "org.apiguardian.**", "org.bouncycastle**", diff --git a/core/src/main/kotlin/net/corda/core/crypto/CordaSecurityProvider.kt b/core/src/main/kotlin/net/corda/core/crypto/CordaSecurityProvider.kt index 0be1edfd55..70bf257a3f 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/CordaSecurityProvider.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/CordaSecurityProvider.kt @@ -5,11 +5,10 @@ import net.corda.core.crypto.CordaObjectIdentifier.COMPOSITE_SIGNATURE import net.corda.core.crypto.internal.PlatformSecureRandomService import org.bouncycastle.asn1.ASN1ObjectIdentifier import java.security.Provider -import java.util.* +import java.util.Optional import java.util.concurrent.ConcurrentHashMap -@Suppress("DEPRECATION") // JDK11: should replace with Provider(String name, double version, String info) (since 9) -class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME security provider wrapper") { +class CordaSecurityProvider : Provider(PROVIDER_NAME, "0.2", "$PROVIDER_NAME security provider") { companion object { const val PROVIDER_NAME = "Corda" } @@ -17,21 +16,8 @@ class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME secur private val services = ConcurrentHashMap, Optional>() init { - put("KeyFactory.${CompositeKey.KEY_ALGORITHM}", CompositeKeyFactory::class.java.name) - put("Alg.Alias.KeyFactory.$COMPOSITE_KEY", CompositeKey.KEY_ALGORITHM) - put("Alg.Alias.KeyFactory.OID.$COMPOSITE_KEY", CompositeKey.KEY_ALGORITHM) - put("Signature.${CompositeSignature.SIGNATURE_ALGORITHM}", CompositeSignature::class.java.name) - put("Alg.Alias.Signature.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM) - put("Alg.Alias.Signature.OID.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM) - putPlatformSecureRandomService() - - // JDK11+ - Hack to set Provider#legacyChanged to false, without this SecureRandom will not - // pickup our random implementation (even if our provider is the first provider in - // the chain). - super.getService("UNDEFINED", "UNDEFINED") - } - - private fun putPlatformSecureRandomService() { + putService(Service(this, "KeyFactory", CompositeKey.KEY_ALGORITHM, CompositeKeyFactory::class.java.name, listOf("$COMPOSITE_KEY", "OID.$COMPOSITE_KEY"), null)) + putService(Service(this, "Signature", CompositeSignature.SIGNATURE_ALGORITHM, CompositeSignature::class.java.name, listOf("$COMPOSITE_SIGNATURE", "OID.$COMPOSITE_SIGNATURE"), null)) putService(PlatformSecureRandomService(this)) } diff --git a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt index 8018c68892..7fbdde3cd7 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt @@ -1,31 +1,26 @@ package net.corda.core.crypto import net.corda.core.CordaOID +import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512 import net.corda.core.crypto.internal.AliasPrivateKey +import net.corda.core.crypto.internal.Curve25519.isOnCurve25519 import net.corda.core.crypto.internal.Instances.withSignature import net.corda.core.crypto.internal.PublicKeyCache 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.`id-Curve25519ph` import net.corda.core.crypto.internal.providerMap +import net.corda.core.crypto.internal.sunEcProvider import net.corda.core.internal.utilities.PrivateInterner import net.corda.core.serialization.serialize import net.corda.core.utilities.ByteSequence -import net.i2p.crypto.eddsa.EdDSAEngine -import net.i2p.crypto.eddsa.EdDSAPrivateKey -import net.i2p.crypto.eddsa.EdDSAPublicKey -import net.i2p.crypto.eddsa.math.GroupElement -import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec -import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable -import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec -import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec import org.bouncycastle.asn1.ASN1Integer import org.bouncycastle.asn1.ASN1ObjectIdentifier import org.bouncycastle.asn1.DERNull import org.bouncycastle.asn1.DERUTF8String import org.bouncycastle.asn1.DLSequence import org.bouncycastle.asn1.bc.BCObjectIdentifiers +import org.bouncycastle.asn1.edec.EdECObjectIdentifiers import org.bouncycastle.asn1.nist.NISTObjectIdentifiers import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers import org.bouncycastle.asn1.pkcs.PrivateKeyInfo @@ -34,6 +29,9 @@ import org.bouncycastle.asn1.x509.AlgorithmIdentifier import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo import org.bouncycastle.asn1.x9.X9ObjectIdentifiers import org.bouncycastle.crypto.CryptoServicesRegistrar +import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters +import org.bouncycastle.crypto.util.PrivateKeyInfoFactory +import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey import org.bouncycastle.jcajce.provider.asymmetric.edec.BCEdDSAPrivateKey @@ -49,20 +47,24 @@ import org.bouncycastle.jce.spec.ECPublicKeySpec import org.bouncycastle.math.ec.ECConstants import org.bouncycastle.math.ec.FixedPointCombMultiplier import org.bouncycastle.math.ec.WNafUtil +import org.bouncycastle.math.ec.rfc8032.Ed25519 import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey import org.bouncycastle.pqc.jcajce.spec.SPHINCS256KeyGenParameterSpec import java.math.BigInteger 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.Signature import java.security.SignatureException +import java.security.interfaces.EdECPrivateKey +import java.security.interfaces.EdECPublicKey import java.security.spec.InvalidKeySpecException +import java.security.spec.NamedParameterSpec import java.security.spec.PKCS8EncodedKeySpec import java.security.spec.X509EncodedKeySpec import javax.crypto.Mac @@ -77,7 +79,7 @@ import javax.crypto.spec.SecretKeySpec *
  • RSA_SHA256 (RSA PKCS#1 using SHA256 as hash algorithm). *
  • ECDSA_SECP256K1_SHA256 (ECDSA using the secp256k1 Koblitz curve and SHA256 as hash algorithm). *
  • ECDSA_SECP256R1_SHA256 (ECDSA using the secp256r1 (NIST P-256) curve and SHA256 as hash algorithm). - *
  • EDDSA_ED25519_SHA512 (EdDSA using the ed255519 twisted Edwards curve and SHA512 as hash algorithm). + *
  • EDDSA_ED25519_SHA512 (EdDSA using the ed25519 twisted Edwards curve and SHA512 as hash algorithm). *
  • SPHINCS256_SHA512 (SPHINCS-256 hash-based signature scheme using SHA512 as hash algorithm). * */ @@ -95,7 +97,7 @@ object Crypto { listOf(AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, null)), cordaBouncyCastleProvider.name, "RSA", - "SHA256WITHRSA", + "SHA256withRSA", null, 3072, "RSA_SHA256 signature scheme using SHA256 as hash algorithm." @@ -140,13 +142,12 @@ object Crypto { val EDDSA_ED25519_SHA512: SignatureScheme = SignatureScheme( 4, "EDDSA_ED25519_SHA512", - AlgorithmIdentifier(`id-Curve25519ph`, null), - emptyList(), // Both keys and the signature scheme use the same OID in i2p library. - // We added EdDSA to bouncy castle for certificate signing. - cordaBouncyCastleProvider.name, - "1.3.101.112", - EdDSAEngine.SIGNATURE_ALGORITHM, - EdDSANamedCurveTable.getByName("ED25519"), + AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519, null), + emptyList(), // Both keys and the signature scheme use the same OID. + sunEcProvider.name, + "Ed25519", + "Ed25519", + NamedParameterSpec.ED25519, 256, "EdDSA signature scheme using the ed25519 twisted Edwards curve." ) @@ -164,11 +165,11 @@ object Crypto { val SPHINCS256_SHA256 = SignatureScheme( 5, "SPHINCS-256_SHA512", - AlgorithmIdentifier(BCObjectIdentifiers.sphincs256_with_SHA512, DLSequence(arrayOf(ASN1Integer(0), SHA512_256))), + AlgorithmIdentifier(BCObjectIdentifiers.sphincs256_with_SHA512, null), listOf(AlgorithmIdentifier(BCObjectIdentifiers.sphincs256, DLSequence(arrayOf(ASN1Integer(0), SHA512_256)))), bouncyCastlePQCProvider.name, "SPHINCS256", - "SHA512WITHSPHINCS256", + "SHA512withSPHINCS256", SPHINCS256KeyGenParameterSpec(SPHINCS256KeyGenParameterSpec.SHA512_256), 256, "SPHINCS-256 hash-based signature scheme. It provides 128bit security against post-quantum attackers " + @@ -244,8 +245,9 @@ object Crypto { @JvmStatic fun findSignatureScheme(algorithm: AlgorithmIdentifier): SignatureScheme { - return algorithmMap[normaliseAlgorithmIdentifier(algorithm)] - ?: throw IllegalArgumentException("Unrecognised algorithm: ${algorithm.algorithm.id}") + return requireNotNull(algorithmMap[normaliseAlgorithmIdentifier(algorithm)]) { + "Unrecognised algorithm identifier: ${algorithm.algorithm} ${algorithm.parameters}" + } } /** Find [SignatureScheme] by platform specific schemeNumberID. */ @@ -307,12 +309,11 @@ object Crypto { @JvmStatic fun decodePrivateKey(encodedKey: ByteArray): PrivateKey { val keyInfo = PrivateKeyInfo.getInstance(encodedKey) - if (keyInfo.privateKeyAlgorithm.algorithm == ASN1ObjectIdentifier(CordaOID.ALIAS_PRIVATE_KEY)) { - return convertIfBCEdDSAPrivateKey(decodeAliasPrivateKey(keyInfo)) + return if (keyInfo.privateKeyAlgorithm.algorithm == ASN1ObjectIdentifier(CordaOID.ALIAS_PRIVATE_KEY)) { + decodeAliasPrivateKey(keyInfo) + } else { + findSignatureScheme(keyInfo.privateKeyAlgorithm).keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey)) } - val signatureScheme = findSignatureScheme(keyInfo.privateKeyAlgorithm) - val keyFactory = keyFactory(signatureScheme) - return convertIfBCEdDSAPrivateKey(keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey))) } private fun decodeAliasPrivateKey(keyInfo: PrivateKeyInfo): PrivateKey { @@ -351,8 +352,7 @@ object Crypto { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" } try { - val keyFactory = keyFactory(signatureScheme) - return convertIfBCEdDSAPrivateKey(keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey))) + return signatureScheme.keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey)) } catch (ikse: InvalidKeySpecException) { throw InvalidKeySpecException("This private key cannot be decoded, please ensure it is PKCS8 encoded and that " + "it corresponds to the input scheme's code name.", ikse) @@ -368,12 +368,11 @@ object Crypto { */ @JvmStatic fun decodePublicKey(encodedKey: ByteArray): PublicKey { - return PublicKeyCache.publicKeyForCachedBytes(ByteSequence.of(encodedKey)) ?: { + return PublicKeyCache.publicKeyForCachedBytes(ByteSequence.of(encodedKey)) ?: run { val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(encodedKey) val signatureScheme = findSignatureScheme(subjectPublicKeyInfo.algorithm) - val keyFactory = keyFactory(signatureScheme) - convertIfBCEdDSAPublicKey(keyFactory.generatePublic(X509EncodedKeySpec(encodedKey))) - }() + internPublicKey(signatureScheme.keyFactory.generatePublic(X509EncodedKeySpec(encodedKey))) + } } @JvmStatic @@ -412,8 +411,7 @@ object Crypto { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" } try { - val keyFactory = keyFactory(signatureScheme) - return convertIfBCEdDSAPublicKey(keyFactory.generatePublic(X509EncodedKeySpec(encodedKey))) + return signatureScheme.keyFactory.generatePublic(X509EncodedKeySpec(encodedKey)) } catch (ikse: InvalidKeySpecException) { throw InvalidKeySpecException("This public key cannot be decoded, please ensure it is X509 encoded and " + "that it corresponds to the input scheme's code name.", ikse) @@ -471,12 +469,8 @@ object Crypto { return withSignature(signatureScheme) { signature -> // 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 - // SecureRandom implementation. Also, SPHINCS-256 implementation in BouncyCastle 1.60 fails with - // ClassCastException if we invoke initSign with a SecureRandom as an input. - // TODO Although we handle the above issue here, consider updating to BC 1.61+ which provides a fix. - if (signatureScheme == EDDSA_ED25519_SHA512 - || signatureScheme == SPHINCS256_SHA256 - || signatureScheme == RSA_SHA256) { + // SecureRandom implementation. + if (signatureScheme == EDDSA_ED25519_SHA512 || signatureScheme == SPHINCS256_SHA256 || signatureScheme == RSA_SHA256) { signature.initSign(privateKey) } else { // The rest of the algorithms will require a SecureRandom input (i.e., ECDSA or any new algorithm for which @@ -716,8 +710,7 @@ object Crypto { * This operation is currently supported for ECDSA secp256r1 (NIST P-256), ECDSA secp256k1 and EdDSA ed25519. * * Similarly to BIP32, the implemented algorithm uses an HMAC function based on SHA512 and it is actually - * an implementation the HKDF rfc - Step 1: Extract function, - * @see HKDF + * an implementation of the [HKDF rfc - Step 1: Extract function](https://tools.ietf.org/html/rfc5869), * which is practically a variation of the private-parent-key -> private-child-key hardened key generation of BIP32. * * Unlike BIP32, where both private and public keys are extended to prevent deterministically @@ -725,8 +718,8 @@ object Crypto { * without a chain-code and the generated key relies solely on the security of the private key. * * Although without a chain-code we lose the aforementioned property of not depending solely on the key, - * it should be mentioned that the cryptographic strength of the HMAC depends upon the size of the secret key. - * @see HMAC Security + * it should be mentioned that the cryptographic strength of the HMAC depends upon the size of the secret key + * (see [HMAC Security](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code#Security)). * Thus, as long as the master key is kept secret and has enough entropy (~256 bits for EC-schemes), the system * is considered secure. * @@ -743,9 +736,9 @@ object Crypto { *
  • salt values should not be chosen by an attacker. *

    * - * Regarding the last requirement, according to Krawczyk's HKDF scheme: While there is no need to keep the salt secret, - * it is assumed that salt values are independent of the input keying material. - * @see Cryptographic Extraction and Key Derivation - The HKDF Scheme. + * Regarding the last requirement, according to Krawczyk's HKDF scheme: _While there is no need to keep the salt secret, + * it is assumed that salt values are independent of the input keying material_ + * (see [Cryptographic Extraction and Key Derivation - The HKDF Scheme](http://eprint.iacr.org/2010/264.pdf)). * * There are also protocols that require an authenticated nonce (e.g. when a DH derived key is used as a seed) and thus * we need to make sure that nonces come from legitimate parties rather than selected by an attacker. @@ -845,13 +838,7 @@ object Crypto { private fun deriveKeyPairEdDSA(privateKey: PrivateKey, seed: ByteArray): KeyPair { // Compute HMAC(privateKey, seed). val macBytes = deriveHMAC(privateKey, seed) - - // Calculate key pair. - val params = EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec - val bytes = macBytes.copyOf(params.curve.field.getb() / 8) // Need to pad the entropy to the valid seed length. - val privateKeyD = EdDSAPrivateKeySpec(bytes, params) - val publicKeyD = EdDSAPublicKeySpec(privateKeyD.a, params) - return KeyPair(internPublicKey(EdDSAPublicKey(publicKeyD)), EdDSAPrivateKey(privateKeyD)) + return deriveEdDSAKeyPair(macBytes) } /** @@ -882,15 +869,20 @@ object Crypto { fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy) // Custom key pair generator from entropy. - // The BigIntenger.toByteArray() uses the two's-complement representation. + // The BigInteger.toByteArray() uses the two's-complement representation. // The entropy is transformed to a byte array in big-endian byte-order and // only the first ed25519.field.getb() / 8 bytes are used. private fun deriveEdDSAKeyPairFromEntropy(entropy: BigInteger): KeyPair { - val params = EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec - val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8) // Need to pad the entropy to the valid seed length. - val priv = EdDSAPrivateKeySpec(bytes, params) - val pub = EdDSAPublicKeySpec(priv.a, params) - return KeyPair(internPublicKey(EdDSAPublicKey(pub)), EdDSAPrivateKey(priv)) + return deriveEdDSAKeyPair(entropy.toByteArray().copyOf(Ed25519.PUBLIC_KEY_SIZE)) + } + + private fun deriveEdDSAKeyPair(bytes: ByteArray): KeyPair { + val privateKeyParams = Ed25519PrivateKeyParameters(bytes, 0) // This will copy the first 256 bits + val encodedPrivateKey = PrivateKeyInfoFactory.createPrivateKeyInfo(privateKeyParams).encoded + val privateKey = EDDSA_ED25519_SHA512.keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedPrivateKey)) + val encodedPublicKey = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(privateKeyParams.generatePublicKey()).encoded + val publicKey = EDDSA_ED25519_SHA512.keyFactory.generatePublic(X509EncodedKeySpec(encodedPublicKey)) + return KeyPair(internPublicKey(publicKey), privateKey) } // Custom key pair generator from an entropy required for various tests. It is similar to deriveKeyPairECDSA, @@ -925,7 +917,7 @@ object Crypto { val mac = Mac.getInstance("HmacSHA512", cordaBouncyCastleProvider) val keyData = when (privateKey) { is BCECPrivateKey -> privateKey.d.toByteArray() - is EdDSAPrivateKey -> privateKey.geta() + is EdECPrivateKey -> privateKey.bytes.get() else -> throw InvalidKeyException("Key type ${privateKey.algorithm} is not supported for deterministic key derivation") } val key = SecretKeySpec(keyData, "HmacSHA512") @@ -936,12 +928,12 @@ object Crypto { /** * Check if a point's coordinates are on the expected curve to avoid certain types of ECC attacks. * Point-at-infinity is not permitted as well. - * @see Small subgroup and invalid-curve attacks for a more descriptive explanation on such attacks. + * See [Small subgroup and invalid-curve attacks](https://safecurves.cr.yp.to/twist.html) for a more descriptive explanation on such attacks. * We use this function on [validatePublicKey], which is currently used for signature verification only. * Thus, as these attacks are mostly not relevant to signature verification, we should note that * we are doing it out of an abundance of caution and specifically to proactively protect developers * against using these points as part of a DH key agreement or for use cases as yet unimagined. - * This method currently applies to BouncyCastle's ECDSA (both R1 and K1 curves) and I2P's EdDSA (ed25519 curve). + * This method currently applies to BouncyCastle's ECDSA (both R1 and K1 curves) and JCA EdDSA (ed25519 curve). * @param publicKey a [PublicKey], usually used to validate a signer's public key in on the Curve. * @param signatureScheme a [SignatureScheme] object, retrieved from supported signature schemes, see [Crypto]. * @return true if the point lies on the curve or false if it doesn't. @@ -954,17 +946,11 @@ object Crypto { } return when (publicKey) { is BCECPublicKey -> publicKey.parameters == signatureScheme.algSpec && !publicKey.q.isInfinity && publicKey.q.isValid - is EdDSAPublicKey -> publicKey.params == signatureScheme.algSpec && !isEdDSAPointAtInfinity(publicKey) && publicKey.a.isOnCurve + is EdECPublicKey -> signatureScheme == EDDSA_ED25519_SHA512 && publicKey.params.name.equals("Ed25519", ignoreCase = true) && publicKey.point.isOnCurve25519 else -> throw IllegalArgumentException("Unsupported key type: ${publicKey::class}") } } - // Return true if EdDSA publicKey is point at infinity. - // For EdDSA a custom function is required as it is not supported by the I2P implementation. - private fun isEdDSAPointAtInfinity(publicKey: EdDSAPublicKey): Boolean { - return publicKey.a.toP3() == (EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec).curve.getZero(GroupElement.Representation.P3) - } - /** Check if the requested [SignatureScheme] is supported by the system. */ @JvmStatic fun isSupportedSignatureScheme(signatureScheme: SignatureScheme): Boolean { @@ -981,7 +967,7 @@ object Crypto { // Check if a public key satisfies algorithm specs (for ECC: key should lie on the curve and not being point-at-infinity). private fun validatePublicKey(signatureScheme: SignatureScheme, key: PublicKey): Boolean { return when (key) { - is BCECPublicKey, is EdDSAPublicKey -> publicKeyOnCurve(signatureScheme, key) + is BCECPublicKey, is EdECPublicKey -> publicKeyOnCurve(signatureScheme, key) is BCRSAPublicKey -> key.modulus.bitLength() >= 2048 // Although the recommended RSA key size is 3072, we accept any key >= 2048bits. is BCSphincs256PublicKey -> true else -> throw IllegalArgumentException("Unsupported key type: ${key::class}") @@ -991,21 +977,6 @@ object Crypto { private val interner = PrivateInterner() private fun internPublicKey(key: PublicKey): PublicKey = PublicKeyCache.cachePublicKey(interner.intern(key)) - - private fun convertIfBCEdDSAPublicKey(key: PublicKey): PublicKey { - return internPublicKey(when (key) { - is BCEdDSAPublicKey -> EdDSAPublicKey(X509EncodedKeySpec(key.encoded)) - else -> key - }) - } - - private fun convertIfBCEdDSAPrivateKey(key: PrivateKey): PrivateKey { - return when (key) { - is BCEdDSAPrivateKey -> EdDSAPrivateKey(PKCS8EncodedKeySpec(key.encoded)) - else -> key - } - } - /** * Convert a public key to a supported implementation. * @param key a public key. @@ -1031,9 +1002,9 @@ object Crypto { is BCECPublicKey -> internPublicKey(key) is BCRSAPublicKey -> internPublicKey(key) is BCSphincs256PublicKey -> internPublicKey(key) - is EdDSAPublicKey -> internPublicKey(key) + is EdECPublicKey -> internPublicKey(key) is CompositeKey -> internPublicKey(key) - is BCEdDSAPublicKey -> convertIfBCEdDSAPublicKey(key) + is BCEdDSAPublicKey -> internPublicKey(key) else -> decodePublicKey(key.encoded) } } @@ -1052,8 +1023,8 @@ object Crypto { is BCECPrivateKey -> key is BCRSAPrivateKey -> key is BCSphincs256PrivateKey -> key - is EdDSAPrivateKey -> key - is BCEdDSAPrivateKey -> convertIfBCEdDSAPrivateKey(key) + is EdECPrivateKey -> key + is BCEdDSAPrivateKey -> key else -> decodePrivateKey(key.encoded) } } @@ -1095,8 +1066,4 @@ object Crypto { private fun setBouncyCastleRNG() { CryptoServicesRegistrar.setSecureRandom(newSecureRandom()) } - - private fun keyFactory(signatureScheme: SignatureScheme) = signatureScheme.getKeyFactory { - KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName]) - } } diff --git a/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt b/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt index 3fc649f989..797123bf4d 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt @@ -3,7 +3,8 @@ package net.corda.core.crypto import net.corda.core.contracts.PrivacySalt -import net.corda.core.crypto.internal.platformSecureRandomFactory +import net.corda.core.crypto.internal.PlatformSecureRandomService +import net.corda.core.crypto.internal.cordaSecurityProvider import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.serialize import net.corda.core.utilities.OpaqueBytes @@ -19,6 +20,7 @@ import java.security.PublicKey import java.security.SecureRandom import java.security.SecureRandomSpi import java.security.SignatureException +import kotlin.math.abs /** * Utility to simplify the act of signing a byte array. @@ -231,7 +233,12 @@ object DummySecureRandom : SecureRandom(DummySecureRandomSpi(), null) * which should never happen and suggests an unusual JVM or non-standard Java library. */ @Throws(NoSuchAlgorithmException::class) -fun newSecureRandom(): SecureRandom = platformSecureRandomFactory() +fun newSecureRandom(): SecureRandom = sharedSecureRandom + +// This is safe to share because of the underlying implementation of SecureRandomSpi +private val sharedSecureRandom: SecureRandom by lazy(LazyThreadSafetyMode.PUBLICATION) { + SecureRandom.getInstance(PlatformSecureRandomService.ALGORITHM, cordaSecurityProvider) +} /** * Returns a random positive non-zero long generated using a secure RNG. This function sacrifies a bit of entropy in order @@ -239,7 +246,7 @@ fun newSecureRandom(): SecureRandom = platformSecureRandomFactory() */ fun random63BitValue(): Long { while (true) { - val candidate = Math.abs(newSecureRandom().nextLong()) + val candidate = abs(newSecureRandom().nextLong()) // No need to check for -0L if (candidate != 0L && candidate != Long.MIN_VALUE) { return candidate diff --git a/core/src/main/kotlin/net/corda/core/crypto/SignatureScheme.kt b/core/src/main/kotlin/net/corda/core/crypto/SignatureScheme.kt index 27b3fc4750..885868873a 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/SignatureScheme.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/SignatureScheme.kt @@ -1,5 +1,6 @@ package net.corda.core.crypto +import net.corda.core.crypto.internal.providerMap import org.bouncycastle.asn1.x509.AlgorithmIdentifier import java.security.KeyFactory import java.security.Signature @@ -36,11 +37,6 @@ data class SignatureScheme( @Volatile private var memoizedKeyFactory: KeyFactory? = null - internal fun getKeyFactory(factoryFactory: () -> KeyFactory): KeyFactory { - return memoizedKeyFactory ?: run { - val newFactory = factoryFactory() - memoizedKeyFactory = newFactory - newFactory - } - } + internal val keyFactory: KeyFactory + get() = memoizedKeyFactory ?: KeyFactory.getInstance(algorithmName, providerMap[providerName]).also { memoizedKeyFactory = it } } diff --git a/core/src/main/kotlin/net/corda/core/crypto/internal/Curve25519.kt b/core/src/main/kotlin/net/corda/core/crypto/internal/Curve25519.kt new file mode 100644 index 0000000000..7b5a7dd9f8 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/crypto/internal/Curve25519.kt @@ -0,0 +1,46 @@ + +package net.corda.core.crypto.internal + +import java.math.BigInteger +import java.math.BigInteger.TWO +import java.security.spec.EdECPoint + +/** + * Parameters for Curve25519, as defined in https://www.rfc-editor.org/rfc/rfc7748#section-4.1. + */ +@Suppress("MagicNumber") +object Curve25519 { + val p = TWO.pow(255) - 19.toBigInteger() // 2^255 - 19 + val d = ModP(BigInteger("37095705934669439343138083508754565189542113879843219016388785533085940283555")) + + val EdECPoint.isOnCurve25519: Boolean + // https://www.rfc-editor.org/rfc/rfc8032.html#section-5.1.3 + get() { + if (y >= p) return false + val ySquared = ModP(y).pow(TWO) + val u = ySquared - 1 // y^2 - 1 (mod p) + val v = d * ySquared + 1 // dy^2 + 1 (mod p) + val x = (u / v).pow((p + 3.toBigInteger()).shiftRight(3)) // (u/v)^((p+3)/8) (mod p) + val vxSquared = v * x.pow(TWO) + return vxSquared == u || vxSquared == -u + } + + fun BigInteger.modP(): ModP = ModP(mod(p)) + + private fun BigInteger.additiveInverse(): BigInteger = p - this + + data class ModP(val value: BigInteger) : Comparable { + fun pow(exponent: BigInteger): ModP = ModP(value.modPow(exponent, p)) + + operator fun unaryMinus(): ModP = ModP(value.additiveInverse()) + operator fun plus(other: ModP): ModP = (this.value + other.value).modP() + operator fun plus(other: Int): ModP = (this.value + other.toBigInteger()).modP() + operator fun minus(other: ModP): ModP = (this.value + other.value.additiveInverse()).modP() + operator fun minus(other: Int): ModP = (this.value + other.toBigInteger().additiveInverse()).modP() + operator fun times(other: ModP): ModP = (this.value * other.value).modP() + operator fun div(other: ModP): ModP = (this.value * other.value.modInverse(p)).modP() + + override fun compareTo(other: ModP): Int = this.value.compareTo(other.value) + override fun toString(): String = "$value (mod Curve25519 p)" + } +} diff --git a/core/src/main/kotlin/net/corda/core/crypto/internal/Instances.kt b/core/src/main/kotlin/net/corda/core/crypto/internal/Instances.kt index fc7336f855..64b5feef78 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/internal/Instances.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/internal/Instances.kt @@ -26,9 +26,8 @@ object Instances { private val signatureFactory: SignatureFactory = CachingSignatureFactory() // 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) + private data class SignatureKey(val algorithm: String, val providerName: String?, val providerVersion: String?) { + constructor(algorithm: String, provider: Provider?) : this(algorithm, provider?.name, provider?.versionStr) } private class CachingSignatureFactory : SignatureFactory { diff --git a/core/src/main/kotlin/net/corda/core/crypto/internal/PlatformSecureRandom.kt b/core/src/main/kotlin/net/corda/core/crypto/internal/PlatformSecureRandom.kt index 6e94948c3b..1570c55f82 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/internal/PlatformSecureRandom.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/internal/PlatformSecureRandom.kt @@ -2,8 +2,6 @@ package net.corda.core.crypto.internal import io.netty.util.concurrent.FastThreadLocal -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 java.io.DataInputStream @@ -16,21 +14,8 @@ import java.security.SecureRandom import java.security.SecureRandomSpi import kotlin.system.exitProcess -/** - * This has been migrated into a separate class so that it - * is easier to delete from the core-deterministic module. - */ -val platformSecureRandom: () -> SecureRandom = when { - SgxSupport.isInsideEnclave -> { - { DummySecureRandom } - } - else -> { - { sharedSecureRandom } - } -} - class PlatformSecureRandomService(provider: Provider) - : Provider.Service(provider, "SecureRandom", ALGORITHM, PlatformSecureRandomSpi::javaClass.name, null, null) { + : Provider.Service(provider, "SecureRandom", ALGORITHM, PlatformSecureRandomSpi::class.java.name, null, null) { companion object { const val ALGORITHM = "CordaPRNG" @@ -88,8 +73,3 @@ private class LinuxSecureRandomSpi : SecureRandomSpi() { 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) -} diff --git a/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt b/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt index df19ab17b3..27bfcafb96 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt @@ -1,24 +1,17 @@ package net.corda.core.crypto.internal import net.corda.core.crypto.CordaSecurityProvider -import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512 -import net.corda.core.crypto.Crypto.decodePrivateKey -import net.corda.core.crypto.Crypto.decodePublicKey -import net.corda.core.internal.X509EdDSAEngine -import net.i2p.crypto.eddsa.EdDSAEngine -import net.i2p.crypto.eddsa.EdDSASecurityProvider -import org.bouncycastle.asn1.ASN1ObjectIdentifier -import org.bouncycastle.asn1.pkcs.PrivateKeyInfo -import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo -import org.bouncycastle.jcajce.provider.asymmetric.ec.AlgorithmParametersSpi -import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider import java.security.Provider -import java.security.SecureRandom import java.security.Security import java.util.Collections.unmodifiableMap +val sunEcProvider = checkNotNull(Security.getProvider("SunEC")).also { + // Insert Secp256k1SupportProvider just in-front of SunEC for adding back support for secp256k1 + Security.insertProviderAt(Secp256k1SupportProvider(), Security.getProviders().indexOf(it)) +} + 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). @@ -29,40 +22,8 @@ val cordaSecurityProvider = CordaSecurityProvider().also { Security.insertProviderAt(it, 1) // The position is 1-based. } -// OID taken from https://tools.ietf.org/html/draft-ietf-curdle-pkix-00 -val `id-Curve25519ph` = ASN1ObjectIdentifier("1.3.101.112") -val cordaBouncyCastleProvider = BouncyCastleProvider().apply { - putAll(EdDSASecurityProvider()) - // Override the normal EdDSA engine with one which can handle X509 keys. - put("Signature.${EdDSAEngine.SIGNATURE_ALGORITHM}", X509EdDSAEngine::class.java.name) - put("Signature.Ed25519", X509EdDSAEngine::class.java.name) - addKeyInfoConverter(`id-Curve25519ph`, object : AsymmetricKeyInfoConverter { - override fun generatePublic(keyInfo: SubjectPublicKeyInfo) = decodePublicKey(EDDSA_ED25519_SHA512, keyInfo.encoded) - override fun generatePrivate(keyInfo: PrivateKeyInfo) = decodePrivateKey(EDDSA_ED25519_SHA512, keyInfo.encoded) - }) - // Required due to [X509CRL].verify() reported issues in network-services after BC 1.60 update. - put("AlgorithmParameters.SHA256WITHECDSA", AlgorithmParametersSpi::class.java.name) -}.also { - // This registration is needed for reading back EdDSA key from java keystore. - // 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. +val cordaBouncyCastleProvider = BouncyCastleProvider().also { Security.addProvider(it) - - // Remove providers that class with bouncy castle - val bcProviders = it.keys - - // JDK 17: Add SunEC provider as lowest priority, as we use Bouncycastle for EDDSA - // and remove amy algorithms that conflict with Bouncycastle - val sunEC = Security.getProvider("SunEC") - if (sunEC != null) { - Security.removeProvider("SunEC") - Security.addProvider(sunEC) - - for(alg in sunEC.keys) { - if (bcProviders.contains(alg)) { - sunEC.remove(alg) - } - } - } } val bouncyCastlePQCProvider = BouncyCastlePQCProvider().apply { @@ -75,8 +36,6 @@ val bouncyCastlePQCProvider = BouncyCastlePQCProvider().apply { // i.e. if someone removes a Provider and then he/she adds a new one with the same name. // The val is immutable to avoid any harmful state changes. internal val providerMap: Map = unmodifiableMap( - listOf(cordaBouncyCastleProvider, cordaSecurityProvider, bouncyCastlePQCProvider) + listOf(sunEcProvider, cordaBouncyCastleProvider, cordaSecurityProvider, bouncyCastlePQCProvider) .associateByTo(LinkedHashMap(), Provider::getName) ) - -fun platformSecureRandomFactory(): SecureRandom = platformSecureRandom() // To minimise diff of CryptoUtils against open-source. diff --git a/core/src/main/kotlin/net/corda/core/crypto/internal/Secp256k1SupportProvider.kt b/core/src/main/kotlin/net/corda/core/crypto/internal/Secp256k1SupportProvider.kt new file mode 100644 index 0000000000..5f7f669b1b --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/crypto/internal/Secp256k1SupportProvider.kt @@ -0,0 +1,188 @@ +@file:Suppress("MagicNumber") + +package net.corda.core.crypto.internal + +import org.bouncycastle.jce.provider.BouncyCastleProvider +import java.math.BigInteger +import java.math.BigInteger.ZERO +import java.security.AlgorithmParameters +import java.security.KeyPair +import java.security.KeyPairGeneratorSpi +import java.security.PrivateKey +import java.security.Provider +import java.security.PublicKey +import java.security.SecureRandom +import java.security.Signature +import java.security.SignatureSpi +import java.security.interfaces.ECPrivateKey +import java.security.interfaces.ECPublicKey +import java.security.spec.AlgorithmParameterSpec +import java.security.spec.ECFieldFp +import java.security.spec.ECParameterSpec +import java.security.spec.ECPoint +import java.security.spec.EllipticCurve +import java.security.spec.NamedParameterSpec + +/** + * Augment the SunEC provider with secp256k1 curve support by delegating to [BouncyCastleProvider] when secp256k1 keys or params are + * requested. Otherwise delegates to SunEC. + */ +class Secp256k1SupportProvider : Provider("Secp256k1Support", "1.0", "Augmenting SunEC with support for the secp256k1 curve via BC") { + init { + put("Signature.SHA256withECDSA", Secp256k1SupportSignatureSpi::class.java.name) + put("KeyPairGenerator.EC", Secp256k1SupportKeyPairGeneratorSpi::class.java.name) + put("AlgorithmParameters.EC", "sun.security.util.ECParameters") + put("KeyFactory.EC", "sun.security.ec.ECKeyFactory") + } + + class Secp256k1SupportSignatureSpi : SignatureSpi() { + private lateinit var sunEc: Signature + private lateinit var bc: Signature + private lateinit var selected: Signature + + override fun engineInitVerify(publicKey: PublicKey?) { + selectProvider((publicKey as? ECPublicKey)?.params) + selected.initVerify(publicKey) + } + + override fun engineInitSign(privateKey: PrivateKey?) { + selectProvider((privateKey as? ECPrivateKey)?.params) + selected.initSign(privateKey) + } + + override fun engineSetParameter(params: AlgorithmParameterSpec?) { + selectProvider(params) + // The BC implementation throws UnsupportedOperationException, so we just avoid calling it. + if (selected !== bc) { + selected.setParameter(params) + } + } + + private fun selectProvider(params: AlgorithmParameterSpec?) { + if (params.isSecp256k1) { + if (!::bc.isInitialized) { + bc = Signature.getInstance("SHA256withECDSA", cordaBouncyCastleProvider) + } + selected = bc + } else { + selectSunEc() + } + } + + private fun selectSunEc() { + if (!::sunEc.isInitialized) { + sunEc = Signature.getInstance("SHA256withECDSA", sunEcProvider) + } + selected = sunEc + } + + override fun engineUpdate(b: Byte) { + defaultToSunEc() + selected.update(b) + } + + override fun engineUpdate(b: ByteArray?, off: Int, len: Int) { + defaultToSunEc() + selected.update(b, off, len) + } + + override fun engineSign(): ByteArray { + defaultToSunEc() + return selected.sign() + } + + override fun engineVerify(sigBytes: ByteArray?): Boolean { + defaultToSunEc() + return selected.verify(sigBytes) + } + + override fun engineGetParameters(): AlgorithmParameters { + defaultToSunEc() + return selected.parameters + } + + @Deprecated("Deprecated in Java") + @Suppress("DEPRECATION") + override fun engineSetParameter(param: String?, value: Any?) { + defaultToSunEc() + selected.setParameter(param, value) + } + + @Deprecated("Deprecated in Java") + @Suppress("DEPRECATION") + override fun engineGetParameter(param: String?): Any { + defaultToSunEc() + return selected.getParameter(param) + } + + private fun defaultToSunEc() { + // Even though it's probably a bug to start using the Signature object without first calling one of the intialize methods, + // default it to SunEC provider anyway and let it deal with the issue. + if (!::selected.isInitialized) { + selectSunEc() + } + } + } + + class Secp256k1SupportKeyPairGeneratorSpi : KeyPairGeneratorSpi() { + // The methods in KeyPairGeneratorSpi are public, which allows us to directly call them. This is not the case with SignatureSpi (above). + private lateinit var sunEc: KeyPairGeneratorSpi + private lateinit var bc: KeyPairGeneratorSpi + private lateinit var selected: KeyPairGeneratorSpi + + override fun initialize(keysize: Int, random: SecureRandom?) { + selectSunEc() + selected.initialize(keysize, random) + } + + override fun initialize(params: AlgorithmParameterSpec?, random: SecureRandom?) { + if (params.isSecp256k1) { + if (!::bc.isInitialized) { + bc = org.bouncycastle.jcajce.provider.asymmetric.ec.KeyPairGeneratorSpi.EC() + } + selected = bc + } else { + selectSunEc() + } + selected.initialize(params, random) + } + + private fun selectSunEc() { + if (!::sunEc.isInitialized) { + sunEc = sunEcProvider.getService("KeyPairGenerator", "EC").newInstance(null) as KeyPairGeneratorSpi + } + selected = sunEc + } + + override fun generateKeyPair(): KeyPair { + if (!::selected.isInitialized) { + // In-case initialize wasn't first called, default to SunEC + selectSunEc() + } + return selected.generateKeyPair() + } + } +} + +/** + * Parameters for the secp256k1 curve + */ +private object Secp256k1 { + val n = BigInteger("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16) + val g = ECPoint( + BigInteger("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", 16), + BigInteger("483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", 16) + ) + val curve = EllipticCurve( + ECFieldFp(BigInteger("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", 16)), + ZERO, + 7.toBigInteger() + ) +} + +val AlgorithmParameterSpec?.isSecp256k1: Boolean + get() = when (this) { + is ECParameterSpec -> cofactor == 1 && order == Secp256k1.n && curve == Secp256k1.curve && generator == Secp256k1.g + is NamedParameterSpec -> name.equals("secp256k1", ignoreCase = true) + else -> false + } diff --git a/core/src/main/kotlin/net/corda/core/internal/StatePointerSearch.kt b/core/src/main/kotlin/net/corda/core/internal/StatePointerSearch.kt index e552172844..b17f50e0c8 100644 --- a/core/src/main/kotlin/net/corda/core/internal/StatePointerSearch.kt +++ b/core/src/main/kotlin/net/corda/core/internal/StatePointerSearch.kt @@ -13,7 +13,7 @@ import java.util.* class StatePointerSearch(val state: ContractState) { private companion object { // Classes in these packages should not be part of a search. - private val blackListedPackages = setOf("java.", "javax.", "org.bouncycastle.", "net.i2p.crypto.") + private val blackListedPackages = setOf("java.", "javax.", "org.bouncycastle.") } // Type required for traversal. diff --git a/core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt b/core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt deleted file mode 100644 index 94c4897da8..0000000000 --- a/core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt +++ /dev/null @@ -1,59 +0,0 @@ -package net.corda.core.internal - -import net.corda.core.crypto.Crypto -import net.i2p.crypto.eddsa.EdDSAEngine -import net.i2p.crypto.eddsa.EdDSAPublicKey -import java.security.AlgorithmParameters -import java.security.InvalidKeyException -import java.security.MessageDigest -import java.security.PrivateKey -import java.security.PublicKey -import java.security.SecureRandom -import java.security.Signature -import java.security.spec.AlgorithmParameterSpec -import java.security.spec.X509EncodedKeySpec - -/** - * Wrapper around [EdDSAEngine] which can intelligently rewrite X509Keys to a [EdDSAPublicKey]. This is a temporary - * solution until this is integrated upstream and/or a custom certificate factory implemented to force the correct - * key type. Only intercepts public keys passed into [engineInitVerify], as there is no equivalent issue with private - * keys. - */ -class X509EdDSAEngine : Signature { - private val engine: EdDSAEngine - - constructor() : super(EdDSAEngine.SIGNATURE_ALGORITHM) { - engine = EdDSAEngine() - } - - constructor(digest: MessageDigest) : super(EdDSAEngine.SIGNATURE_ALGORITHM) { - engine = EdDSAEngine(digest) - } - - override fun engineInitSign(privateKey: PrivateKey) = engine.initSign(privateKey) - - override fun engineInitSign(privateKey: PrivateKey, random: SecureRandom) = engine.initSign(privateKey, random) - - override fun engineInitVerify(publicKey: PublicKey) { - val parsedKey = try { - publicKey as? EdDSAPublicKey ?: EdDSAPublicKey(X509EncodedKeySpec(Crypto.encodePublicKey(publicKey))) - } catch (e: Exception) { - throw (InvalidKeyException(e.message)) - } - engine.initVerify(parsedKey) - } - - override fun engineSign(): ByteArray = engine.sign() - override fun engineVerify(sigBytes: ByteArray): Boolean = engine.verify(sigBytes) - - override fun engineUpdate(b: Byte) = engine.update(b) - override fun engineUpdate(b: ByteArray, off: Int, len: Int) = engine.update(b, off, len) - - override fun engineGetParameters(): AlgorithmParameters = engine.parameters - override fun engineSetParameter(params: AlgorithmParameterSpec) = engine.setParameter(params) - @Suppress("DEPRECATION", "OverridingDeprecatedMember") - override fun engineGetParameter(param: String): Any = engine.getParameter(param) - - @Suppress("DEPRECATION", "OverridingDeprecatedMember") - override fun engineSetParameter(param: String, value: Any?) = engine.setParameter(param, value) -} diff --git a/core/src/main/kotlin/net/corda/core/utilities/SgxSupport.kt b/core/src/main/kotlin/net/corda/core/utilities/SgxSupport.kt deleted file mode 100644 index b4691cb1e0..0000000000 --- a/core/src/main/kotlin/net/corda/core/utilities/SgxSupport.kt +++ /dev/null @@ -1,8 +0,0 @@ -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)") - } -} diff --git a/core/src/test/java/net/corda/core/internal/X509EdDSAEngineTest.java b/core/src/test/java/net/corda/core/internal/X509EdDSAEngineTest.java deleted file mode 100644 index e353db8043..0000000000 --- a/core/src/test/java/net/corda/core/internal/X509EdDSAEngineTest.java +++ /dev/null @@ -1,135 +0,0 @@ -package net.corda.core.internal; - -import net.corda.core.crypto.Crypto; -import net.i2p.crypto.eddsa.EdDSAEngine; -import net.i2p.crypto.eddsa.EdDSAPublicKey; -import org.junit.Test; -import sun.security.util.BitArray; -import sun.security.util.ObjectIdentifier; -import sun.security.x509.AlgorithmId; -import sun.security.x509.X509Key; - -import java.io.IOException; -import java.math.BigInteger; -import java.security.InvalidKeyException; -import java.security.KeyPair; -import java.security.SignatureException; -import java.util.Random; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.junit.Assert.assertTrue; - -/** - * JDK11 upgrade: rewritten in Java to gain access to private internal JDK classes via module directives (not available to Kotlin compiler): - * import sun.security.util.BitArray; - * import sun.security.util.ObjectIdentifier; - * import sun.security.x509.AlgorithmId; - * import sun.security.x509.X509Key; - */ -public class X509EdDSAEngineTest { - private static final long SEED = 20170920L; - private static final int TEST_DATA_SIZE = 2000; - - // offset into an EdDSA header indicating where the key header and actual key start - // in the underlying byte array - private static final int KEY_HEADER_START = 9; - private static final int KEY_START = 12; - - private X509Key toX509Key(EdDSAPublicKey publicKey) throws IOException, InvalidKeyException { - byte[] internals = publicKey.getEncoded(); - - // key size in the header includes the count unused bits at the end of the key - // [keyHeaderStart + 2] but NOT the key header ID [keyHeaderStart] so the - // actual length of the key blob is size - 1 - int keySize = (internals[KEY_HEADER_START + 1]) - 1; - - byte[] key = new byte[keySize]; - System.arraycopy(internals, KEY_START, key, 0, keySize); - - // 1.3.101.102 is the EdDSA OID - return new TestX509Key(new AlgorithmId(ObjectIdentifier.of("1.3.101.112")), new BitArray(keySize * 8, key)); - } - - private static class TestX509Key extends X509Key { - TestX509Key(AlgorithmId algorithmId, BitArray key) throws InvalidKeyException { - this.algid = algorithmId; - this.setKey(key); - this.encode(); - } - } - - /** - * Put the X509EdDSA engine through basic tests to verify that the functions are hooked up correctly. - */ - @Test - public void SignAndVerify() throws InvalidKeyException, SignatureException { - X509EdDSAEngine engine = new X509EdDSAEngine(); - KeyPair keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger.valueOf(SEED)); - EdDSAPublicKey publicKey = (EdDSAPublicKey) keyPair.getPublic(); - byte[] randomBytes = new byte[TEST_DATA_SIZE]; - new Random(SEED).nextBytes(randomBytes); - engine.initSign(keyPair.getPrivate()); - engine.update(randomBytes[0]); - engine.update(randomBytes, 1, randomBytes.length - 1); - - // Now verify the signature - byte[] signature = engine.sign(); - - engine.initVerify(publicKey); - engine.update(randomBytes); - assertTrue(engine.verify(signature)); - } - - /** - * Verify that signing with an X509Key wrapped EdDSA key works. - */ - @Test - public void SignAndVerifyWithX509Key() throws InvalidKeyException, SignatureException, IOException { - X509EdDSAEngine engine = new X509EdDSAEngine(); - KeyPair keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger.valueOf(SEED + 1)); - X509Key publicKey = toX509Key((EdDSAPublicKey) keyPair.getPublic()); - byte[] randomBytes = new byte[TEST_DATA_SIZE]; - new Random(SEED + 1).nextBytes(randomBytes); - engine.initSign(keyPair.getPrivate()); - engine.update(randomBytes[0]); - engine.update(randomBytes, 1, randomBytes.length - 1); - - // Now verify the signature - byte[] signature = engine.sign(); - - engine.initVerify(publicKey); - engine.update(randomBytes); - assertTrue(engine.verify(signature)); - } - - /** - * Verify that signing with an X509Key wrapped EdDSA key succeeds when using the underlying EdDSAEngine. - */ - @Test - public void SignAndVerifyWithX509KeyAndOldEngineFails() throws InvalidKeyException, SignatureException, IOException { - X509EdDSAEngine engine = new X509EdDSAEngine(); - KeyPair keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger.valueOf(SEED + 1)); - X509Key publicKey = toX509Key((EdDSAPublicKey) keyPair.getPublic()); - byte[] randomBytes = new byte[TEST_DATA_SIZE]; - new Random(SEED + 1).nextBytes(randomBytes); - engine.initSign(keyPair.getPrivate()); - engine.update(randomBytes[0]); - engine.update(randomBytes, 1, randomBytes.length - 1); - - // Now verify the signature - byte[] signature = engine.sign(); - engine.initVerify(publicKey); - engine.update(randomBytes); - engine.verify(signature); - } - - /** Verify will fail if the input public key cannot be converted to EdDSA public key. */ - @Test - public void verifyWithNonSupportedKeyTypeFails() { - EdDSAEngine engine = new EdDSAEngine(); - KeyPair keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger.valueOf(SEED)); - assertThatExceptionOfType(InvalidKeyException.class).isThrownBy(() -> - engine.initVerify(keyPair.getPublic()) - ); - } -} diff --git a/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt b/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt index b011d029c6..d5c125b7bb 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt @@ -8,15 +8,10 @@ import net.corda.core.crypto.Crypto.RSA_SHA256 import net.corda.core.crypto.Crypto.SPHINCS256_SHA256 import net.corda.core.crypto.internal.PlatformSecureRandomService import net.corda.core.utilities.OpaqueBytes -import net.i2p.crypto.eddsa.EdDSAKey -import net.i2p.crypto.eddsa.EdDSAPrivateKey -import net.i2p.crypto.eddsa.EdDSAPublicKey -import net.i2p.crypto.eddsa.math.GroupElement -import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec -import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable -import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec import org.apache.commons.lang3.ArrayUtils.EMPTY_BYTE_ARRAY +import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatIllegalArgumentException +import org.assertj.core.api.Assertions.assertThatThrownBy import org.bouncycastle.asn1.pkcs.PrivateKeyInfo import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey @@ -24,15 +19,18 @@ import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey import org.bouncycastle.jce.ECNamedCurveTable import org.bouncycastle.jce.interfaces.ECKey import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec +import org.bouncycastle.math.ec.rfc8032.Ed25519 import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey import org.junit.Assert.assertNotEquals -import org.junit.Ignore import org.junit.Test import java.math.BigInteger import java.security.KeyPairGenerator import java.security.SecureRandom import java.security.Security +import java.security.interfaces.EdECPrivateKey +import java.security.interfaces.EdECPublicKey +import java.security.spec.NamedParameterSpec import java.util.Random import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -132,11 +130,8 @@ class CryptoUtilsTest { // test on malformed signatures (even if they change for 1 bit) signedData[0] = signedData[0].inc() - try { + assertThatThrownBy { Crypto.doVerify(pubKey, signedData, testBytes) - fail() - } catch (e: Exception) { - // expected } } @@ -498,9 +493,9 @@ class CryptoUtilsTest { val (privEd, pubEd) = keyPairEd assertEquals(privEd.algorithm, "EdDSA") - assertEquals((privEd as EdDSAKey).params, EdDSANamedCurveTable.getByName("ED25519")) + assertEquals((privEd as EdECPrivateKey).params.name, NamedParameterSpec.ED25519.name) assertEquals(pubEd.algorithm, "EdDSA") - assertEquals((pubEd as EdDSAKey).params, EdDSANamedCurveTable.getByName("ED25519")) + assertEquals((pubEd as EdECPublicKey).params.name, NamedParameterSpec.ED25519.name) } @Test(timeout=300_000) @@ -659,18 +654,23 @@ class CryptoUtilsTest { @Test(timeout=300_000) fun `Check EdDSA public key on curve`() { - val keyPairEdDSA = Crypto.generateKeyPair(EDDSA_ED25519_SHA512) - val pubEdDSA = keyPairEdDSA.public - assertTrue(Crypto.publicKeyOnCurve(EDDSA_ED25519_SHA512, pubEdDSA)) - // Use R1 curve for check. - assertFalse(Crypto.publicKeyOnCurve(ECDSA_SECP256R1_SHA256, pubEdDSA)) - // Check for point at infinity. - val pubKeySpec = EdDSAPublicKeySpec((EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec).curve.getZero(GroupElement.Representation.P3), EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec) - assertFalse(Crypto.publicKeyOnCurve(EDDSA_ED25519_SHA512, EdDSAPublicKey(pubKeySpec))) + repeat(100) { + val keyPairEdDSA = Crypto.generateKeyPair(EDDSA_ED25519_SHA512) + val pubEdDSA = keyPairEdDSA.public + assertTrue(Crypto.publicKeyOnCurve(EDDSA_ED25519_SHA512, pubEdDSA)) + // Use R1 curve for check. + assertFalse(Crypto.publicKeyOnCurve(ECDSA_SECP256R1_SHA256, pubEdDSA)) + } + val invalidKey = run { + val bytes = ByteArray(Ed25519.PUBLIC_KEY_SIZE).also { it[0] = 2 } + val encoded = SubjectPublicKeyInfo(EDDSA_ED25519_SHA512.signatureOID, bytes).encoded + Crypto.decodePublicKey(encoded) + } + assertThat(invalidKey).isInstanceOf(EdECPublicKey::class.java) + assertThat(Crypto.publicKeyOnCurve(EDDSA_ED25519_SHA512, invalidKey)).isFalse() } @Test(timeout = 300_000) - @Ignore("TODO JDK17: Fixme") fun `Unsupported EC public key type on curve`() { val keyGen = KeyPairGenerator.getInstance("EC") // sun.security.ec.ECPublicKeyImpl keyGen.initialize(256, newSecureRandom()) @@ -772,10 +772,8 @@ class CryptoUtilsTest { // Check scheme. assertEquals(priv.algorithm, dpriv.algorithm) assertEquals(pub.algorithm, dpub.algorithm) - assertTrue(dpriv is EdDSAPrivateKey) - assertTrue(dpub is EdDSAPublicKey) - assertEquals((dpriv as EdDSAKey).params, EdDSANamedCurveTable.getByName("ED25519")) - assertEquals((dpub as EdDSAKey).params, EdDSANamedCurveTable.getByName("ED25519")) + assertEquals((dpriv as EdECPrivateKey).params.name, NamedParameterSpec.ED25519.name) + assertEquals((dpub as EdECPublicKey).params.name, NamedParameterSpec.ED25519.name) assertEquals(Crypto.findSignatureScheme(dpriv), EDDSA_ED25519_SHA512) assertEquals(Crypto.findSignatureScheme(dpub), EDDSA_ED25519_SHA512) diff --git a/core/src/test/kotlin/net/corda/core/crypto/EdDSATests.kt b/core/src/test/kotlin/net/corda/core/crypto/EdDSATests.kt index 6a30e5e2d6..666d2b7ce0 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/EdDSATests.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/EdDSATests.kt @@ -1,17 +1,16 @@ package net.corda.core.crypto +import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512 +import net.corda.core.crypto.internal.Instances.withSignature import net.corda.core.utilities.hexToByteArray import net.corda.core.utilities.toHex -import net.i2p.crypto.eddsa.EdDSAPrivateKey -import net.i2p.crypto.eddsa.EdDSASecurityProvider -import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec -import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec +import org.assertj.core.api.Assertions.assertThat +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo import org.junit.Test import java.security.PrivateKey -import java.security.Signature -import java.util.Locale -import kotlin.test.assertEquals -import kotlin.test.assertNotEquals +import java.security.spec.EdECPrivateKeySpec +import java.security.spec.NamedParameterSpec +import java.security.spec.X509EncodedKeySpec /** * Testing PureEdDSA Ed25519 using test vectors from https://tools.ietf.org/html/rfc8032#section-7.1 @@ -19,8 +18,6 @@ import kotlin.test.assertNotEquals class EdDSATests { @Test(timeout=300_000) fun `PureEdDSA Ed25519 test vectors`() { - val edParams = Crypto.EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec - // MESSAGE (length 0 bytes). val testVector1 = SignatureTestVector( "9d61b19deffd5a60ba844af492ec2cc4" + @@ -152,11 +149,24 @@ class EdDSATests { "3dca179c138ac17ad9bef1177331a704" ) + val keyFactory = EDDSA_ED25519_SHA512.keyFactory + val testVectors = listOf(testVector1, testVector2, testVector3, testVector1024, testVectorSHAabc) - testVectors.forEach { - val privateKey = EdDSAPrivateKey(EdDSAPrivateKeySpec(it.privateKeyHex.hexToByteArray(), edParams)) - assertEquals(it.signatureOutputHex, doSign(privateKey, it.messageToSignHex.hexToByteArray()).toHex() - .lowercase(Locale.getDefault())) + testVectors.forEach { testVector -> + val messageBytes = testVector.messageToSignHex.hexToByteArray() + val signatureBytes = testVector.signatureOutputHex.hexToByteArray() + // Check the private key produces the expected signature + val privateKey = keyFactory.generatePrivate(EdECPrivateKeySpec(NamedParameterSpec.ED25519, testVector.privateKeyHex.hexToByteArray())) + assertThat(doSign(privateKey, messageBytes)).isEqualTo(signatureBytes) + // Check the public key verifies the signature + val result = withSignature(EDDSA_ED25519_SHA512) { signature -> + val publicKeyInfo = SubjectPublicKeyInfo(EDDSA_ED25519_SHA512.signatureOID, testVector.publicKeyHex.hexToByteArray()) + val publicKey = keyFactory.generatePublic(X509EncodedKeySpec(publicKeyInfo.encoded)) + signature.initVerify(publicKey) + signature.update(messageBytes) + signature.verify(signatureBytes) + } + assertThat(result).isTrue() } // Test vector for the variant Ed25519ctx, expected to fail. @@ -172,9 +182,8 @@ class EdDSATests { "5a5ca2df6668346291c2043d4eb3e90d" ) - val privateKey = EdDSAPrivateKey(EdDSAPrivateKeySpec(testVectorEd25519ctx.privateKeyHex.hexToByteArray(), edParams)) - assertNotEquals(testVectorEd25519ctx.signatureOutputHex, doSign(privateKey, testVectorEd25519ctx.messageToSignHex.hexToByteArray()).toHex() - .lowercase(Locale.getDefault())) + val privateKey = keyFactory.generatePrivate(EdECPrivateKeySpec(NamedParameterSpec.ED25519, testVectorEd25519ctx.privateKeyHex.hexToByteArray())) + assertThat(doSign(privateKey, testVectorEd25519ctx.messageToSignHex.hexToByteArray()).toHex().lowercase()).isNotEqualTo(testVectorEd25519ctx.signatureOutputHex) } /** A test vector object for digital signature schemes. */ @@ -185,9 +194,10 @@ class EdDSATests { // Required to implement a custom doSign function, because Corda's Crypto.doSign does not allow empty messages (testVector1). private fun doSign(privateKey: PrivateKey, clearData: ByteArray): ByteArray { - val signature = Signature.getInstance(Crypto.EDDSA_ED25519_SHA512.signatureName, EdDSASecurityProvider()) - signature.initSign(privateKey) - signature.update(clearData) - return signature.sign() + return withSignature(EDDSA_ED25519_SHA512) { signature -> + signature.initSign(privateKey) + signature.update(clearData) + signature.sign() + } } } diff --git a/detekt-baseline.xml b/detekt-baseline.xml index 01a15a1059..d3fdd92468 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -1426,7 +1426,6 @@ TooManyFunctions:ActionExecutorImpl.kt$ActionExecutorImpl : ActionExecutor TooManyFunctions:AppendOnlyPersistentMap.kt$AppendOnlyPersistentMapBase<K, V, E, out EK> TooManyFunctions:ArtemisTcpTransport.kt$ArtemisTcpTransport$Companion - TooManyFunctions:BCCryptoService.kt$BCCryptoService : CryptoService TooManyFunctions:BFTSmart.kt$BFTSmart$Replica : DefaultRecoverable TooManyFunctions:BaseTransaction.kt$BaseTransaction : NamedByHash TooManyFunctions:ClassCarpenter.kt$ClassCarpenterImpl : ClassCarpenter @@ -1669,9 +1668,6 @@ WildcardImport:AttachmentsClassLoader.kt$import net.corda.core.serialization.* WildcardImport:AttachmentsClassLoaderStaticContractTests.kt$import net.corda.core.contracts.* WildcardImport:AutoOfferFlow.kt$import net.corda.core.flows.* - WildcardImport:BCCryptoService.kt$import java.security.* - WildcardImport:BCCryptoService.kt$import net.corda.nodeapi.internal.cryptoservice.* - WildcardImport:BCCryptoServiceTests.kt$import java.security.* WildcardImport:BFTNotaryServiceTests.kt$import net.corda.core.crypto.* WildcardImport:BFTNotaryServiceTests.kt$import net.corda.testing.node.internal.* WildcardImport:BFTSmart.kt$import net.corda.core.crypto.* diff --git a/node-api-tests/build.gradle b/node-api-tests/build.gradle index a13fc4d9d6..3ac3d4d775 100644 --- a/node-api-tests/build.gradle +++ b/node-api-tests/build.gradle @@ -15,7 +15,6 @@ dependencies { testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}" testImplementation "junit:junit:$junit_version" testImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version" - testImplementation "net.i2p.crypto:eddsa:$eddsa_version" testImplementation "com.typesafe:config:$typesafe_config_version" testImplementation "io.dropwizard.metrics:metrics-core:$metrics_version" testImplementation "co.paralleluniverse:quasar-core:$quasar_version" diff --git a/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt index 356be174eb..b8d84467cd 100644 --- a/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt +++ b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt @@ -1,6 +1,5 @@ package net.corda.nodeapitests.internal.crypto - import io.netty.handler.ssl.ClientAuth import io.netty.handler.ssl.SslContextBuilder import io.netty.handler.ssl.SslProvider @@ -52,7 +51,6 @@ import net.corda.testing.core.BOB_NAME import net.corda.testing.core.TestIdentity import net.corda.testing.driver.internal.incrementalPortAllocation import net.corda.testing.internal.createDevIntermediateCaCertPath -import net.i2p.crypto.eddsa.EdDSAPrivateKey import org.assertj.core.api.Assertions.assertThat import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier import org.bouncycastle.asn1.x509.BasicConstraints @@ -60,9 +58,7 @@ import org.bouncycastle.asn1.x509.CRLDistPoint import org.bouncycastle.asn1.x509.Extension import org.bouncycastle.asn1.x509.KeyUsage import org.bouncycastle.asn1.x509.SubjectKeyIdentifier -import org.bouncycastle.jcajce.provider.asymmetric.edec.BCEdDSAPrivateKey import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder @@ -77,7 +73,8 @@ import java.security.KeyPair import java.security.PrivateKey import java.security.cert.CertPath import java.security.cert.X509Certificate -import java.util.* +import java.security.interfaces.EdECPrivateKey +import java.util.Date import javax.net.ssl.SSLContext import javax.net.ssl.SSLParameters import javax.net.ssl.SSLServerSocket @@ -93,7 +90,6 @@ import kotlin.test.assertNull import kotlin.test.assertTrue import kotlin.test.fail -@Ignore("TODO JDK17: Fixme") class X509UtilitiesTest { private companion object { val ALICE = TestIdentity(ALICE_NAME, 70).party @@ -122,9 +118,9 @@ class X509UtilitiesTest { val schemeToKeyTypes = listOf( // By default, JKS returns SUN EC key. - Triple(ECDSA_SECP256R1_SHA256,java.security.interfaces.ECPrivateKey::class.java, org.bouncycastle.jce.interfaces.ECPrivateKey::class.java), - Triple(ECDSA_SECP256K1_SHA256,java.security.interfaces.ECPrivateKey::class.java, org.bouncycastle.jce.interfaces.ECPrivateKey::class.java), - Triple(EDDSA_ED25519_SHA512, EdDSAPrivateKey::class.java, EdDSAPrivateKey::class.java), + Triple(ECDSA_SECP256R1_SHA256, java.security.interfaces.ECPrivateKey::class.java, org.bouncycastle.jce.interfaces.ECPrivateKey::class.java), + Triple(ECDSA_SECP256K1_SHA256, java.security.interfaces.ECPrivateKey::class.java, org.bouncycastle.jce.interfaces.ECPrivateKey::class.java), + Triple(EDDSA_ED25519_SHA512, EdECPrivateKey::class.java, EdECPrivateKey::class.java), // By default, JKS returns SUN RSA key. Triple(SPHINCS256_SHA256, BCSphincs256PrivateKey::class.java, BCSphincs256PrivateKey::class.java) ) @@ -136,8 +132,7 @@ class X509UtilitiesTest { @Test(timeout=300_000) fun `create valid self-signed CA certificate`() { - Crypto.supportedSignatureSchemes().filter { it != COMPOSITE_KEY - && ( it != SPHINCS256_SHA256)}.forEach { validSelfSignedCertificate(it) } + Crypto.supportedSignatureSchemes().filter { it != COMPOSITE_KEY }.forEach { validSelfSignedCertificate(it) } } private fun validSelfSignedCertificate(signatureScheme: SignatureScheme) { @@ -158,7 +153,7 @@ class X509UtilitiesTest { @Test(timeout=300_000) fun `load and save a PEM file certificate`() { - Crypto.supportedSignatureSchemes().filter { it != COMPOSITE_KEY }.forEach { loadSavePEMCert(it) } + Crypto.supportedSignatureSchemes().filter { it != COMPOSITE_KEY }.forEach(::loadSavePEMCert) } private fun loadSavePEMCert(signatureScheme: SignatureScheme) { @@ -172,8 +167,7 @@ class X509UtilitiesTest { @Test(timeout=300_000) fun `create valid server certificate chain`() { - certChainSchemeCombinations.filter{ it.first != SPHINCS256_SHA256 } - .forEach { createValidServerCertChain(it.first, it.second) } + certChainSchemeCombinations.forEach { createValidServerCertChain(it.first, it.second) } } private fun createValidServerCertChain(signatureSchemeRoot: SignatureScheme, signatureSchemeChild: SignatureScheme) { @@ -451,13 +445,11 @@ class X509UtilitiesTest { schemeToKeyTypes.forEach { getCorrectKeyFromKeystore(it.first, it.second, it.third) } } - private fun getCorrectKeyFromKeystore(signatureScheme: SignatureScheme, uncastedClass: Class, castedClass: Class) { + private fun getCorrectKeyFromKeystore(signatureScheme: SignatureScheme, rawClass: Class, supportedClass: Class) { val keyPair = generateKeyPair(signatureScheme) - val (keyFromKeystore, keyFromKeystoreCasted) = storeAndGetKeysFromKeystore(keyPair) - if (uncastedClass == EdDSAPrivateKey::class.java && keyFromKeystore !is BCEdDSAPrivateKey) { - assertThat(keyFromKeystore).isInstanceOf(uncastedClass) - } - assertThat(keyFromKeystoreCasted).isInstanceOf(castedClass) + val (rawKey, supportedKey) = storeAndGetKeysFromKeystore(keyPair) + assertThat(rawKey).isInstanceOf(rawClass) + assertThat(supportedKey).isInstanceOf(supportedClass) } private fun storeAndGetKeysFromKeystore(keyPair: KeyPair): Pair { @@ -466,9 +458,9 @@ class X509UtilitiesTest { val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword") keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert)) - val keyFromKeystore = keyStore.getKey("Key", "keypassword".toCharArray()) - val keyFromKeystoreCasted = keyStore.getSupportedKey("Key", "keypassword") - return Pair(keyFromKeystore, keyFromKeystoreCasted) + val rawKey = keyStore.getKey("Key", "keypassword".toCharArray()) + val supportedKey = keyStore.getSupportedKey("Key", "keypassword") + return Pair(rawKey, supportedKey) } @Test(timeout=300_000) diff --git a/node-api/build.gradle b/node-api/build.gradle index 8b7dbdea93..79e04a203c 100644 --- a/node-api/build.gradle +++ b/node-api/build.gradle @@ -57,7 +57,6 @@ dependencies { implementation "io.reactivex:rxjava:$rxjava_version" implementation "javax.persistence:javax.persistence-api:2.2" implementation "org.hibernate:hibernate-core:$hibernate_version" - implementation "net.i2p.crypto:eddsa:$eddsa_version" implementation "co.paralleluniverse:quasar-osgi-annotations:$quasar_version" runtimeOnly 'com.mattbertolini:liquibase-slf4j:2.0.0' diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStore.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStore.kt index 92980beae1..b50e9d8fa3 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStore.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStore.kt @@ -78,7 +78,7 @@ interface CertificateStore : Iterable> { } fun setCertPathOnly(alias: String, certificates: List) { - // In case CryptoService and CertificateStore share the same KeyStore (i.e., when BCCryptoService is used), + // In case CryptoService and CertificateStore share the same KeyStore (i.e., when DefaultCryptoService is used), // extract the existing key from the Keystore and store it again along with the new certificate chain. // This is because KeyStores do not support updateKeyEntry and thus we cannot update the certificate chain // without overriding the key entry. diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilder.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilder.kt index bbee9e5d2a..a84fb0ad08 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilder.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilder.kt @@ -1,6 +1,5 @@ package net.corda.nodeapi.internal.crypto -import net.corda.core.crypto.Crypto.SPHINCS256_SHA256 import net.corda.core.crypto.SignatureScheme import net.corda.core.crypto.internal.Instances import org.bouncycastle.asn1.x509.AlgorithmIdentifier @@ -27,9 +26,7 @@ object ContentSignerBuilder { val sig = try { signatureInstance.apply { - // TODO special handling for Sphincs due to a known BouncyCastle's Sphincs bug we reported. - // It is fixed in BC 161b12, so consider updating the below if-statement after updating BouncyCastle. - if (random != null && signatureScheme != SPHINCS256_SHA256) { + if (random != null) { initSign(privateKey, random) } else { initSign(privateKey) @@ -48,7 +45,7 @@ object ContentSignerBuilder { private class SignatureOutputStream(private val sig: Signature, private val optimised: Boolean) : OutputStream() { private var alreadySigned = false - internal val signature: ByteArray by lazy { + val signature: ByteArray by lazy { try { alreadySigned = true sig.sign() diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt index c4d062dfff..c9e17b9773 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt @@ -69,7 +69,7 @@ import kotlin.io.path.reader import kotlin.io.path.writer object X509Utilities { - // Note that this default value only applies to BCCryptoService. Other implementations of CryptoService may have to use different + // Note that this default value only applies to DefaultCryptoService. Other implementations of CryptoService may have to use different // schemes (for instance `UtimacoCryptoService.DEFAULT_IDENTITY_SIGNATURE_SCHEME`). val DEFAULT_IDENTITY_SIGNATURE_SCHEME = Crypto.EDDSA_ED25519_SHA512 val DEFAULT_TLS_SIGNATURE_SCHEME = Crypto.ECDSA_SECP256R1_SHA256 @@ -303,10 +303,10 @@ object X509Utilities { crlDistPoint: String? = null, crlIssuer: X500Name? = null): X509Certificate { val builder = createPartialCertificate(certificateType, issuer, issuerPublicKey, subject, subjectPublicKey, validityWindow, nameConstraints, crlDistPoint, crlIssuer) - return builder.build(issuerSigner).run { - require(isValidOn(Date())){"Certificate is not valid at instant now"} - toJca() - } + val certificate = builder.build(issuerSigner).toJca() + certificate.checkValidity(Date()) + certificate.verify(issuerPublicKey) + return certificate } /** @@ -340,18 +340,22 @@ object X509Utilities { validityWindow, nameConstraints, crlDistPoint, - crlIssuer) - return builder.build(signer).run { - require(isValidOn(Date())){"Certificate is not valid at instant now"} - require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public))){"Invalid signature"} - toJca() - } + crlIssuer + ) + val certificate = builder.build(signer).toJca() + certificate.checkValidity(Date()) + certificate.verify(issuerKeyPair.public) + return certificate } /** * Create certificate signing request using provided information. */ - fun createCertificateSigningRequest(subject: X500Principal, email: String, publicKey: PublicKey, contentSigner: ContentSigner, certRole: CertRole = CertRole.NODE_CA): PKCS10CertificationRequest { + fun createCertificateSigningRequest(subject: X500Principal, + email: String, + publicKey: PublicKey, + contentSigner: ContentSigner, + certRole: CertRole = CertRole.NODE_CA): PKCS10CertificationRequest { return JcaPKCS10CertificationRequestBuilder(subject, publicKey) .addAttribute(BCStyle.E, DERUTF8String(email)) .addAttribute(ASN1ObjectIdentifier(CordaOID.X509_EXTENSION_CORDA_ROLE), certRole) @@ -410,7 +414,7 @@ object X509Utilities { } // Assuming cert type to role is 1:1 -val CertRole.certificateType: CertificateType get() = CertificateType.values().first { it.role == this } +val CertRole.certificateType: CertificateType get() = CertificateType.entries.first { it.role == this } /** * Convert a [X509Certificate] into BouncyCastle's [X509CertificateHolder]. diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/cryptoservice/bouncycastle/BCCryptoService.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/cryptoservice/DefaultCryptoService.kt similarity index 87% rename from node-api/src/main/kotlin/net/corda/nodeapi/internal/cryptoservice/bouncycastle/BCCryptoService.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/cryptoservice/DefaultCryptoService.kt index d57f750b96..f48cbe0fb1 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/cryptoservice/bouncycastle/BCCryptoService.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/cryptoservice/DefaultCryptoService.kt @@ -1,9 +1,8 @@ -package net.corda.nodeapi.internal.cryptoservice.bouncycastle +package net.corda.nodeapi.internal.cryptoservice import net.corda.core.crypto.Crypto import net.corda.core.crypto.SignatureScheme -import net.corda.core.crypto.internal.Instances.getSignatureInstance -import net.corda.core.crypto.internal.cordaBouncyCastleProvider +import net.corda.core.crypto.internal.Instances.withSignature import net.corda.core.crypto.newSecureRandom import net.corda.core.crypto.sha256 import net.corda.core.utilities.detailedLogger @@ -14,27 +13,30 @@ import net.corda.nodeapi.internal.crypto.ContentSignerBuilder import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore import net.corda.nodeapi.internal.crypto.save -import net.corda.nodeapi.internal.cryptoservice.* -import net.corda.nodeapi.internal.cryptoservice.CryptoService -import net.corda.nodeapi.internal.cryptoservice.CryptoServiceException import org.bouncycastle.operator.ContentSigner import java.nio.file.Path -import java.security.* +import java.security.KeyPair +import java.security.KeyPairGenerator +import java.security.KeyStore +import java.security.PrivateKey +import java.security.Provider +import java.security.PublicKey +import java.security.Signature import java.security.spec.ECGenParameterSpec import javax.crypto.Cipher import javax.crypto.KeyGenerator import javax.security.auth.x500.X500Principal /** - * Basic implementation of a [CryptoService] that uses BouncyCastle for cryptographic operations + * Basic implementation of a [CryptoService] which uses Corda's [Provider]s for cryptographic operations * and a Java KeyStore in the form of [CertificateStore] to store private keys. - * This service reuses the [NodeConfiguration.signingCertificateStore] to store keys. * * The [wrappingKeyStorePath] must be provided in order to execute any wrapping operations (e.g. [createWrappingKey], [generateWrappedKeyPair]) */ -class BCCryptoService(private val legalName: X500Principal, - private val certificateStoreSupplier: CertificateStoreSupplier, - private val wrappingKeyStorePath: Path? = null) : CryptoService { +@Suppress("TooManyFunctions") +class DefaultCryptoService(private val legalName: X500Principal, + private val certificateStoreSupplier: CertificateStoreSupplier, + private val wrappingKeyStorePath: Path? = null) : CryptoService { private companion object { val detailedLogger = detailedLogger() @@ -97,7 +99,7 @@ class BCCryptoService(private val legalName: X500Principal, private fun signWithAlgorithm(alias: String, data: ByteArray, signAlgorithm: String): ByteArray { val privateKey = certificateStore.query { getPrivateKey(alias, certificateStore.entryPassword) } - val signature = Signature.getInstance(signAlgorithm, cordaBouncyCastleProvider) + val signature = Signature.getInstance(signAlgorithm) detailedLogger.trace { "CryptoService(action=signing_start;alias=$alias;algorithm=$signAlgorithm)" } signature.initSign(privateKey, newSecureRandom()) signature.update(data) @@ -126,7 +128,7 @@ class BCCryptoService(private val legalName: X500Principal, /** * If a node is running in [NodeConfiguration.devMode] and for backwards compatibility purposes, the same [KeyStore] - * is reused outside [BCCryptoService] to update certificate paths. [resyncKeystore] will sync [BCCryptoService]'s + * is reused outside [DefaultCryptoService] to update certificate paths. [resyncKeystore] will sync [DefaultCryptoService]'s * loaded [certificateStore] in memory with the contents of the corresponding [KeyStore] file. */ fun resyncKeystore() { @@ -178,7 +180,7 @@ class BCCryptoService(private val legalName: X500Principal, } val wrappingKey = wrappingKeyStore.getKey(masterKeyAlias, certificateStore.entryPassword.toCharArray()) - val cipher = Cipher.getInstance("AESWRAPPAD", cordaBouncyCastleProvider) + val cipher = Cipher.getInstance("AESWRAPPAD") cipher.init(Cipher.WRAP_MODE, wrappingKey) val keyPairGenerator = keyPairGeneratorFromScheme(childKeyScheme) @@ -199,22 +201,23 @@ class BCCryptoService(private val legalName: X500Principal, 1 -> "AESWRAPPAD" else -> "AES" } - val cipher = Cipher.getInstance(algorithm, cordaBouncyCastleProvider) + val cipher = Cipher.getInstance(algorithm) cipher.init(Cipher.UNWRAP_MODE, wrappingKey) val privateKey = cipher.unwrap(wrappedPrivateKey.keyMaterial, keyAlgorithmFromScheme(wrappedPrivateKey.signatureScheme), Cipher.PRIVATE_KEY) as PrivateKey - val signature = getSignatureInstance(wrappedPrivateKey.signatureScheme.signatureName, cordaBouncyCastleProvider) - signature.initSign(privateKey, newSecureRandom()) - signature.update(payloadToSign) - return signature.sign() + return withSignature(wrappedPrivateKey.signatureScheme) { signature -> + signature.initSign(privateKey, newSecureRandom()) + signature.update(payloadToSign) + signature.sign() + } } - override fun getWrappingMode(): WrappingMode? = WrappingMode.DEGRADED_WRAPPED + override fun getWrappingMode(): WrappingMode = WrappingMode.DEGRADED_WRAPPED private fun keyPairGeneratorFromScheme(scheme: SignatureScheme): KeyPairGenerator { val algorithm = keyAlgorithmFromScheme(scheme) - val keyPairGenerator = KeyPairGenerator.getInstance(algorithm, cordaBouncyCastleProvider) + val keyPairGenerator = KeyPairGenerator.getInstance(algorithm) when (scheme) { Crypto.ECDSA_SECP256R1_SHA256 -> keyPairGenerator.initialize(ECGenParameterSpec("secp256r1")) Crypto.ECDSA_SECP256K1_SHA256 -> keyPairGenerator.initialize(ECGenParameterSpec("secp256k1")) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/DefaultKryoCustomizer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/DefaultKryoCustomizer.kt index 3f80ba5200..76597a3f32 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/DefaultKryoCustomizer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/DefaultKryoCustomizer.kt @@ -20,7 +20,6 @@ import de.javakaffee.kryoserializers.guava.ImmutableSortedSetSerializer import net.corda.core.contracts.ContractAttachment import net.corda.core.contracts.ContractClassName import net.corda.core.contracts.PrivacySalt -import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.SecureHash import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.AbstractAttachment @@ -40,14 +39,6 @@ import net.corda.core.utilities.toNonEmptySet import net.corda.serialization.internal.DefaultWhitelist import net.corda.serialization.internal.GeneratedAttachment import net.corda.serialization.internal.MutableClassWhitelist -import net.i2p.crypto.eddsa.EdDSAPrivateKey -import net.i2p.crypto.eddsa.EdDSAPublicKey -import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey -import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey -import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateCrtKey -import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey -import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey -import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey import org.objenesis.instantiator.ObjectInstantiator import org.objenesis.strategy.InstantiatorStrategy import org.objenesis.strategy.StdInstantiatorStrategy @@ -62,14 +53,13 @@ import java.security.PublicKey import java.security.cert.CertPath import java.security.cert.X509Certificate import java.util.* -import kotlin.collections.ArrayList object DefaultKryoCustomizer { private val serializationWhitelists: List by lazy { ServiceLoader.load(SerializationWhitelist::class.java, this.javaClass.classLoader).toList() + DefaultWhitelist } - fun customize(kryo: Kryo, publicKeySerializer: Serializer = PublicKeySerializer): Kryo { + fun customize(kryo: Kryo): Kryo { return kryo.apply { isRegistrationRequired = false references = true @@ -110,6 +100,8 @@ object DefaultKryoCustomizer { // Please add any new registrations to the end. addDefaultSerializer(LinkedHashMapIteratorSerializer.getIterator()::class.java.superclass, LinkedHashMapIteratorSerializer) + addDefaultSerializer(PublicKey::class.java, PublicKeySerializer) + addDefaultSerializer(PrivateKey::class.java, PrivateKeySerializer) register(LinkedHashMapEntrySerializer.getEntry()::class.java, LinkedHashMapEntrySerializer) register(LinkedListItrSerializer.getListItr()::class.java, LinkedListItrSerializer) register(Arrays.asList("").javaClass, ArraysAsListSerializer()) @@ -126,11 +118,6 @@ object DefaultKryoCustomizer { // InputStream subclasses whitelisting, required for attachments. register(BufferedInputStream::class.java, InputStreamSerializer) register(Class.forName("sun.net.www.protocol.jar.JarURLConnection\$JarURLInputStream"), InputStreamSerializer) - register(PublicKey::class.java, publicKeySerializer) - register(PrivateKey::class.java, PrivateKeySerializer) - register(EdDSAPublicKey::class.java, publicKeySerializer) - register(EdDSAPrivateKey::class.java, PrivateKeySerializer) - register(CompositeKey::class.java, publicKeySerializer) // Using a custom serializer for compactness // Exceptions. We don't bother sending the stack traces as the client will fill in its own anyway. register(Array::class, read = { _, _ -> emptyArray() }, write = { _, _, _ -> }) // This ensures a NonEmptySetSerializer is constructed with an initial value. @@ -139,12 +126,6 @@ object DefaultKryoCustomizer { register(Class::class.java, ClassSerializer) register(FileInputStream::class.java, InputStreamSerializer) register(CertPath::class.java, CertPathSerializer) - register(BCECPrivateKey::class.java, PrivateKeySerializer) - register(BCECPublicKey::class.java, publicKeySerializer) - register(BCRSAPrivateCrtKey::class.java, PrivateKeySerializer) - register(BCRSAPublicKey::class.java, publicKeySerializer) - register(BCSphincs256PrivateKey::class.java, PrivateKeySerializer) - register(BCSphincs256PublicKey::class.java, publicKeySerializer) register(NotaryChangeWireTransaction::class.java, NotaryChangeWireTransactionSerializer) register(PartyAndCertificate::class.java, PartyAndCertificateSerializer) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt index b68b4a2e68..0ad5a7d037 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt @@ -28,7 +28,6 @@ import net.corda.core.transactions.NotaryChangeWireTransaction import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.OpaqueBytes -import net.corda.core.utilities.SgxSupport import net.corda.serialization.internal.serializationContextKey import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -83,11 +82,8 @@ class ImmutableClassSerializer(val klass: KClass) : Serializer() init { // Verify that this class is immutable (all properties are final). - // We disable this check inside SGX as the reflection blows up. - if (!SgxSupport.isInsideEnclave) { - props.forEach { - require(it !is KMutableProperty<*>) { "$it mutable property of class: ${klass} is unsupported" } - } + props.forEach { + require(it !is KMutableProperty<*>) { "$it mutable property of class: $klass is unsupported" } } } diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/SignedNodeInfoTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/SignedNodeInfoTest.kt index 544a5d34ec..39fa3073ae 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/SignedNodeInfoTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/SignedNodeInfoTest.kt @@ -6,16 +6,15 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.NodeInfo import net.corda.core.utilities.NetworkHostAndPort +import net.corda.coretesting.internal.TestNodeInfoBuilder +import net.corda.coretesting.internal.signWith import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME import net.corda.testing.core.SerializationEnvironmentRule -import net.corda.coretesting.internal.TestNodeInfoBuilder -import net.corda.coretesting.internal.signWith import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy -import org.junit.Ignore import org.junit.Rule import org.junit.Test import java.security.KeyPair @@ -56,7 +55,6 @@ class SignedNodeInfoTest { } @Test(timeout=300_000) - @Ignore("TODO JDK17: Fixme") fun `verifying composite keys only`() { val aliceKeyPair = generateKeyPair() val bobKeyPair = generateKeyPair() diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilderTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilderTest.kt index 6920c78093..7b3a329a9b 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilderTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilderTest.kt @@ -28,6 +28,6 @@ class ContentSignerBuilderTest { .isThrownBy { ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider) } - .withMessage("Incorrect key type EC for signature scheme NONEwithEdDSA") + .withMessage("Incorrect key type EC for signature scheme Ed25519") } -} \ No newline at end of file +} diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/cryptoservice/bouncycastle/BCCryptoServiceTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/cryptoservice/DefaultCryptoServiceTests.kt similarity index 83% rename from node-api/src/test/kotlin/net/corda/nodeapi/internal/cryptoservice/bouncycastle/BCCryptoServiceTests.kt rename to node-api/src/test/kotlin/net/corda/nodeapi/internal/cryptoservice/DefaultCryptoServiceTests.kt index 5eafd10187..560f33a69a 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/cryptoservice/bouncycastle/BCCryptoServiceTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/cryptoservice/DefaultCryptoServiceTests.kt @@ -1,33 +1,32 @@ -package net.corda.nodeapi.internal.cryptoservice.bouncycastle +package net.corda.nodeapi.internal.cryptoservice import net.corda.core.crypto.Crypto import net.corda.core.crypto.SignatureScheme import net.corda.core.crypto.internal.cordaBouncyCastleProvider import net.corda.core.utilities.days +import net.corda.coretesting.internal.stubs.CertificateStoreStubs import net.corda.nodeapi.internal.config.CertificateStoreSupplier import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509Utilities -import net.corda.nodeapi.internal.cryptoservice.CryptoService -import net.corda.nodeapi.internal.cryptoservice.CryptoServiceException -import net.corda.nodeapi.internal.cryptoservice.WrappedPrivateKey -import net.corda.nodeapi.internal.cryptoservice.WrappingMode -import net.corda.testing.core.ALICE_NAME -import net.corda.coretesting.internal.stubs.CertificateStoreStubs import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore +import net.corda.testing.core.ALICE_NAME import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.bouncycastle.jce.provider.BouncyCastleProvider import org.junit.Before -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder import java.io.FileOutputStream import java.nio.file.Path -import java.security.* +import java.security.KeyPair +import java.security.KeyPairGenerator +import java.security.KeyStore +import java.security.PublicKey +import java.security.Signature import java.security.spec.ECGenParameterSpec import java.time.Duration -import java.util.* +import java.util.UUID import javax.crypto.Cipher import javax.security.auth.x500.X500Principal import kotlin.io.path.div @@ -35,7 +34,7 @@ import kotlin.test.assertFailsWith import kotlin.test.assertFalse import kotlin.test.assertTrue -class BCCryptoServiceTests { +class DefaultCryptoServiceTests { companion object { val clearData = "data".toByteArray() } @@ -61,15 +60,13 @@ class BCCryptoServiceTests { } @Test(timeout=300_000) - @Ignore("TODO JDK17: Fixme") - fun `BCCryptoService generate key pair and sign both data and cert`() { - val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) + fun `cryptoService generate key pair and sign both data and cert`() { + val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) // Testing every supported scheme. - Crypto.supportedSignatureSchemes().filter { it != Crypto.COMPOSITE_KEY - && it.signatureName != "SHA512WITHSPHINCS256"}.forEach { generateKeyAndSignForScheme(cryptoService, it) } + Crypto.supportedSignatureSchemes().filter { it != Crypto.COMPOSITE_KEY }.forEach { generateKeyAndSignForScheme(cryptoService, it) } } - private fun generateKeyAndSignForScheme(cryptoService: BCCryptoService, signatureScheme: SignatureScheme) { + private fun generateKeyAndSignForScheme(cryptoService: DefaultCryptoService, signatureScheme: SignatureScheme) { val alias = "signature${signatureScheme.schemeNumberID}" val pubKey = cryptoService.generateKeyPair(alias, signatureScheme) assertTrue { cryptoService.containsKey(alias) } @@ -95,12 +92,10 @@ class BCCryptoServiceTests { } @Test(timeout=300_000) - @Ignore("TODO JDK17: Fixme") - fun `BCCryptoService generate key pair and sign with existing schemes`() { - val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) + fun `cryptoService generate key pair and sign with existing schemes`() { + val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) // Testing every supported scheme. - Crypto.supportedSignatureSchemes().filter { it != Crypto.COMPOSITE_KEY - && it.signatureName != "SHA512WITHSPHINCS256"}.forEach { + Crypto.supportedSignatureSchemes().filter { it != Crypto.COMPOSITE_KEY }.forEach { val alias = "signature${it.schemeNumberID}" val pubKey = cryptoService.generateKeyPair(alias, it) assertTrue { cryptoService.containsKey(alias) } @@ -110,9 +105,7 @@ class BCCryptoServiceTests { } @Test(timeout=300_000) - @Ignore("TODO JDK17: Fixme") - fun `BCCryptoService generate key pair and sign with passed signing algorithm`() { - + fun `cryptoService generate key pair and sign with passed signing algorithm`() { assertTrue{signAndVerify(signAlgo = "NONEwithRSA", alias = "myKeyAlias", keyTypeAlgo = "RSA")} assertTrue{signAndVerify(signAlgo = "MD2withRSA", alias = "myKeyAlias", keyTypeAlgo = "RSA")} assertTrue{signAndVerify(signAlgo = "MD5withRSA", alias = "myKeyAlias", keyTypeAlgo = "RSA")} @@ -132,7 +125,7 @@ class BCCryptoServiceTests { private fun signAndVerify(signAlgo: String, alias: String, keyTypeAlgo: String): Boolean { val keyPairGenerator = KeyPairGenerator.getInstance(keyTypeAlgo) val keyPair = keyPairGenerator.genKeyPair() - val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, createKeystore(alias, keyPair), wrappingKeyStorePath) + val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, createKeystore(alias, keyPair), wrappingKeyStorePath) assertTrue { cryptoService.containsKey(alias) } val signatureData = cryptoService.sign(alias, clearData, signAlgo) return verify(signAlgo, cryptoService.getPublicKey(alias), signatureData, clearData) @@ -175,7 +168,7 @@ class BCCryptoServiceTests { @Test(timeout=300_000) fun `When key does not exist getPublicKey, sign and getSigner should throw`() { val nonExistingAlias = "nonExistingAlias" - val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) + val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) assertFalse { cryptoService.containsKey(nonExistingAlias) } assertFailsWith { cryptoService.getPublicKey(nonExistingAlias) } assertFailsWith { cryptoService.sign(nonExistingAlias, clearData) } @@ -184,7 +177,7 @@ class BCCryptoServiceTests { @Test(timeout=300_000) fun `cryptoService supports degraded mode of wrapping`() { - val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) + val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) val supportedMode = cryptoService.getWrappingMode() assertThat(supportedMode).isEqualTo(WrappingMode.DEGRADED_WRAPPED) @@ -192,7 +185,7 @@ class BCCryptoServiceTests { @Test(timeout=300_000) fun `cryptoService does not fail when requested to create same wrapping key twice with failIfExists is false`() { - val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) + val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) val keyAlias = UUID.randomUUID().toString() cryptoService.createWrappingKey(keyAlias) @@ -201,7 +194,7 @@ class BCCryptoServiceTests { @Test(timeout=300_000) fun `cryptoService does fail when requested to create same wrapping key twice with failIfExists is true`() { - val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) + val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) val keyAlias = UUID.randomUUID().toString() cryptoService.createWrappingKey(keyAlias) @@ -213,7 +206,7 @@ class BCCryptoServiceTests { @Test(timeout=300_000) fun `cryptoService fails when asked to generate wrapped key pair or sign, but the master key specified does not exist`() { - val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) + val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) val wrappingKeyAlias = UUID.randomUUID().toString() @@ -230,7 +223,7 @@ class BCCryptoServiceTests { @Test(timeout=300_000) fun `cryptoService can generate wrapped key pair and sign with the private key successfully, using default algorithm`() { - val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) + val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) val wrappingKeyAlias = UUID.randomUUID().toString() cryptoService.createWrappingKey(wrappingKeyAlias) @@ -239,7 +232,7 @@ class BCCryptoServiceTests { @Test(timeout=300_000) fun `cryptoService can generate wrapped key pair and sign with the private key successfully`() { - val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) + val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) val wrappingKeyAlias = UUID.randomUUID().toString() cryptoService.createWrappingKey(wrappingKeyAlias) @@ -264,7 +257,7 @@ class BCCryptoServiceTests { @Test(timeout=300_000) fun `cryptoService can sign with previously encoded version of wrapped key`() { - val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) + val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) val wrappingKeyAlias = UUID.randomUUID().toString() cryptoService.createWrappingKey(wrappingKeyAlias) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt index 0692abeae5..f58d46312c 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt @@ -139,7 +139,12 @@ class KryoTests(private val compression: CordaSerializationEncoding?) { @Test(timeout=300_000) fun `deserialised key pair functions the same as serialised one`() { - val keyPair = generateKeyPair() + // The default signature scheme, EDDSA_ED25519_SHA512, generates public keys which have a writeReplace method (EdDSAPublicKeyImpl). + // This is picked up by Quasar's custom ReplaceableObjectKryo, which will *always* use the writeReplace for serialisation, ignoring + // any Kryo serialisers that might have been configured. This thus means the deserialisation path does not go via + // Cryto.decodePublicKey, which interns the materialised PublicKey. To avoid all of this, and test the last assertion, we use + // ECDSA keys, whose implementation doesn't have a writeReplace method. + val keyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) val bitsToSign: ByteArray = Ints.toByteArray(0x01234567) val wrongBits: ByteArray = Ints.toByteArray(0x76543210) val signature = keyPair.sign(bitsToSign) diff --git a/node/build.gradle b/node/build.gradle index 44c08ef17a..f904c1db21 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -223,7 +223,6 @@ dependencies { integrationTestImplementation "junit:junit:$junit_version" integrationTestImplementation "org.assertj:assertj-core:${assertj_version}" integrationTestImplementation "org.apache.qpid:qpid-jms-client:${protonj_version}" - integrationTestImplementation "net.i2p.crypto:eddsa:$eddsa_version" // used by FinalityFlowErrorHandlingTest slowIntegrationTestImplementation project(':testing:cordapps:cashobservers') @@ -276,7 +275,6 @@ quasar { "io.github.classgraph**", "io.netty*", "liquibase**", - "net.i2p.crypto.**", "nonapi.io.github.classgraph.**", "org.apiguardian.**", "org.bouncycastle**", diff --git a/node/capsule/build.gradle b/node/capsule/build.gradle index 3a2ac236c4..6258b55be3 100644 --- a/node/capsule/build.gradle +++ b/node/capsule/build.gradle @@ -65,7 +65,7 @@ tasks.register('buildCordaJAR', FatCapsule) { applicationVersion = corda_release_version applicationId = "net.corda.node.Corda" // See experimental/quasar-hook/README.md for how to generate. - def quasarExcludeExpression = "x(antlr**;bftsmart**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;org.mockito**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;kotlin**;net.bytebuddy**;net.i2p**;org.apache**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.objectweb**;org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**;com.lmax**;picocli**;liquibase**;com.github.benmanes**;org.json**;org.postgresql**;nonapi.io.github.classgraph**;io.opentelemetry**)" + def quasarExcludeExpression = "x(antlr**;bftsmart**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;org.mockito**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;kotlin**;net.bytebuddy**;org.apache**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.objectweb**;org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**;com.lmax**;picocli**;liquibase**;com.github.benmanes**;org.json**;org.postgresql**;nonapi.io.github.classgraph**;io.opentelemetry**)" def quasarClassLoaderExclusion = "l(net.corda.core.serialization.internal.**)" def quasarOptions = "m" javaAgents = quasar_classifier ? ["quasar-core-${quasar_version}-${quasar_classifier}.jar=${quasarOptions}${quasarExcludeExpression}${quasarClassLoaderExclusion}"] : ["quasar-core-${quasar_version}.jar=${quasarExcludeExpression}${quasarClassLoaderExclusion}"] diff --git a/node/capsule/src/main/resources/node-jvm-args.txt b/node/capsule/src/main/resources/node-jvm-args.txt index 21d6d9f829..d47e01d01a 100644 --- a/node/capsule/src/main/resources/node-jvm-args.txt +++ b/node/capsule/src/main/resources/node-jvm-args.txt @@ -3,7 +3,12 @@ --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.security.cert=ALL-UNNAMED +--add-opens=java.base/java.security.spec=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED +--add-opens=java.base/sun.security.pkcs=ALL-UNNAMED +--add-opens=java.base/sun.security.util=ALL-UNNAMED +--add-opens=java.base/sun.security.x509=ALL-UNNAMED --add-opens=java.sql/java.sql=ALL-UNNAMED +--add-opens=jdk.crypto.ec/sun.security.ec.ed=ALL-UNNAMED diff --git a/node/src/integration-test/kotlin/net/corda/node/customcheckpointserializer/TestCorDapp.kt b/node/src/integration-test/kotlin/net/corda/node/customcheckpointserializer/TestCorDapp.kt index 1d3e929dde..ee94fc62d0 100644 --- a/node/src/integration-test/kotlin/net/corda/node/customcheckpointserializer/TestCorDapp.kt +++ b/node/src/integration-test/kotlin/net/corda/node/customcheckpointserializer/TestCorDapp.kt @@ -7,7 +7,6 @@ import net.corda.core.flows.StartableByRPC import net.corda.core.serialization.CheckpointCustomSerializer import net.corda.testing.node.internal.CustomCordapp import net.corda.testing.node.internal.enclosedCordapp -import net.i2p.crypto.eddsa.EdDSAPublicKey import org.assertj.core.api.Assertions import java.security.PublicKey import java.time.Duration @@ -198,17 +197,4 @@ class TestCorDapp { throw FlowException("Broken on purpose") } } - - @Suppress("unused") - class BrokenEdDSAPublicKeySerializer : - CheckpointCustomSerializer { - override fun toProxy(obj: EdDSAPublicKey): String { - throw FlowException("Broken on purpose") - } - - override fun fromProxy(proxy: String): EdDSAPublicKey { - throw FlowException("Broken on purpose") - } - } - } diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 57cbea71f5..8d12f27003 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -150,7 +150,7 @@ import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.cordapp.CordappLoader import net.corda.nodeapi.internal.cordapp.cordappSchemas import net.corda.nodeapi.internal.cryptoservice.CryptoService -import net.corda.nodeapi.internal.cryptoservice.bouncycastle.BCCryptoService +import net.corda.nodeapi.internal.cryptoservice.DefaultCryptoService import net.corda.nodeapi.internal.lifecycle.NodeLifecycleEvent import net.corda.nodeapi.internal.lifecycle.NodeLifecycleEventsDistributor import net.corda.nodeapi.internal.lifecycle.NodeServicesContext @@ -1061,7 +1061,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } protected open fun makeCryptoService(): CryptoService { - return BCCryptoService(configuration.myLegalName.x500Principal, configuration.signingCertificateStore) + return DefaultCryptoService(configuration.myLegalName.x500Principal, configuration.signingCertificateStore) } @VisibleForTesting diff --git a/node/src/main/kotlin/net/corda/node/internal/KeyStoreHandler.kt b/node/src/main/kotlin/net/corda/node/internal/KeyStoreHandler.kt index 6c4225cdc0..f5ff1aa2c3 100644 --- a/node/src/main/kotlin/net/corda/node/internal/KeyStoreHandler.kt +++ b/node/src/main/kotlin/net/corda/node/internal/KeyStoreHandler.kt @@ -16,7 +16,7 @@ import net.corda.nodeapi.internal.crypto.X509Utilities.DISTRIBUTED_NOTARY_KEY_AL import net.corda.nodeapi.internal.crypto.X509Utilities.NODE_IDENTITY_KEY_ALIAS import net.corda.nodeapi.internal.crypto.checkValidity import net.corda.nodeapi.internal.cryptoservice.CryptoService -import net.corda.nodeapi.internal.cryptoservice.bouncycastle.BCCryptoService +import net.corda.nodeapi.internal.cryptoservice.DefaultCryptoService import java.io.IOException import java.math.BigInteger import java.nio.file.NoSuchFileException @@ -54,8 +54,8 @@ class KeyStoreHandler(private val configuration: NodeConfiguration, private val if (configuration.devMode) { configuration.configureWithDevSSLCertificate(cryptoService, devModeKeyEntropy) // configureWithDevSSLCertificate is a devMode process that writes directly to keystore files, so - // we should re-synchronise BCCryptoService with the updated keystore file. - if (cryptoService is BCCryptoService) { + // we should re-synchronise DefaultCryptoService with the updated keystore file. + if (cryptoService is DefaultCryptoService) { cryptoService.resyncKeystore() } } diff --git a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt index 9777d07a14..bd5c9c9999 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt @@ -17,7 +17,7 @@ import net.corda.nodeapi.internal.config.toProperties import net.corda.nodeapi.internal.crypto.X509KeyStore import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.cryptoservice.CryptoService -import net.corda.nodeapi.internal.cryptoservice.bouncycastle.BCCryptoService +import net.corda.nodeapi.internal.cryptoservice.DefaultCryptoService import net.corda.nodeapi.internal.installDevNodeCaCertPath import net.corda.nodeapi.internal.loadDevCaTrustStore import net.corda.nodeapi.internal.registerDevP2pCertificates @@ -195,7 +195,7 @@ fun MutualSslConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500N FileBasedCertificateStoreSupplier(keyStore.path, keyStore.storePassword, keyStore.entryPassword).get(true) .also { it.registerDevP2pCertificates(myLegalName) } when (cryptoService) { - is BCCryptoService, null -> { + is DefaultCryptoService, null -> { val signingKeyStore = FileBasedCertificateStoreSupplier(signingCertificateStore.path, signingCertificateStore.storePassword, signingCertificateStore.entryPassword).get(true) .also { it.installDevNodeCaCertPath(myLegalName) diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index a5cf742a9e..02d3695995 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -77,7 +77,7 @@ interface NodeConfiguration : ConfigurationWithOptionsContainer { val baseDirectory: Path val certificatesDirectory: Path // signingCertificateStore is used to store certificate chains. - // However, BCCryptoService is reusing this to store keys as well. + // However, DefaultCryptoService is reusing this to store keys as well. val signingCertificateStore: FileBasedCertificateStoreSupplier val p2pSslOptions: MutualSslConfiguration diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt index b999b1183d..c35cc1fa9f 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt @@ -22,7 +22,7 @@ import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA import net.corda.nodeapi.internal.crypto.X509Utilities.DEFAULT_VALIDITY_WINDOW import net.corda.nodeapi.internal.crypto.x509 import net.corda.nodeapi.internal.cryptoservice.CryptoService -import net.corda.nodeapi.internal.cryptoservice.bouncycastle.BCCryptoService +import net.corda.nodeapi.internal.cryptoservice.DefaultCryptoService import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.openssl.jcajce.JcaPEMWriter import org.bouncycastle.operator.ContentSigner @@ -98,7 +98,7 @@ open class NetworkRegistrationHelper( certificatesDirectory.safeSymbolicRead().createDirectories() // We need this in case cryptoService and certificateStore share the same KeyStore (for backwards compatibility purposes). // If we didn't, then an update to cryptoService wouldn't be reflected to certificateStore that is already loaded in memory. - val certStore: CertificateStore = if (cryptoService is BCCryptoService) cryptoService.certificateStore else certificateStore + val certStore: CertificateStore = if (cryptoService is DefaultCryptoService) cryptoService.certificateStore else certificateStore // SELF_SIGNED_PRIVATE_KEY is used as progress indicator. if (certStore.contains(nodeCaKeyAlias) && !certStore.contains(SELF_SIGNED_PRIVATE_KEY)) { @@ -169,7 +169,7 @@ open class NetworkRegistrationHelper( certificatesDirectory.safeSymbolicRead().createDirectories() // We need this in case cryptoService and certificateStore share the same KeyStore (for backwards compatibility purposes). // If we didn't, then an update to cryptoService wouldn't be reflected to certificateStore that is already loaded in memory. - val certStore: CertificateStore = if (cryptoService is BCCryptoService) cryptoService.certificateStore else certificateStore + val certStore: CertificateStore = if (cryptoService is DefaultCryptoService) cryptoService.certificateStore else certificateStore if (!certStore.contains(nodeCaKeyAlias)) { logProgress("Node CA key doesn't exist, program will now terminate...") @@ -374,7 +374,7 @@ class NodeRegistrationConfiguration( tlsCertCrlDistPoint = config.tlsCertCrlDistPoint, certificatesDirectory = config.certificatesDirectory, emailAddress = config.emailAddress, - cryptoService = BCCryptoService(config.myLegalName.x500Principal, config.signingCertificateStore), + cryptoService = DefaultCryptoService(config.myLegalName.x500Principal, config.signingCertificateStore), certificateStore = config.signingCertificateStore.get(true), notaryServiceConfig = config.notary?.let { // Validation of the presence of the notary service legal name is only done here and not in the top level configuration diff --git a/node/src/test/kotlin/net/corda/node/internal/KeyStoreHandlerTest.kt b/node/src/test/kotlin/net/corda/node/internal/KeyStoreHandlerTest.kt index 8e67f741ed..e6b39038a6 100644 --- a/node/src/test/kotlin/net/corda/node/internal/KeyStoreHandlerTest.kt +++ b/node/src/test/kotlin/net/corda/node/internal/KeyStoreHandlerTest.kt @@ -23,13 +23,12 @@ import net.corda.nodeapi.internal.crypto.X509Utilities.DISTRIBUTED_NOTARY_COMPOS import net.corda.nodeapi.internal.crypto.X509Utilities.DISTRIBUTED_NOTARY_KEY_ALIAS import net.corda.nodeapi.internal.crypto.X509Utilities.NODE_IDENTITY_KEY_ALIAS import net.corda.nodeapi.internal.cryptoservice.CryptoService -import net.corda.nodeapi.internal.cryptoservice.bouncycastle.BCCryptoService +import net.corda.nodeapi.internal.cryptoservice.DefaultCryptoService import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Before -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder @@ -37,7 +36,6 @@ import java.security.KeyPair import java.security.PublicKey import kotlin.io.path.div -@Ignore("TODO JDK17: Fixme") class KeyStoreHandlerTest { @Rule @JvmField @@ -49,7 +47,7 @@ class KeyStoreHandlerTest { private val keyStore get() = config.signingCertificateStore.get() - private lateinit var cryptoService: BCCryptoService + private lateinit var cryptoService: DefaultCryptoService private lateinit var keyStoreHandler: KeyStoreHandler @@ -66,7 +64,7 @@ class KeyStoreHandlerTest { doReturn(ALICE_NAME).whenever(it).myLegalName doReturn(null).whenever(it).notary } - cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore) + cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore) keyStoreHandler = KeyStoreHandler(config, cryptoService) } @@ -192,7 +190,7 @@ class KeyStoreHandlerTest { val devCertificateDir = tempFolder.root.toPath() / "certificates-dev" val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(devCertificateDir) val p2pSslOptions = CertificateStoreStubs.P2P.withCertificatesDirectory(devCertificateDir) - val devCryptoService = BCCryptoService(config.myLegalName.x500Principal, signingCertificateStore) + val devCryptoService = DefaultCryptoService(config.myLegalName.x500Principal, signingCertificateStore) doReturn(true).whenever(config).devMode doReturn(signingCertificateStore).whenever(config).signingCertificateStore diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index 6b982b920c..1110f14ea1 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -955,8 +955,7 @@ class DriverDSLImpl( val excludePackagePattern = "x(antlr**;bftsmart**;ch**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;" + "com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;" + "com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;" + - "io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;" + - "net.i2p**;org.apache**;" + + "io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;org.apache**;" + "org.assertj**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;" + "org.hamcrest**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.junit**;org.mockito**;org.objectweb**;" + "org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**;" +