Added composite key provider for storing composite keys in keystore (#1006)

* Add unit tests around decoding composite keys

(cherry picked from commit 9ccdd8e)

* Start writing a Composite signature scheme

(cherry picked from commit 72ac3a5)

* Composite key serialisation

* refactoring

* * Address PR issues

* * Address PR issues

* * Address PR issues

* * Address PR issues

* fix up after rebase
This commit is contained in:
Patrick Kuo 2017-07-12 12:13:29 +01:00 committed by GitHub
parent 5f7b8f6ec3
commit 78ecff7933
20 changed files with 269 additions and 51 deletions

View File

@ -10,6 +10,7 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule
import net.corda.contracts.BusinessCalendar
import net.corda.core.contracts.Amount
import net.corda.core.crypto.*
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party

View File

@ -1,6 +1,9 @@
package net.corda.core.crypto
import net.corda.core.crypto.random63BitValue
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.crypto.composite.CompositeSignature
import net.corda.core.crypto.provider.CordaObjectIdentifier
import net.corda.core.crypto.provider.CordaSecurityProvider
import net.i2p.crypto.eddsa.EdDSAEngine
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
@ -147,6 +150,22 @@ object Crypto {
"at the cost of larger key sizes and loss of compatibility."
)
/**
* Corda composite key type
*/
val COMPOSITE_KEY = SignatureScheme(
6,
"COMPOSITE",
AlgorithmIdentifier(CordaObjectIdentifier.compositeKey),
emptyList(),
CordaSecurityProvider.PROVIDER_NAME,
CompositeKey.KEY_ALGORITHM,
CompositeSignature.SIGNATURE_ALGORITHM,
null,
null,
"Composite keys composed from individual public keys"
)
/** Our default signature scheme if no algorithm is specified (e.g. for key generation). */
val DEFAULT_SIGNATURE_SCHEME = EDDSA_ED25519_SHA512
@ -159,7 +178,8 @@ object Crypto {
ECDSA_SECP256K1_SHA256,
ECDSA_SECP256R1_SHA256,
EDDSA_ED25519_SHA512,
SPHINCS256_SHA256
SPHINCS256_SHA256,
COMPOSITE_KEY
).associateBy { it.schemeCodeName }
/**
@ -177,6 +197,7 @@ object Crypto {
// The val is private to avoid any harmful state changes.
private val providerMap: Map<String, Provider> = mapOf(
BouncyCastleProvider.PROVIDER_NAME to getBouncyCastleProvider(),
CordaSecurityProvider.PROVIDER_NAME to CordaSecurityProvider(),
"BCPQC" to BouncyCastlePQCProvider()) // unfortunately, provider's name is not final in BouncyCastlePQCProvider, so we explicitly set it.
private fun getBouncyCastleProvider() = BouncyCastleProvider().apply {
@ -188,6 +209,7 @@ object Crypto {
// 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.
Security.addProvider(getBouncyCastleProvider())
Security.addProvider(CordaSecurityProvider())
}
/**
@ -202,7 +224,7 @@ object Crypto {
}
fun findSignatureScheme(algorithm: AlgorithmIdentifier): SignatureScheme {
return algorithmMap[normaliseAlgorithmIdentifier(algorithm)] ?: throw IllegalArgumentException("Unrecognised algorithm: ${algorithm}")
return algorithmMap[normaliseAlgorithmIdentifier(algorithm)] ?: throw IllegalArgumentException("Unrecognised algorithm: ${algorithm.algorithm.id}")
}
/**
@ -543,7 +565,7 @@ object Crypto {
if (signatureScheme.algSpec != null)
keyPairGenerator.initialize(signatureScheme.algSpec, newSecureRandom())
else
keyPairGenerator.initialize(signatureScheme.keySize, newSecureRandom())
keyPairGenerator.initialize(signatureScheme.keySize!!, newSecureRandom())
return keyPairGenerator.generateKeyPair()
}

View File

@ -2,6 +2,7 @@
package net.corda.core.crypto
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.identity.Party
import net.corda.core.utilities.OpaqueBytes
import java.math.BigInteger

View File

@ -28,5 +28,5 @@ data class SignatureScheme(
val algorithmName: String,
val signatureName: String,
val algSpec: AlgorithmParameterSpec?,
val keySize: Int,
val keySize: Int?,
val desc: String)

View File

@ -1,8 +1,14 @@
package net.corda.core.crypto
package net.corda.core.crypto.composite
import net.corda.core.crypto.CompositeKey.NodeAndWeight
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.composite.CompositeKey.NodeAndWeight
import net.corda.core.crypto.keys
import net.corda.core.crypto.provider.CordaObjectIdentifier
import net.corda.core.crypto.toSHA256Bytes
import net.corda.core.crypto.toStringShort
import net.corda.core.serialization.CordaSerializable
import org.bouncycastle.asn1.*
import org.bouncycastle.asn1.x509.AlgorithmIdentifier
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import java.nio.ByteBuffer
import java.security.PublicKey
@ -26,9 +32,35 @@ import java.util.*
* signatures required) to satisfy the sub-tree rooted at this node.
*/
@CordaSerializable
class CompositeKey private constructor (val threshold: Int,
children: List<NodeAndWeight>) : PublicKey {
class CompositeKey private constructor(val threshold: Int, children: List<NodeAndWeight>) : PublicKey {
companion object {
val KEY_ALGORITHM = "COMPOSITE"
/**
* Build a composite key from a DER encoded form.
*/
fun getInstance(encoded: ByteArray) = getInstance(ASN1Primitive.fromByteArray(encoded))
fun getInstance(asn1: ASN1Primitive): PublicKey {
val keyInfo = SubjectPublicKeyInfo.getInstance(asn1)
require(keyInfo.algorithm.algorithm == CordaObjectIdentifier.compositeKey)
val sequence = ASN1Sequence.getInstance(keyInfo.parsePublicKey())
val threshold = ASN1Integer.getInstance(sequence.getObjectAt(0)).positiveValue.toInt()
val sequenceOfChildren = ASN1Sequence.getInstance(sequence.getObjectAt(1))
val builder = Builder()
val listOfChildren = sequenceOfChildren.objects.toList()
listOfChildren.forEach { childAsn1 ->
require(childAsn1 is ASN1Sequence)
val childSeq = childAsn1 as ASN1Sequence
val key = Crypto.decodePublicKey((childSeq.getObjectAt(0) as DERBitString).bytes)
val weight = ASN1Integer.getInstance(childSeq.getObjectAt(1))
builder.addKey(key, weight.positiveValue.toInt())
}
return builder.build(threshold)
}
}
val children = children.sorted()
init {
// TODO: replace with the more extensive, but slower, checkValidity() test.
checkConstraints()
@ -47,8 +79,9 @@ class CompositeKey private constructor (val threshold: Int,
require(threshold > 0) { "CompositeKey threshold is set to $threshold, but it should be a positive integer." }
// If threshold is bigger than total weight, then it will never be satisfied.
val totalWeight = totalWeight()
require(threshold <= totalWeight) { "CompositeKey threshold: $threshold cannot be bigger than aggregated weight of " +
"child nodes: $totalWeight"}
require(threshold <= totalWeight) {
"CompositeKey threshold: $threshold cannot be bigger than aggregated weight of child nodes: $totalWeight"
}
}
// Graph cycle detection in the composite key structure to avoid infinite loops on CompositeKey graph traversal and
@ -75,7 +108,7 @@ class CompositeKey private constructor (val threshold: Int,
* TODO: Always call this method when deserialising [CompositeKey]s.
*/
fun checkValidity() {
val visitedMap = IdentityHashMap<CompositeKey,Boolean>()
val visitedMap = IdentityHashMap<CompositeKey, Boolean>()
visitedMap.put(this, true)
cycleDetection(visitedMap) // Graph cycle testing on the root node.
checkConstraints()
@ -93,7 +126,7 @@ class CompositeKey private constructor (val threshold: Int,
private fun totalWeight(): Int {
var sum = 0
for ((_, weight) in children) {
require (weight > 0) { "Non-positive weight: $weight detected." }
require(weight > 0) { "Non-positive weight: $weight detected." }
sum = Math.addExact(sum, weight) // Add and check for integer overflow.
}
return sum
@ -104,17 +137,17 @@ class CompositeKey private constructor (val threshold: Int,
* Each node should be assigned with a positive weight to avoid certain types of weight underflow attacks.
*/
@CordaSerializable
data class NodeAndWeight(val node: PublicKey, val weight: Int): Comparable<NodeAndWeight>, ASN1Object() {
data class NodeAndWeight(val node: PublicKey, val weight: Int) : Comparable<NodeAndWeight>, ASN1Object() {
init {
// We don't allow zero or negative weights. Minimum weight = 1.
require (weight > 0) { "A non-positive weight was detected. Node info: $this" }
require(weight > 0) { "A non-positive weight was detected. Node info: $this" }
}
override fun compareTo(other: NodeAndWeight): Int {
return if (weight == other.weight) {
return if (weight == other.weight)
ByteBuffer.wrap(node.toSHA256Bytes()).compareTo(ByteBuffer.wrap(other.node.toSHA256Bytes()))
} else weight.compareTo(other.weight)
else
weight.compareTo(other.weight)
}
override fun toASN1Primitive(): ASN1Primitive {
@ -129,16 +162,13 @@ class CompositeKey private constructor (val threshold: Int,
}
}
companion object {
val ALGORITHM = CompositeSignature.ALGORITHM_IDENTIFIER.algorithm.toString()
}
/**
* Takes single PublicKey and checks if CompositeKey requirements hold for that key.
*/
fun isFulfilledBy(key: PublicKey) = isFulfilledBy(setOf(key))
override fun getAlgorithm() = ALGORITHM
override fun getAlgorithm() = KEY_ALGORITHM
override fun getEncoded(): ByteArray {
val keyVector = ASN1EncodableVector()
val childrenVector = ASN1EncodableVector()
@ -147,13 +177,14 @@ class CompositeKey private constructor (val threshold: Int,
}
keyVector.add(ASN1Integer(threshold.toLong()))
keyVector.add(DERSequence(childrenVector))
return SubjectPublicKeyInfo(CompositeSignature.ALGORITHM_IDENTIFIER, DERSequence(keyVector)).encoded
return SubjectPublicKeyInfo(AlgorithmIdentifier(CordaObjectIdentifier.compositeKey), DERSequence(keyVector)).encoded
}
override fun getFormat() = ASN1Encoding.DER
// Extracted method from isFulfilledBy.
private fun checkFulfilledBy(keysToCheck: Iterable<PublicKey>): Boolean {
if (keysToCheck.any { it is CompositeKey } ) return false
if (keysToCheck.any { it is CompositeKey }) return false
val totalWeight = children.map { (node, weight) ->
if (node is CompositeKey) {
if (node.checkFulfilledBy(keysToCheck)) weight else 0
@ -221,18 +252,18 @@ class CompositeKey private constructor (val threshold: Int,
* Builds the [CompositeKey]. If [threshold] is not specified, it will default to
* the total (aggregated) weight of the children, effectively generating an "N of N" requirement.
* During process removes single keys wrapped in [CompositeKey] and enforces ordering on child nodes.
*
* @throws IllegalArgumentException
*/
@Throws(IllegalArgumentException::class)
fun build(threshold: Int? = null): PublicKey {
val n = children.size
if (n > 1)
return CompositeKey(threshold ?: children.map { (_, weight) -> weight }.sum(), children)
return if (n > 1)
CompositeKey(threshold ?: children.map { (_, weight) -> weight }.sum(), children)
else if (n == 1) {
require(threshold == null || threshold == children.first().weight)
{ "Trying to build invalid CompositeKey, threshold value different than weight of single child node." }
return children.first().node // We can assume that this node is a correct CompositeKey.
}
else throw IllegalArgumentException("Trying to build CompositeKey without child nodes.")
{ "Trying to build invalid CompositeKey, threshold value different than weight of single child node." }
children.first().node // We can assume that this node is a correct CompositeKey.
} else throw IllegalArgumentException("Trying to build CompositeKey without child nodes.")
}
}
}

View File

@ -1,4 +1,4 @@
package net.corda.core.crypto
package net.corda.core.crypto.composite
import net.corda.core.serialization.deserialize
import org.bouncycastle.asn1.ASN1ObjectIdentifier
@ -10,14 +10,10 @@ import java.security.spec.AlgorithmParameterSpec
/**
* Dedicated class for storing a set of signatures that comprise [CompositeKey].
*/
class CompositeSignature : Signature(ALGORITHM) {
class CompositeSignature : Signature(SIGNATURE_ALGORITHM) {
companion object {
val ALGORITHM = "2.25.30086077608615255153862931087626791003"
// UUID-based OID
// TODO: Register for an OID space and issue our own shorter OID
val ALGORITHM_IDENTIFIER = AlgorithmIdentifier(ASN1ObjectIdentifier(ALGORITHM))
fun getService(provider: Provider) = Provider.Service(provider, "Signature", ALGORITHM, CompositeSignature::class.java.name, emptyList(), emptyMap())
val SIGNATURE_ALGORITHM = "COMPOSITESIG"
fun getService(provider: Provider) = Provider.Service(provider, "Signature", SIGNATURE_ALGORITHM, CompositeSignature::class.java.name, emptyList(), emptyMap())
}
private var signatureState: State? = null

View File

@ -1,5 +1,6 @@
package net.corda.core.crypto
package net.corda.core.crypto.composite
import net.corda.core.crypto.DigitalSignature
import net.corda.core.serialization.CordaSerializable
/**

View File

@ -0,0 +1,34 @@
package net.corda.core.crypto.composite
import java.security.*
import java.security.spec.InvalidKeySpecException
import java.security.spec.KeySpec
import java.security.spec.X509EncodedKeySpec
class KeyFactory : KeyFactorySpi() {
@Throws(InvalidKeySpecException::class)
override fun engineGeneratePrivate(keySpec: KeySpec): PrivateKey {
// Private composite key not supported.
throw InvalidKeySpecException("key spec not recognised: " + keySpec.javaClass)
}
@Throws(InvalidKeySpecException::class)
override fun engineGeneratePublic(keySpec: KeySpec): PublicKey? {
return when (keySpec) {
is X509EncodedKeySpec -> CompositeKey.getInstance(keySpec.encoded)
else -> throw InvalidKeySpecException("key spec not recognised: " + keySpec.javaClass)
}
}
@Throws(InvalidKeySpecException::class)
override fun <T : KeySpec> engineGetKeySpec(key: Key, keySpec: Class<T>): T {
// Only support [X509EncodedKeySpec].
throw InvalidKeySpecException("Not implemented yet $key $keySpec")
}
@Throws(InvalidKeyException::class)
override fun engineTranslateKey(key: Key): Key {
throw InvalidKeyException("No other composite key providers known")
}
}

View File

@ -0,0 +1,37 @@
package net.corda.core.crypto.provider
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.crypto.composite.CompositeSignature
import org.bouncycastle.asn1.ASN1ObjectIdentifier
import org.bouncycastle.asn1.x509.AlgorithmIdentifier
import java.security.AccessController
import java.security.PrivilegedAction
import java.security.Provider
class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME security provider wrapper") {
companion object {
val PROVIDER_NAME = "Corda"
}
init {
AccessController.doPrivileged(PrivilegedAction<Unit> { setup() })
}
private fun setup() {
put("KeyFactory.${CompositeKey.KEY_ALGORITHM}", "net.corda.core.crypto.composite.KeyFactory")
put("Signature.${CompositeSignature.SIGNATURE_ALGORITHM}", "net.corda.core.crypto.composite.CompositeSignature")
val compositeKeyOID = CordaObjectIdentifier.compositeKey.id
put("Alg.Alias.KeyFactory.$compositeKeyOID", CompositeKey.KEY_ALGORITHM)
put("Alg.Alias.KeyFactory.OID.$compositeKeyOID", CompositeKey.KEY_ALGORITHM)
put("Alg.Alias.Signature.$compositeKeyOID", CompositeSignature.SIGNATURE_ALGORITHM)
put("Alg.Alias.Signature.OID.$compositeKeyOID", CompositeSignature.SIGNATURE_ALGORITHM)
}
}
object CordaObjectIdentifier {
// UUID-based OID
// TODO: Register for an OID space and issue our own shorter OID
val compositeKey = ASN1ObjectIdentifier("2.25.30086077608615255153862931087626791002")
val compositeSignature = ASN1ObjectIdentifier("2.25.30086077608615255153862931087626791003")
}

View File

@ -3,7 +3,7 @@ package net.corda.core.node.services
import co.paralleluniverse.fibers.Suspendable
import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.keys

View File

@ -8,7 +8,7 @@ import de.javakaffee.kryoserializers.ArraysAsListSerializer
import de.javakaffee.kryoserializers.BitSetSerializer
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer
import de.javakaffee.kryoserializers.guava.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.crypto.MetaData
import net.corda.core.node.CordaPluginRegistry
import net.corda.core.transactions.SignedTransaction

View File

@ -9,6 +9,7 @@ import com.esotericsoftware.kryo.util.MapReferenceResolver
import com.google.common.annotations.VisibleForTesting
import net.corda.core.contracts.*
import net.corda.core.crypto.*
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.identity.Party
import net.corda.core.node.AttachmentsClassLoader
import net.corda.core.transactions.WireTransaction

View File

@ -2,7 +2,7 @@ package net.corda.core.contracts
import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY
import net.corda.testing.contracts.DummyContract
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.sign

View File

@ -1,14 +1,25 @@
package net.corda.core.crypto
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.crypto.composite.CompositeSignature
import net.corda.core.crypto.composite.CompositeSignaturesWithKeys
import net.corda.core.div
import net.corda.core.serialization.serialize
import net.corda.core.utilities.OpaqueBytes
import org.bouncycastle.asn1.x500.X500Name
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class CompositeKeyTests {
@Rule
@JvmField
val tempFolder: TemporaryFolder = TemporaryFolder()
val aliceKey = generateKeyPair()
val bobKey = generateKeyPair()
val charlieKey = generateKeyPair()
@ -65,7 +76,7 @@ class CompositeKeyTests {
}
@Test
fun `encoded tree decodes correctly`() {
fun `kryo encoded tree decodes correctly`() {
val aliceAndBob = CompositeKey.Builder().addKeys(alicePublicKey, bobPublicKey).build()
val aliceAndBobOrCharlie = CompositeKey.Builder().addKeys(aliceAndBob, charliePublicKey).build(threshold = 1)
@ -75,6 +86,35 @@ class CompositeKeyTests {
assertEquals(decoded, aliceAndBobOrCharlie)
}
@Test
fun `der encoded tree decodes correctly`() {
val aliceAndBob = CompositeKey.Builder().addKeys(alicePublicKey, bobPublicKey).build()
val aliceAndBobOrCharlie = CompositeKey.Builder().addKeys(aliceAndBob, charliePublicKey).build(threshold = 1)
val encoded = aliceAndBobOrCharlie.encoded
val decoded = CompositeKey.getInstance(encoded)
assertEquals(decoded, aliceAndBobOrCharlie)
}
@Test
fun `der encoded tree decodes correctly with weighting`() {
val aliceAndBob = CompositeKey.Builder()
.addKey(alicePublicKey, 2)
.addKey(bobPublicKey, 1)
.build(threshold = 2)
val aliceAndBobOrCharlie = CompositeKey.Builder()
.addKey(aliceAndBob, 3)
.addKey(charliePublicKey, 2)
.build(threshold = 3)
val encoded = aliceAndBobOrCharlie.encoded
val decoded = CompositeKey.getInstance(encoded)
assertEquals(decoded, aliceAndBobOrCharlie)
}
@Test
fun `tree canonical form`() {
assertEquals(CompositeKey.Builder().addKeys(alicePublicKey).build(), alicePublicKey)
@ -260,6 +300,59 @@ class CompositeKeyTests {
assertFalse { compositeKey.isFulfilledBy(signaturesWithoutRSA.byKeys()) }
}
@Test
fun `Test save to keystore`() {
// From test case [CompositeKey from multiple signature schemes and signature verification]
val (privRSA, pubRSA) = Crypto.generateKeyPair(Crypto.RSA_SHA256)
val (privK1, pubK1) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val (privR1, pubR1) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val (privEd, pubEd) = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
val (privSP, pubSP) = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
val RSASignature = privRSA.sign(message.bytes, pubRSA)
val K1Signature = privK1.sign(message.bytes, pubK1)
val R1Signature = privR1.sign(message.bytes, pubR1)
val EdSignature = privEd.sign(message.bytes, pubEd)
val SPSignature = privSP.sign(message.bytes, pubSP)
val compositeKey = CompositeKey.Builder().addKeys(pubRSA, pubK1, pubR1, pubEd, pubSP).build() as CompositeKey
val signatures = listOf(RSASignature, K1Signature, R1Signature, EdSignature, SPSignature)
assertTrue { compositeKey.isFulfilledBy(signatures.byKeys()) }
// One signature is missing.
val signaturesWithoutRSA = listOf(K1Signature, R1Signature, EdSignature, SPSignature)
assertFalse { compositeKey.isFulfilledBy(signaturesWithoutRSA.byKeys()) }
// Create self sign CA.
val caKeyPair = Crypto.generateKeyPair()
val ca = X509Utilities.createSelfSignedCACertificate(X500Name("CN=Test CA"), caKeyPair)
// Sign the composite key with the self sign CA.
val compositeKeyCert = X509Utilities.createCertificate(CertificateType.IDENTITY, ca, caKeyPair, X500Name("CN=CompositeKey"), compositeKey)
// Store certificate to keystore.
val keystorePath = tempFolder.root.toPath() / "keystore.jks"
val keystore = KeyStoreUtilities.loadOrCreateKeyStore(keystorePath, "password")
keystore.setCertificateEntry("CompositeKey", compositeKeyCert.cert)
keystore.save(keystorePath, "password")
// Load keystore from disk.
val keystore2 = KeyStoreUtilities.loadKeyStore(keystorePath, "password")
assertTrue { keystore2.containsAlias("CompositeKey") }
val key = keystore2.getCertificate("CompositeKey").publicKey
// Convert sun public key to Composite key.
val compositeKey2 = Crypto.toSupportedPublicKey(key)
assertTrue { compositeKey2 is CompositeKey }
// Run the same composite key test again.
assertTrue { compositeKey2.isFulfilledBy(signatures.byKeys()) }
assertFalse { compositeKey2.isFulfilledBy(signaturesWithoutRSA.byKeys()) }
// Ensure keys are the same before and after keystore.
assertEquals(compositeKey, compositeKey2)
}
@Test
fun `CompositeKey deterministic children sorting`() {
val (_, pub1) = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)

View File

@ -344,7 +344,7 @@ class CryptoUtilsTest {
@Test
fun `Check supported algorithms`() {
val algList: List<String> = Crypto.supportedSignatureSchemes.keys.toList()
val expectedAlgSet = setOf("RSA_SHA256", "ECDSA_SECP256K1_SHA256", "ECDSA_SECP256R1_SHA256", "EDDSA_ED25519_SHA512", "SPHINCS-256_SHA512")
val expectedAlgSet = setOf("RSA_SHA256", "ECDSA_SECP256K1_SHA256", "ECDSA_SECP256R1_SHA256", "EDDSA_ED25519_SHA512", "SPHINCS-256_SHA512", "COMPOSITE")
assertTrue { Sets.symmetricDifference(expectedAlgSet, algList.toSet()).isEmpty(); }
}

View File

@ -9,7 +9,7 @@ import io.requery.sql.*
import io.requery.sql.platform.Generic
import net.corda.core.contracts.*
import net.corda.testing.contracts.DummyContract
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.toBase58String

View File

@ -4,7 +4,8 @@ import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.CompositeKey
import net.corda.testing.contracts.DummyContract
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.crypto.SecureHash
import net.corda.core.div
import net.corda.core.getOrThrow
@ -22,7 +23,6 @@ import net.corda.node.services.transactions.minClusterSize
import net.corda.node.services.transactions.minCorrectReplicas
import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.node.utilities.transaction
import net.corda.testing.contracts.DummyContract
import net.corda.testing.node.MockNetwork
import org.bouncycastle.asn1.x500.X500Name
import org.junit.After

View File

@ -10,6 +10,7 @@ import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult
import net.corda.core.*
import net.corda.core.crypto.*
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.flows.*
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate

View File

@ -1,7 +1,6 @@
package net.corda.node.utilities
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.X509Utilities
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.crypto.generateKeyPair
import net.corda.core.identity.Party
import net.corda.core.serialization.serialize

View File

@ -2,6 +2,7 @@ package net.corda.testing
import net.corda.core.contracts.*
import net.corda.core.crypto.*
import net.corda.core.crypto.composite.expandedCompositeKeys
import net.corda.core.crypto.testing.NullSignature
import net.corda.core.identity.Party
import net.corda.core.node.ServiceHub