Support for multi-sig schemes. ECC (K1/R1) and EdDSA are fully supported. (#599)

Support for multi-sig scheme. ECC (K1/R1) and EdDSA are fully supported.
This commit is contained in:
Konstantinos Chalkias 2017-05-04 18:19:00 +01:00 committed by GitHub
parent fe7d893de2
commit d8fa75654f
28 changed files with 451 additions and 451 deletions

View File

@ -207,7 +207,7 @@ object JacksonSupport {
object PublicKeySerializer : JsonSerializer<EdDSAPublicKey>() {
override fun serialize(obj: EdDSAPublicKey, generator: JsonGenerator, provider: SerializerProvider) {
check(obj.params == ed25519Curve)
check(obj.params == Crypto.EDDSA_ED25519_SHA512.algSpec)
generator.writeString(obj.toBase58String())
}
}

View File

@ -75,7 +75,7 @@ class CompositeSignature : Signature(ALGORITHM) {
val sig = sigBytes.deserialize<CompositeSignaturesWithKeys>()
return if (verifyKey.isFulfilledBy(sig.sigs.map { it.by })) {
val clearData = buffer.toByteArray()
sig.sigs.all { it.isValidForECDSA(clearData) }
sig.sigs.all { it.isValid(clearData) }
} else {
false
}

View File

@ -1,6 +1,8 @@
package net.corda.core.crypto
import net.i2p.crypto.eddsa.EdDSAEngine
import net.i2p.crypto.eddsa.EdDSAKey
import net.i2p.crypto.eddsa.EdDSASecurityProvider
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable
import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.interfaces.ECKey
@ -26,62 +28,61 @@ import java.security.spec.X509EncodedKeySpec
* </ul>
*/
object Crypto {
// This map is required to defend against users that forcibly call Security.addProvider / Security.removeProvider
// that could cause unexpected and suspicious behaviour.
// i.e. if someone removes a Provider and then he/she adds a new one with the same name.
// The val is private to avoid any harmful state changes.
private val providerMap = mapOf(
EdDSASecurityProvider.PROVIDER_NAME to EdDSASecurityProvider(),
BouncyCastleProvider.PROVIDER_NAME to BouncyCastleProvider(),
"BCPQC" to BouncyCastlePQCProvider()) // unfortunately, provider's name is not final in BouncyCastlePQCProvider, so we explicitly set it.
init {
Security.addProvider(I2PProvider()) // register I2P Crypto Provider, required for EdDSA.
Security.addProvider(BouncyCastleProvider()) // register Bouncy Castle Crypto Provider (for RSA, ECDSA).
Security.addProvider(BouncyCastlePQCProvider()) // register Bouncy Castle Post-Quantum Crypto Provider (for SPHINCS-256).
}
/**
* RSA_SHA256 signature scheme using SHA256 as hash algorithm and MGF1 (with SHA256) as mask generation function.
* Note: Recommended key size >= 3072 bits.
*/
private val RSA_SHA256 = SignatureScheme(
val RSA_SHA256 = SignatureScheme(
1,
"RSA_SHA256",
BouncyCastleProvider.PROVIDER_NAME,
"RSA",
Signature.getInstance("SHA256WITHRSAANDMGF1", "BC"),
KeyFactory.getInstance("RSA", "BC"),
KeyPairGenerator.getInstance("RSA", "BC"),
"SHA256WITHRSAANDMGF1",
null,
3072,
"RSA_SHA256 signature scheme using SHA256 as hash algorithm and MGF1 (with SHA256) as mask generation function."
)
/** ECDSA signature scheme using the secp256k1 Koblitz curve. */
private val ECDSA_SECP256K1_SHA256 = SignatureScheme(
val ECDSA_SECP256K1_SHA256 = SignatureScheme(
2,
"ECDSA_SECP256K1_SHA256",
BouncyCastleProvider.PROVIDER_NAME,
"ECDSA",
Signature.getInstance("SHA256withECDSA", "BC"),
KeyFactory.getInstance("ECDSA", "BC"),
KeyPairGenerator.getInstance("ECDSA", "BC"),
"SHA256withECDSA",
ECNamedCurveTable.getParameterSpec("secp256k1"),
256,
"ECDSA signature scheme using the secp256k1 Koblitz curve."
)
/** ECDSA signature scheme using the secp256r1 (NIST P-256) curve. */
private val ECDSA_SECP256R1_SHA256 = SignatureScheme(
val ECDSA_SECP256R1_SHA256 = SignatureScheme(
3,
"ECDSA_SECP256R1_SHA256",
BouncyCastleProvider.PROVIDER_NAME,
"ECDSA",
Signature.getInstance("SHA256withECDSA", "BC"),
KeyFactory.getInstance("ECDSA", "BC"),
KeyPairGenerator.getInstance("ECDSA", "BC"),
"SHA256withECDSA",
ECNamedCurveTable.getParameterSpec("secp256r1"),
256,
"ECDSA signature scheme using the secp256r1 (NIST P-256) curve."
)
/** EdDSA signature scheme using the ed255519 twisted Edwards curve. */
private val EDDSA_ED25519_SHA512 = SignatureScheme(
val EDDSA_ED25519_SHA512 = SignatureScheme(
4,
"EDDSA_ED25519_SHA512",
"EdDSA",
Signature.getInstance("SHA512withEdDSA", "I2P"),
KeyFactory.getInstance("EdDSA", "I2P"),
KeyPairGenerator.getInstance("EdDSA", "I2P"),
EdDSASecurityProvider.PROVIDER_NAME,
EdDSAKey.KEY_ALGORITHM,
EdDSAEngine.SIGNATURE_ALGORITHM,
EdDSANamedCurveTable.getByName("ED25519"),
256,
"EdDSA signature scheme using the ed25519 twisted Edwards curve."
@ -91,13 +92,12 @@ object Crypto {
* SPHINCS-256 hash-based signature scheme. It provides 128bit security against post-quantum attackers
* at the cost of larger key sizes and loss of compatibility.
*/
private val SPHINCS256_SHA256 = SignatureScheme(
val SPHINCS256_SHA256 = SignatureScheme(
5,
"SPHINCS-256_SHA512",
"SPHINCS-256",
Signature.getInstance("SHA512WITHSPHINCS256", "BCPQC"),
KeyFactory.getInstance("SPHINCS256", "BCPQC"),
KeyPairGenerator.getInstance("SPHINCS256", "BCPQC"),
"BCPQC",
"SPHINCS256",
"SHA512WITHSPHINCS256",
SPHINCS256KeyGenParameterSpec(SPHINCS256KeyGenParameterSpec.SHA512_256),
256,
"SPHINCS-256 hash-based signature scheme. It provides 128bit security against post-quantum attackers " +
@ -105,20 +105,19 @@ object Crypto {
)
/** Our default signature scheme if no algorithm is specified (e.g. for key generation). */
private val DEFAULT_SIGNATURE_SCHEME = EDDSA_ED25519_SHA512
val DEFAULT_SIGNATURE_SCHEME = EDDSA_ED25519_SHA512
/**
* Supported digital signature schemes.
* Note: Only the schemes added in this map will be supported (see [Crypto]).
* Do not forget to add the DEFAULT_SIGNATURE_SCHEME as well.
*/
private val supportedSignatureSchemes = mapOf(
RSA_SHA256.schemeCodeName to RSA_SHA256,
ECDSA_SECP256K1_SHA256.schemeCodeName to ECDSA_SECP256K1_SHA256,
ECDSA_SECP256R1_SHA256.schemeCodeName to ECDSA_SECP256R1_SHA256,
EDDSA_ED25519_SHA512.schemeCodeName to EDDSA_ED25519_SHA512,
SPHINCS256_SHA256.schemeCodeName to SPHINCS256_SHA256
)
val supportedSignatureSchemes = listOf(
RSA_SHA256,
ECDSA_SECP256K1_SHA256,
ECDSA_SECP256R1_SHA256,
EDDSA_ED25519_SHA512,
SPHINCS256_SHA256
).associateBy { it.schemeCodeName }
/**
* Factory pattern to retrieve the corresponding [SignatureScheme] based on the type of the [String] input.
@ -128,17 +127,7 @@ object Crypto {
* @return a currently supported SignatureScheme.
* @throws IllegalArgumentException if the requested signature scheme is not supported.
*/
private fun findSignatureScheme(schemeCodeName: String): SignatureScheme = supportedSignatureSchemes[schemeCodeName] ?: throw IllegalArgumentException("Unsupported key/algorithm for metadata schemeCodeName: $schemeCodeName")
/**
* Retrieve the corresponding [SignatureScheme] based on the type of the input [KeyPair].
* Note that only the Corda platform standard schemes are supported (see [Crypto]).
* This function is usually called when requiring to sign signatures.
* @param keyPair a cryptographic [KeyPair].
* @return a currently supported SignatureScheme or null.
* @throws IllegalArgumentException if the requested signature scheme is not supported.
*/
private fun findSignatureScheme(keyPair: KeyPair): SignatureScheme = findSignatureScheme(keyPair.private)
fun findSignatureScheme(schemeCodeName: String): SignatureScheme = supportedSignatureSchemes[schemeCodeName] ?: throw IllegalArgumentException("Unsupported key/algorithm for metadata schemeCodeName: $schemeCodeName")
/**
* Retrieve the corresponding [SignatureScheme] based on the type of the input [Key].
@ -150,9 +139,11 @@ object Crypto {
* @return a currently supported SignatureScheme.
* @throws IllegalArgumentException if the requested key type is not supported.
*/
private fun findSignatureScheme(key: Key): SignatureScheme {
fun findSignatureScheme(key: Key): SignatureScheme {
for (sig in supportedSignatureSchemes.values) {
val algorithm = key.algorithm
var algorithm = key.algorithm
if (algorithm == "EC") algorithm = "ECDSA" // required to read ECC keys from Keystore, because encoding may change algorithm name from ECDSA to EC.
if (algorithm == "SPHINCS-256") algorithm = "SPHINCS256" // because encoding may change algorithm name from SPHINCS256 to SPHINCS-256.
if (algorithm == sig.algorithmName) {
// If more than one ECDSA schemes are supported, we should distinguish between them by checking their curve parameters.
// TODO: change 'continue' to 'break' if only one EdDSA curve will be used.
@ -170,26 +161,18 @@ object Crypto {
throw IllegalArgumentException("Unsupported key/algorithm for the private key: ${key.encoded.toBase58()}")
}
/**
* Retrieve the corresponding signature scheme code name based on the type of the input [Key].
* See [Crypto] for the supported scheme code names.
* @param key either private or public.
* @return signatureSchemeCodeName for a [Key].
* @throws IllegalArgumentException if the requested key type is not supported.
*/
fun findSignatureSchemeCodeName(key: Key): String = findSignatureScheme(key).schemeCodeName
/**
* Decode a PKCS8 encoded key to its [PrivateKey] object.
* Use this method if the key type is a-priori unknown.
* @param encodedKey a PKCS8 encoded private key.
* @throws IllegalArgumentException on not supported scheme or if the given key specification
* is inappropriate for this key factory to produce a private key.
*/
@Throws(IllegalArgumentException::class)
fun decodePrivateKey(encodedKey: ByteArray): PrivateKey {
for ((_, _, _, _, keyFactory) in supportedSignatureSchemes.values) {
for ((_, _, providerName, algorithmName) in supportedSignatureSchemes.values) {
try {
return keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey))
return KeyFactory.getInstance(algorithmName, providerMap[providerName]).generatePrivate(PKCS8EncodedKeySpec(encodedKey))
} catch (ikse: InvalidKeySpecException) {
// ignore it - only used to bypass the scheme that causes an exception.
}
@ -199,17 +182,27 @@ object Crypto {
/**
* Decode a PKCS8 encoded key to its [PrivateKey] object based on the input scheme code name.
* This will be used by Kryo deserialisation.
* @param encodedKey a PKCS8 encoded private key.
* This should be used when the type key is known, e.g. during Kryo deserialisation or with key caches or key managers.
* @param schemeCodeName a [String] that should match a key in supportedSignatureSchemes map (e.g. ECDSA_SECP256K1_SHA256).
* @param encodedKey a PKCS8 encoded private key.
* @throws IllegalArgumentException on not supported scheme or if the given key specification
* is inappropriate for this key factory to produce a private key.
*/
@Throws(IllegalArgumentException::class, InvalidKeySpecException::class)
fun decodePrivateKey(encodedKey: ByteArray, schemeCodeName: String): PrivateKey {
val sig = findSignatureScheme(schemeCodeName)
fun decodePrivateKey(schemeCodeName: String, encodedKey: ByteArray): PrivateKey = decodePrivateKey(findSignatureScheme(schemeCodeName), encodedKey)
/**
* Decode a PKCS8 encoded key to its [PrivateKey] object based on the input scheme code name.
* This should be used when the type key is known, e.g. during Kryo deserialisation or with key caches or key managers.
* @param signatureScheme a signature scheme (e.g. ECDSA_SECP256K1_SHA256).
* @param encodedKey a PKCS8 encoded private key.
* @throws IllegalArgumentException on not supported scheme or if the given key specification
* is inappropriate for this key factory to produce a private key.
*/
@Throws(IllegalArgumentException::class, InvalidKeySpecException::class)
fun decodePrivateKey(signatureScheme: SignatureScheme, encodedKey: ByteArray): PrivateKey {
try {
return sig.keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey))
return KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName]).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)
}
@ -217,16 +210,16 @@ object Crypto {
/**
* Decode an X509 encoded key to its [PublicKey] object.
* Use this method if the key type is a-priori unknown.
* @param encodedKey an X509 encoded public key.
* @throws UnsupportedSchemeException on not supported scheme.
* @throws IllegalArgumentException on not supported scheme or if the given key specification
* is inappropriate for this key factory to produce a private key.
*/
@Throws(IllegalArgumentException::class)
fun decodePublicKey(encodedKey: ByteArray): PublicKey {
for ((_, _, _, _, keyFactory) in supportedSignatureSchemes.values) {
for ((_, _, providerName, algorithmName) in supportedSignatureSchemes.values) {
try {
return keyFactory.generatePublic(X509EncodedKeySpec(encodedKey))
return KeyFactory.getInstance(algorithmName, providerMap[providerName]).generatePublic(X509EncodedKeySpec(encodedKey))
} catch (ikse: InvalidKeySpecException) {
// ignore it - only used to bypass the scheme that causes an exception.
}
@ -236,39 +229,34 @@ object Crypto {
/**
* Decode an X509 encoded key to its [PrivateKey] object based on the input scheme code name.
* This will be used by Kryo deserialisation.
* @param encodedKey an X509 encoded public key.
* This should be used when the type key is known, e.g. during Kryo deserialisation or with key caches or key managers.
* @param schemeCodeName a [String] that should match a key in supportedSignatureSchemes map (e.g. ECDSA_SECP256K1_SHA256).
* @param encodedKey an X509 encoded public key.
* @throws IllegalArgumentException if the requested scheme is not supported
* @throws InvalidKeySpecException if the given key specification
* is inappropriate for this key factory to produce a public key.
*/
@Throws(IllegalArgumentException::class, InvalidKeySpecException::class)
fun decodePublicKey(encodedKey: ByteArray, schemeCodeName: String): PublicKey {
val sig = findSignatureScheme(schemeCodeName)
fun decodePublicKey(schemeCodeName: String, encodedKey: ByteArray): PublicKey = decodePublicKey(findSignatureScheme(schemeCodeName), encodedKey)
/**
* Decode an X509 encoded key to its [PrivateKey] object based on the input scheme code name.
* This should be used when the type key is known, e.g. during Kryo deserialisation or with key caches or key managers.
* @param signatureScheme a signature scheme (e.g. ECDSA_SECP256K1_SHA256).
* @param encodedKey an X509 encoded public key.
* @throws IllegalArgumentException if the requested scheme is not supported
* @throws InvalidKeySpecException if the given key specification
* is inappropriate for this key factory to produce a public key.
*/
@Throws(IllegalArgumentException::class, InvalidKeySpecException::class)
fun decodePublicKey(signatureScheme: SignatureScheme, encodedKey: ByteArray): PublicKey {
try {
return sig.keyFactory.generatePublic(X509EncodedKeySpec(encodedKey))
return KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName]).generatePublic(X509EncodedKeySpec(encodedKey))
} catch (ikse: InvalidKeySpecException) {
throw 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)
}
}
/**
* Utility to simplify the act of generating keys.
* Normally, we don't expect other errors here, assuming that key generation parameters for every supported signature scheme have been unit-tested.
* @param schemeCodeName a signature scheme's code name (e.g. ECDSA_SECP256K1_SHA256).
* @return a KeyPair for the requested scheme.
* @throws IllegalArgumentException if the requested signature scheme is not supported.
*/
@Throws(IllegalArgumentException::class)
fun generateKeyPair(schemeCodeName: String): KeyPair = findSignatureScheme(schemeCodeName).keyPairGenerator.generateKeyPair()
/**
* Generate a KeyPair using the default signature scheme.
* @return a new KeyPair.
*/
fun generateKeyPair(): KeyPair = DEFAULT_SIGNATURE_SCHEME.keyPairGenerator.generateKeyPair()
/**
* Generic way to sign [ByteArray] data with a [PrivateKey]. Strategy on on identifying the actual signing scheme is based
* on the [PrivateKey] type, but if the schemeCodeName is known, then better use doSign(signatureScheme: String, privateKey: PrivateKey, clearData: ByteArray).
@ -280,7 +268,7 @@ object Crypto {
* @throws SignatureException if signing is not possible due to malformed data or private key.
*/
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
fun doSign(privateKey: PrivateKey, clearData: ByteArray) = doSign(findSignatureScheme(privateKey).sig, privateKey, clearData)
fun doSign(privateKey: PrivateKey, clearData: ByteArray) = doSign(findSignatureScheme(privateKey), privateKey, clearData)
/**
* Generic way to sign [ByteArray] data with a [PrivateKey] and a known schemeCodeName [String].
@ -293,11 +281,11 @@ object Crypto {
* @throws SignatureException if signing is not possible due to malformed data or private key.
*/
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
fun doSign(schemeCodeName: String, privateKey: PrivateKey, clearData: ByteArray) = doSign(findSignatureScheme(schemeCodeName).sig, privateKey, clearData)
fun doSign(schemeCodeName: String, privateKey: PrivateKey, clearData: ByteArray) = doSign(findSignatureScheme(schemeCodeName), privateKey, clearData)
/**
* Generic way to sign [ByteArray] data with a [PrivateKey] and a known [Signature].
* @param signature a [Signature] object, retrieved from supported signature schemes, see [Crypto].
* @param signatureScheme a [SignatureScheme] object, retrieved from supported signature schemes, see [Crypto].
* @param privateKey the signer's [PrivateKey].
* @param clearData the data/message to be signed in [ByteArray] form (usually the Merkle root).
* @return the digital signature (in [ByteArray]) on the input message.
@ -306,7 +294,10 @@ object Crypto {
* @throws SignatureException if signing is not possible due to malformed data or private key.
*/
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
private fun doSign(signature: Signature, privateKey: PrivateKey, clearData: ByteArray): ByteArray {
fun doSign(signatureScheme: SignatureScheme, privateKey: PrivateKey, clearData: ByteArray): ByteArray {
if (!supportedSignatureSchemes.containsKey(signatureScheme.schemeCodeName))
throw IllegalArgumentException("Unsupported key/algorithm for schemeCodeName: $signatureScheme.schemeCodeName")
val signature = Signature.getInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName])
if (clearData.isEmpty()) throw Exception("Signing of an empty array is not permitted!")
signature.initSign(privateKey)
signature.update(clearData)
@ -336,6 +327,7 @@ object Crypto {
/**
* Utility to simplify the act of verifying a digital signature.
* It returns true if it succeeds, but it always throws an exception if verification fails.
* @param schemeCodeName a signature scheme's code name (e.g. ECDSA_SECP256K1_SHA256).
* @param publicKey the signer's [PublicKey].
* @param signatureData the signatureData on a message.
* @param clearData the clear data/message that was signed (usually the Merkle root).
@ -347,7 +339,7 @@ object Crypto {
* @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty.
*/
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
fun doVerify(schemeCodeName: String, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray) = doVerify(findSignatureScheme(schemeCodeName).sig, publicKey, signatureData, clearData)
fun doVerify(schemeCodeName: String, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray) = doVerify(findSignatureScheme(schemeCodeName), publicKey, signatureData, clearData)
/**
* Utility to simplify the act of verifying a digital signature by identifying the signature scheme used from the input public key's type.
@ -365,12 +357,12 @@ object Crypto {
* @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty.
*/
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
fun doVerify(publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray) = doVerify(findSignatureScheme(publicKey).sig, publicKey, signatureData, clearData)
fun doVerify(publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray) = doVerify(findSignatureScheme(publicKey), publicKey, signatureData, clearData)
/**
* Method to verify a digital signature.
* It returns true if it succeeds, but it always throws an exception if verification fails.
* @param signature a [Signature] object, retrieved from supported signature schemes, see [Crypto].
* @param signatureScheme a [SignatureScheme] object, retrieved from supported signature schemes, see [Crypto].
* @param publicKey the signer's [PublicKey].
* @param signatureData the signatureData on a message.
* @param clearData the clear data/message that was signed (usually the Merkle root).
@ -379,14 +371,15 @@ object Crypto {
* @throws SignatureException if this signatureData object is not initialized properly,
* the passed-in signatureData is improperly encoded or of the wrong type,
* if this signatureData scheme is unable to process the input data provided, if the verification is not possible.
* @throws IllegalArgumentException if any of the clear or signature data is empty.
* @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty.
*/
private fun doVerify(signature: Signature, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean {
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
fun doVerify(signatureScheme: SignatureScheme, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean {
if (!supportedSignatureSchemes.containsKey(signatureScheme.schemeCodeName))
throw IllegalArgumentException("Unsupported key/algorithm for schemeCodeName: $signatureScheme.schemeCodeName")
if (signatureData.isEmpty()) throw IllegalArgumentException("Signature data is empty!")
if (clearData.isEmpty()) throw IllegalArgumentException("Clear data is empty, nothing to verify!")
signature.initVerify(publicKey)
signature.update(clearData)
val verificationResult = signature.verify(signatureData)
val verificationResult = isValid(signatureScheme, publicKey, signatureData, clearData)
if (verificationResult) {
return true
} else {
@ -413,15 +406,82 @@ object Crypto {
}
/**
* Check if the requested signature scheme is supported by the system.
* @param schemeCodeName a signature scheme's code name (e.g. ECDSA_SECP256K1_SHA256).
* @return true if the signature scheme is supported.
* Utility to simplify the act of verifying a digital signature by identifying the signature scheme used from the input public key's type.
* It returns true if it succeeds and false if not. In comparison to [doVerify] if the key and signature
* do not match it returns false rather than throwing an exception. Normally you should use the function which throws,
* as it avoids the risk of failing to test the result.
* Use this method if the signature scheme is not a-priori known.
* @param publicKey the signer's [PublicKey].
* @param signatureData the signatureData on a message.
* @param clearData the clear data/message that was signed (usually the Merkle root).
* @return true if verification passes or false if verification fails.
* @throws SignatureException if this signatureData object is not initialized properly,
* the passed-in signatureData is improperly encoded or of the wrong type,
* if this signatureData scheme is unable to process the input data provided, if the verification is not possible.
*/
fun isSupportedSignatureScheme(schemeCodeName: String): Boolean = schemeCodeName in supportedSignatureSchemes
@Throws(SignatureException::class)
fun isValid(publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray) = isValid(findSignatureScheme(publicKey), publicKey, signatureData, clearData)
/** @return the default signature scheme's code name. */
fun getDefaultSignatureSchemeCodeName(): String = DEFAULT_SIGNATURE_SCHEME.schemeCodeName
/** @return a [List] of Strings with the scheme code names defined in [SignatureScheme] for all of our supported signature schemes, see [Crypto]. */
fun listSupportedSignatureSchemes(): List<String> = supportedSignatureSchemes.keys.toList()
/**
* Method to verify a digital signature. In comparison to [doVerify] if the key and signature
* do not match it returns false rather than throwing an exception.
* Use this method if the signature scheme type is a-priori unknown.
* @param signatureScheme a [SignatureScheme] object, retrieved from supported signature schemes, see [Crypto].
* @param publicKey the signer's [PublicKey].
* @param signatureData the signatureData on a message.
* @param clearData the clear data/message that was signed (usually the Merkle root).
* @return true if verification passes or false if verification fails.
* @throws SignatureException if this signatureData object is not initialized properly,
* the passed-in signatureData is improperly encoded or of the wrong type,
* if this signatureData scheme is unable to process the input data provided, if the verification is not possible.
* @throws IllegalArgumentException if the requested signature scheme is not supported.
*/
@Throws(SignatureException::class, IllegalArgumentException::class)
fun isValid(signatureScheme: SignatureScheme, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean {
if (!supportedSignatureSchemes.containsKey(signatureScheme.schemeCodeName))
throw IllegalArgumentException("Unsupported key/algorithm for schemeCodeName: $signatureScheme.schemeCodeName")
val signature = Signature.getInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName])
signature.initVerify(publicKey)
signature.update(clearData)
return signature.verify(signatureData)
}
/**
* Utility to simplify the act of generating keys.
* Normally, we don't expect other errors here, assuming that key generation parameters for every supported signature scheme have been unit-tested.
* @param schemeCodeName a signature scheme's code name (e.g. ECDSA_SECP256K1_SHA256).
* @return a KeyPair for the requested signature scheme code name.
* @throws IllegalArgumentException if the requested signature scheme is not supported.
*/
@Throws(IllegalArgumentException::class)
fun generateKeyPair(schemeCodeName: String): KeyPair = generateKeyPair(findSignatureScheme(schemeCodeName))
/**
* Generate a [KeyPair] for the selected [SignatureScheme].
* Note that RSA is the sole algorithm initialized specifically by its supported keySize.
* @param signatureScheme a supported [SignatureScheme], see [Crypto].
* @return a new [KeyPair] for the requested [SignatureScheme].
* @throws IllegalArgumentException if the requested signature scheme is not supported.
*/
@Throws(IllegalArgumentException::class)
fun generateKeyPair(signatureScheme: SignatureScheme): KeyPair {
if (!supportedSignatureSchemes.containsKey(signatureScheme.schemeCodeName))
throw IllegalArgumentException("Unsupported key/algorithm for schemeCodeName: $signatureScheme.schemeCodeName")
val keyPairGenerator = KeyPairGenerator.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName])
if (signatureScheme.algSpec != null)
keyPairGenerator.initialize(signatureScheme.algSpec, newSecureRandom())
else
keyPairGenerator.initialize(signatureScheme.keySize, newSecureRandom())
return keyPairGenerator.generateKeyPair()
}
/**
* Generate a [KeyPair] using the default signature scheme.
* @return a new [KeyPair].
*/
fun generateKeyPair(): KeyPair = generateKeyPair(DEFAULT_SIGNATURE_SCHEME)
/** Check if the requested signature scheme is supported by the system. */
fun isSupportedSignatureScheme(schemeCodeName: String): Boolean = schemeCodeName in supportedSignatureSchemes
fun isSupportedSignatureScheme(signatureScheme: SignatureScheme): Boolean = signatureScheme.schemeCodeName in supportedSignatureSchemes
}

View File

@ -4,11 +4,8 @@ package net.corda.core.crypto
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes
import net.i2p.crypto.eddsa.EdDSAEngine
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import net.i2p.crypto.eddsa.KeyPairGenerator
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
@ -40,34 +37,45 @@ class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {
@CordaSerializable
object NullSignature : DigitalSignature.WithKey(NullPublicKey, ByteArray(32))
/** Utility to simplify the act of signing a byte array */
fun PrivateKey.signWithECDSA(bytes: ByteArray): DigitalSignature {
val signer = EdDSAEngine()
signer.initSign(this)
signer.update(bytes)
val sig = signer.sign()
return DigitalSignature(sig)
/**
* Utility to simplify the act of signing a byte array.
* @param bytesToSign the data/message to be signed in [ByteArray] form (usually the Merkle root).
* @return the [DigitalSignature] object on the input message.
* @throws IllegalArgumentException if the signature scheme is not supported for this private key.
* @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key.
*/
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
fun PrivateKey.sign(bytesToSign: ByteArray): DigitalSignature {
return DigitalSignature(Crypto.doSign(this, bytesToSign))
}
fun PrivateKey.signWithECDSA(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
return DigitalSignature.WithKey(publicKey, signWithECDSA(bytesToSign).bytes)
fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
return DigitalSignature.WithKey(publicKey, this.sign(bytesToSign).bytes)
}
val ed25519Curve: EdDSANamedCurveSpec = EdDSANamedCurveTable.getByName("ED25519")
/**
* Helper function to sign with a key pair.
* @param bytesToSign the data/message to be signed in [ByteArray] form (usually the Merkle root).
* @return the digital signature (in [ByteArray]) on the input message.
* @throws IllegalArgumentException if the signature scheme is not supported for this private key.
* @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key.
*/
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
fun KeyPair.sign(bytesToSign: ByteArray) = private.sign(bytesToSign, public)
fun KeyPair.sign(bytesToSign: OpaqueBytes) = private.sign(bytesToSign.bytes, public)
fun KeyPair.sign(bytesToSign: OpaqueBytes, party: Party) = sign(bytesToSign.bytes, party)
fun KeyPair.signWithECDSA(bytesToSign: ByteArray) = private.signWithECDSA(bytesToSign, public)
fun KeyPair.signWithECDSA(bytesToSign: OpaqueBytes) = private.signWithECDSA(bytesToSign.bytes, public)
fun KeyPair.signWithECDSA(bytesToSign: OpaqueBytes, party: Party) = signWithECDSA(bytesToSign.bytes, party)
// TODO This case will need more careful thinking, as party owningKey can be a CompositeKey. One way of doing that is
// implementation of CompositeSignature.
@Throws(InvalidKeyException::class)
fun KeyPair.signWithECDSA(bytesToSign: ByteArray, party: Party): DigitalSignature.LegallyIdentifiable {
val sig = signWithECDSA(bytesToSign)
fun KeyPair.sign(bytesToSign: ByteArray, party: Party): DigitalSignature.LegallyIdentifiable {
val sig = sign(bytesToSign)
val sigKey = when (party.owningKey) { // Quick workaround when we have CompositeKey as Party owningKey.
is CompositeKey -> throw InvalidKeyException("Signing for parties with CompositeKey not supported.")
else -> party.owningKey
}
sigKey.verifyWithECDSA(bytesToSign, sig)
return DigitalSignature.LegallyIdentifiable(party, sig.bytes)
}
@ -77,18 +85,14 @@ fun KeyPair.signWithECDSA(bytesToSign: ByteArray, party: Party): DigitalSignatur
* @throws InvalidKeyException if the key to verify the signature with is not valid (i.e. wrong key type for the
* signature).
* @throws SignatureException if the signature is invalid (i.e. damaged), or does not match the key (incorrect).
* @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty.
*/
// TODO: SignatureException should be used only for a damaged signature, as per `java.security.Signature.verify()`,
// we should use another exception (perhaps IllegalArgumentException) for indicating the signature is valid but does
// not match.
@Throws(IllegalStateException::class, SignatureException::class)
fun PublicKey.verifyWithECDSA(content: ByteArray, signature: DigitalSignature) {
if (!isValidForECDSA(content, signature))
throw SignatureException("Signature did not match")
}
@Throws(SignatureException::class, IllegalArgumentException::class, InvalidKeyException::class)
fun PublicKey.verify(content: ByteArray, signature: DigitalSignature) = Crypto.doVerify(this, signature.bytes, content)
/**
* Utility to simplify the act of verifying a signature. In comparison to [verifyWithECDSA] if the key and signature
* Utility to simplify the act of verifying a signature. In comparison to [verify] if the key and signature
* do not match it returns false rather than throwing an exception. Normally you should use the function which throws,
* as it avoids the risk of failing to test the result, but this is for uses such as [java.security.Signature.verify]
* implementations.
@ -96,18 +100,14 @@ fun PublicKey.verifyWithECDSA(content: ByteArray, signature: DigitalSignature) {
* @throws InvalidKeyException if the key to verify the signature with is not valid (i.e. wrong key type for the
* signature).
* @throws SignatureException if the signature is invalid (i.e. damaged).
* @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty.
* @return whether the signature is correct for this key.
*/
@Throws(IllegalStateException::class, SignatureException::class)
fun PublicKey.isValidForECDSA(content: ByteArray, signature: DigitalSignature) : Boolean {
val pubKey = when (this) {
is CompositeKey -> throw IllegalStateException("Verification of CompositeKey signatures currently not supported.") // TODO CompositeSignature verification.
else -> this
}
val verifier = EdDSAEngine()
verifier.initVerify(pubKey)
verifier.update(content)
return verifier.verify(signature.bytes)
@Throws(IllegalStateException::class, SignatureException::class, IllegalArgumentException::class)
fun PublicKey.isValid(content: ByteArray, signature: DigitalSignature) : Boolean {
if (this is CompositeKey)
throw IllegalStateException("Verification of CompositeKey signatures currently not supported.") // TODO CompositeSignature verification.
return Crypto.isValid(this, signature.bytes, content)
}
/** Render a public key to a string, using a short form if it's an elliptic curve public key */
@ -148,27 +148,17 @@ fun generateKeyPair(): KeyPair = Crypto.generateKeyPair()
/**
* Returns a key pair derived from the given private key entropy. This is useful for unit tests and other cases where
* you want hard-coded private keys.
* This currently works for EdDSA ED25519 only.
*/
fun entropyToKeyPair(entropy: BigInteger): KeyPair {
val params = ed25519Curve
val params = EdDSANamedCurveTable.getByName("ED25519")
val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8)
val priv = EdDSAPrivateKeySpec(bytes, params)
val pub = EdDSAPublicKeySpec(priv.a, params)
val key = KeyPair(EdDSAPublicKey(pub), EdDSAPrivateKey(priv))
return key
val keyPair = KeyPair(EdDSAPublicKey(pub), EdDSAPrivateKey(priv))
return keyPair
}
/**
* Helper function for signing.
* @param clearData the data/message to be signed in [ByteArray] form (usually the Merkle root).
* @return the digital signature (in [ByteArray]) on the input message.
* @throws IllegalArgumentException if the signature scheme is not supported for this private key.
* @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key.
*/
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
fun PrivateKey.sign(clearData: ByteArray): ByteArray = Crypto.doSign(this, clearData)
/**
* Helper function for signing.
* @param metaData tha attached MetaData object.
@ -180,17 +170,6 @@ fun PrivateKey.sign(clearData: ByteArray): ByteArray = Crypto.doSign(this, clear
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
fun PrivateKey.sign(metaData: MetaData): TransactionSignature = Crypto.doSign(this, metaData)
/**
* Helper function to sign with a key pair.
* @param clearData the data/message to be signed in [ByteArray] form (usually the Merkle root).
* @return the digital signature (in [ByteArray]) on the input message.
* @throws IllegalArgumentException if the signature scheme is not supported for this private key.
* @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key.
*/
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
fun KeyPair.sign(clearData: ByteArray): ByteArray = Crypto.doSign(this.private, clearData)
/**
* Helper function to verify a signature.
* @param signatureData the signature on a message.

View File

@ -22,7 +22,7 @@ open class DigitalSignature(bits: ByteArray) : OpaqueBytes(bits) {
* @throws SignatureException if the signature is invalid (i.e. damaged), or does not match the key (incorrect).
*/
@Throws(InvalidKeyException::class, SignatureException::class)
fun verifyWithECDSA(content: ByteArray) = by.verifyWithECDSA(content, this)
fun verify(content: ByteArray) = by.verify(content, this)
/**
* Utility to simplify the act of verifying a signature.
*
@ -31,9 +31,9 @@ open class DigitalSignature(bits: ByteArray) : OpaqueBytes(bits) {
* @throws SignatureException if the signature is invalid (i.e. damaged), or does not match the key (incorrect).
*/
@Throws(InvalidKeyException::class, SignatureException::class)
fun verifyWithECDSA(content: OpaqueBytes) = by.verifyWithECDSA(content.bytes, this)
fun verify(content: OpaqueBytes) = by.verify(content.bytes, this)
/**
* Utility to simplify the act of verifying a signature. In comparison to [verifyWithECDSA] doesn't throw an
* Utility to simplify the act of verifying a signature. In comparison to [verify] doesn't throw an
* exception, making it more suitable where a boolean is required, but normally you should use the function
* which throws, as it avoids the risk of failing to test the result.
*
@ -43,7 +43,7 @@ open class DigitalSignature(bits: ByteArray) : OpaqueBytes(bits) {
* @return whether the signature is correct for this key.
*/
@Throws(InvalidKeyException::class, SignatureException::class)
fun isValidForECDSA(content: ByteArray) = by.isValidForECDSA(content, this)
fun isValid(content: ByteArray) = by.isValid(content, this)
}
// TODO: consider removing this as whoever needs to identify the signer should be able to derive it from the public key

View File

@ -1,34 +0,0 @@
package net.corda.core.crypto
import java.security.Provider
/**
* A simple I2P [Provider] that supports the EdDSA signature scheme.
*/
class I2PProvider : Provider("I2P", 0.1, "I2P Security Provider v0.1, implementing I2P's EdDSA 25519.") {
init { setup() }
private fun setup() {
// Key OID: 1.3.101.100; Sig OID: 1.3.101.101
put("KeyFactory.EdDSA", "net.i2p.crypto.eddsa.KeyFactory")
put("KeyPairGenerator.EdDSA", "net.i2p.crypto.eddsa.KeyPairGenerator")
put("Signature.SHA512withEdDSA", "net.i2p.crypto.eddsa.EdDSAEngine")
// without these, Certificate.verify() fails.
put("Alg.Alias.KeyFactory.1.3.101.100", "EdDSA")
put("Alg.Alias.KeyFactory.OID.1.3.101.100", "EdDSA")
// Without these, keytool fails with:
// keytool error: java.security.NoSuchAlgorithmException: unrecognized algorithm name: SHA512withEdDSA.
put("Alg.Alias.KeyPairGenerator.1.3.101.100", "EdDSA")
put("Alg.Alias.KeyPairGenerator.OID.1.3.101.100", "EdDSA")
// with this setting, keytool's keygen doesn't work.
// java.security.cert.CertificateException: Signature algorithm mismatch.
// It must match the key setting (1.3.101.100) to work,
// but this works fine with programmatic cert generation.
put("Alg.Alias.Signature.1.3.101.101", "SHA512withEdDSA")
put("Alg.Alias.Signature.OID.1.3.101.101", "SHA512withEdDSA")
}
}

View File

@ -1,7 +1,5 @@
package net.corda.core.crypto
import java.security.KeyFactory
import java.security.KeyPairGeneratorSpi
import java.security.Signature
import java.security.spec.AlgorithmParameterSpec
@ -9,12 +7,9 @@ import java.security.spec.AlgorithmParameterSpec
* This class is used to define a digital signature scheme.
* @param schemeNumberID we assign a number ID for more efficient on-wire serialisation. Please ensure uniqueness between schemes.
* @param schemeCodeName code name for this signature scheme (e.g. RSA_SHA256, ECDSA_SECP256K1_SHA256, ECDSA_SECP256R1_SHA256, EDDSA_ED25519_SHA512, SPHINCS-256_SHA512).
* @param providerName the provider's name (e.g. "BC").
* @param algorithmName which signature algorithm is used (e.g. RSA, ECDSA. EdDSA, SPHINCS-256).
* @param sig the [Signature] class that provides the functionality of a digital signature scheme.
* eg. Signature.getInstance("SHA256withECDSA", "BC").
* @param keyFactory the KeyFactory for this scheme (e.g. KeyFactory.getInstance("RSA", "BC")).
* @param keyPairGenerator defines the <i>Service Provider Interface</i> (<b>SPI</b>) for the {@code KeyPairGenerator} class.
* e.g. KeyPairGenerator.getInstance("ECDSA", "BC").
* @param signatureName a signature-scheme name as required to create [Signature] objects (e.g. "SHA256withECDSA")
* @param algSpec parameter specs for the underlying algorithm. Note that RSA is defined by the key size rather than algSpec.
* eg. ECGenParameterSpec("secp256k1").
* @param keySize the private key size (currently used for RSA only).
@ -23,22 +18,9 @@ import java.security.spec.AlgorithmParameterSpec
data class SignatureScheme(
val schemeNumberID: Int,
val schemeCodeName: String,
val providerName: String,
val algorithmName: String,
val sig: Signature,
val keyFactory: KeyFactory,
val keyPairGenerator: KeyPairGeneratorSpi,
val signatureName: String,
val algSpec: AlgorithmParameterSpec?,
val keySize: Int,
val desc: String) {
/**
* KeyPair generators are always initialized once we create them, as no re-initialization is required.
* Note that RSA is the sole algorithm initialized specifically by its supported keySize.
*/
init {
if (algSpec != null)
keyPairGenerator.initialize(algSpec, newSecureRandom())
else
keyPairGenerator.initialize(keySize, newSecureRandom())
}
}
val desc: String)

View File

@ -22,7 +22,7 @@ open class SignedData<T : Any>(val raw: SerializedBytes<T>, val sig: DigitalSign
*/
@Throws(SignatureException::class)
fun verified(): T {
sig.by.verifyWithECDSA(raw.bytes, sig)
sig.by.verify(raw.bytes, sig)
val data = raw.deserialize()
verifyData(data)
return data

View File

@ -18,6 +18,12 @@ import net.corda.core.utilities.NonEmptySetSerializer
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import org.bouncycastle.asn1.x500.X500Name
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.strategy.StdInstantiatorStrategy
import org.slf4j.Logger
import java.io.BufferedInputStream
@ -93,6 +99,13 @@ object DefaultKryoCustomizer {
register(X500Name::class.java, X500NameSerializer)
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)
val customization = KryoSerializationCustomization(this)
pluginRegistries.forEach { it.customizeSerialization(customization) }
}

View File

@ -12,6 +12,7 @@ import net.corda.core.node.AttachmentsClassLoader
import net.corda.core.transactions.WireTransaction
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
import org.bouncycastle.asn1.ASN1InputStream
@ -22,6 +23,7 @@ import java.io.*
import java.lang.reflect.InvocationTargetException
import java.nio.file.Files
import java.nio.file.Path
import java.security.PrivateKey
import java.security.PublicKey
import java.security.spec.InvalidKeySpecException
import java.time.Instant
@ -340,13 +342,13 @@ object WireTransactionSerializer : Serializer<WireTransaction>() {
@ThreadSafe
object Ed25519PrivateKeySerializer : Serializer<EdDSAPrivateKey>() {
override fun write(kryo: Kryo, output: Output, obj: EdDSAPrivateKey) {
check(obj.params == ed25519Curve)
check(obj.params == Crypto.EDDSA_ED25519_SHA512.algSpec )
output.writeBytesWithLength(obj.seed)
}
override fun read(kryo: Kryo, input: Input, type: Class<EdDSAPrivateKey>): EdDSAPrivateKey {
val seed = input.readBytesWithLength()
return EdDSAPrivateKey(EdDSAPrivateKeySpec(seed, ed25519Curve))
return EdDSAPrivateKey(EdDSAPrivateKeySpec(seed, Crypto.EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec))
}
}
@ -354,13 +356,13 @@ object Ed25519PrivateKeySerializer : Serializer<EdDSAPrivateKey>() {
@ThreadSafe
object Ed25519PublicKeySerializer : Serializer<EdDSAPublicKey>() {
override fun write(kryo: Kryo, output: Output, obj: EdDSAPublicKey) {
check(obj.params == ed25519Curve)
check(obj.params == Crypto.EDDSA_ED25519_SHA512.algSpec)
output.writeBytesWithLength(obj.abyte)
}
override fun read(kryo: Kryo, input: Input, type: Class<EdDSAPublicKey>): EdDSAPublicKey {
val A = input.readBytesWithLength()
return EdDSAPublicKey(EdDSAPublicKeySpec(A, ed25519Curve))
return EdDSAPublicKey(EdDSAPublicKeySpec(A, Crypto.EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec))
}
}
@ -382,6 +384,31 @@ object CompositeKeySerializer : Serializer<CompositeKey>() {
}
}
@ThreadSafe
object PrivateKeySerializer : Serializer<PrivateKey>() {
override fun write(kryo: Kryo, output: Output, obj: PrivateKey) {
output.writeBytesWithLength(obj.encoded)
}
override fun read(kryo: Kryo, input: Input, type: Class<PrivateKey>): PrivateKey {
val A = input.readBytesWithLength()
return Crypto.decodePrivateKey(A)
}
}
/** For serialising a public key */
@ThreadSafe
object PublicKeySerializer : Serializer<PublicKey>() {
override fun write(kryo: Kryo, output: Output, obj: PublicKey) {
output.writeBytesWithLength(obj.encoded)
}
override fun read(kryo: Kryo, input: Input, type: Class<PublicKey>): PublicKey {
val A = input.readBytesWithLength()
return Crypto.decodePublicKey(A)
}
}
/**
* Helper function for reading lists with number of elements at the beginning.
* @param minLen minimum number of elements we expect for list to include, defaults to 1
@ -525,7 +552,7 @@ object MetaDataSerializer : Serializer<MetaData>() {
val visibleInputs = kryo.readClassAndObject(input) as BitSet?
val signedInputs = kryo.readClassAndObject(input) as BitSet?
val merkleRoot = input.readBytesWithLength()
val publicKey = Crypto.decodePublicKey(input.readBytesWithLength(), schemeCodeName)
val publicKey = Crypto.decodePublicKey(schemeCodeName, input.readBytesWithLength())
return MetaData(schemeCodeName, versionID, signatureType, timestamp, visibleInputs, signedInputs, merkleRoot, publicKey)
}
}

View File

@ -7,7 +7,7 @@ import net.corda.core.node.ServiceHub
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.isFulfilledBy
import net.corda.core.crypto.signWithECDSA
import net.corda.core.crypto.sign
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializedBytes
import java.security.KeyPair
@ -93,7 +93,7 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
@Throws(SignatureException::class)
fun checkSignaturesAreValid() {
for (sig in sigs) {
sig.verifyWithECDSA(id.bytes)
sig.verify(id.bytes)
}
}
@ -153,7 +153,7 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
*
* @return a digital signature of the transaction.
*/
fun signWithECDSA(keyPair: KeyPair) = keyPair.signWithECDSA(this.id.bytes)
fun signWithECDSA(keyPair: KeyPair) = keyPair.sign(this.id.bytes)
override fun toString(): String = "${javaClass.simpleName}(id=$id)"
}

View File

@ -97,7 +97,7 @@ open class TransactionBuilder(
fun signWith(key: KeyPair): TransactionBuilder {
check(currentSigs.none { it.by == key.public }) { "This partial transaction was already signed by ${key.public}" }
val data = toWireTransaction().id
addSignatureUnchecked(key.signWithECDSA(data.bytes))
addSignatureUnchecked(key.sign(data.bytes))
return this
}
@ -121,7 +121,7 @@ open class TransactionBuilder(
*/
fun checkSignature(sig: DigitalSignature.WithKey) {
require(commands.any { it.signers.any { sig.by in it.keys } }) { "Signature key doesn't match any command" }
sig.verifyWithECDSA(toWireTransaction().id)
sig.verify(toWireTransaction().id)
}
/** Adds the signature directly to the transaction, without checking it for validity. */

View File

@ -98,7 +98,7 @@ abstract class AbstractStateReplacementFlow {
val response = sendAndReceive<DigitalSignature.WithKey>(party, proposal)
return response.unwrap {
check(party.owningKey.isFulfilledBy(it.by)) { "Not signed by the required participant" }
it.verifyWithECDSA(stx.id)
it.verify(stx.id)
it
}
}
@ -157,7 +157,7 @@ abstract class AbstractStateReplacementFlow {
// TODO: This step should not be necessary, as signatures are re-checked in verifySignatures.
val allSignatures = swapSignatures.unwrap { signatures ->
signatures.forEach { it.verifyWithECDSA(stx.id) }
signatures.forEach { it.verify(stx.id) }
signatures
}
@ -187,7 +187,7 @@ abstract class AbstractStateReplacementFlow {
private fun sign(stx: SignedTransaction): DigitalSignature.WithKey {
val myKey = serviceHub.legalIdentityKey
return myKey.signWithECDSA(stx.id)
return myKey.sign(stx.id)
}
}
}

View File

@ -77,7 +77,7 @@ object NotaryFlow {
private fun validateSignature(sig: DigitalSignature.WithKey, data: ByteArray) {
check(sig.by in notaryParty.owningKey.keys) { "Invalid signer for the notary result" }
sig.verifyWithECDSA(data)
sig.verify(data)
}
}
@ -142,7 +142,7 @@ object NotaryFlow {
private fun sign(bits: ByteArray): DigitalSignature.WithKey {
val mySigningKey = serviceHub.notaryIdentityKey
return mySigningKey.signWithECDSA(bits)
return mySigningKey.sign(bits)
}
private fun notaryException(txId: SecureHash, e: UniquenessException): NotaryException {

View File

@ -144,7 +144,7 @@ object TwoPartyDealFlow {
open fun computeOurSignature(partialTX: SignedTransaction): DigitalSignature.WithKey {
progressTracker.currentStep = SIGNING
return myKeyPair.signWithECDSA(partialTX.id)
return myKeyPair.sign(partialTX.id)
}
@Suspendable

View File

@ -5,7 +5,7 @@ import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.signWithECDSA
import net.corda.core.crypto.sign
import net.corda.core.serialization.SerializedBytes
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction
@ -26,7 +26,7 @@ class TransactionTests {
private fun makeSigned(wtx: WireTransaction, vararg keys: KeyPair): SignedTransaction {
val bytes: SerializedBytes<WireTransaction> = wtx.serialized
return SignedTransaction(bytes, keys.map { it.signWithECDSA(wtx.id.bytes) })
return SignedTransaction(bytes, keys.map { it.sign(wtx.id.bytes) })
}
@Test

View File

@ -4,7 +4,6 @@ import net.corda.core.serialization.OpaqueBytes
import net.corda.core.serialization.serialize
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertTrue
@ -19,9 +18,9 @@ class CompositeKeyTests {
val message = OpaqueBytes("Transaction".toByteArray())
val aliceSignature = aliceKey.signWithECDSA(message)
val bobSignature = bobKey.signWithECDSA(message)
val charlieSignature = charlieKey.signWithECDSA(message)
val aliceSignature = aliceKey.sign(message)
val bobSignature = bobKey.sign(message)
val charlieSignature = charlieKey.sign(message)
val compositeAliceSignature = CompositeSignaturesWithKeys(listOf(aliceSignature))
@Test

View File

@ -11,9 +11,6 @@ import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey
import org.junit.Assert.assertNotEquals
import org.junit.Test
import java.security.KeyFactory
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
import java.util.*
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
@ -32,11 +29,11 @@ class CryptoUtilsTest {
@Test
fun `Generate key pairs`() {
// testing supported algorithms
val rsaKeyPair = Crypto.generateKeyPair("RSA_SHA256")
val ecdsaKKeyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
val ecdsaRKeyPair = Crypto.generateKeyPair("ECDSA_SECP256R1_SHA256")
val eddsaKeyPair = Crypto.generateKeyPair("EDDSA_ED25519_SHA512")
val sphincsKeyPair = Crypto.generateKeyPair("SPHINCS-256_SHA512")
val rsaKeyPair = Crypto.generateKeyPair(Crypto.RSA_SHA256)
val ecdsaKKeyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val ecdsaRKeyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val eddsaKeyPair = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
val sphincsKeyPair = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
// not null private keys
assertNotNull(rsaKeyPair.private)
@ -65,17 +62,16 @@ class CryptoUtilsTest {
@Test
fun `RSA full process keygen-sign-verify`() {
val keyPair = Crypto.generateKeyPair("RSA_SHA256")
val keyPair = Crypto.generateKeyPair(Crypto.RSA_SHA256)
val (privKey, pubKey) = keyPair
// test for some data
val signedData = keyPair.sign(testBytes)
val verification = keyPair.verify(signedData, testBytes)
val signedData = Crypto.doSign(privKey, testBytes)
val verification = Crypto.doVerify(pubKey, signedData, testBytes)
assertTrue(verification)
// test for empty data signing
try {
keyPair.sign(ByteArray(0))
Crypto.doSign(privKey, ByteArray(0))
fail()
} catch (e: Exception) {
// expected
@ -83,7 +79,7 @@ class CryptoUtilsTest {
// test for empty source data when verifying
try {
keyPair.verify(testBytes, ByteArray(0))
Crypto.doVerify(pubKey, testBytes, ByteArray(0))
fail()
} catch (e: Exception) {
// expected
@ -91,88 +87,83 @@ class CryptoUtilsTest {
// test for empty signed data when verifying
try {
keyPair.verify(ByteArray(0), testBytes)
Crypto.doVerify(pubKey, ByteArray(0), testBytes)
fail()
} catch (e: Exception) {
// expected
}
// test for zero bytes data
val signedDataZeros = keyPair.sign(ByteArray(100))
val verificationZeros = keyPair.verify(signedDataZeros, ByteArray(100))
val signedDataZeros = Crypto.doSign(privKey, ByteArray(100))
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, ByteArray(100))
assertTrue(verificationZeros)
// test for 1MB of data (I successfully tested it locally for 1GB as well)
val MBbyte = ByteArray(1000000) // 1.000.000
Random().nextBytes(MBbyte)
val signedDataBig = keyPair.sign(MBbyte)
val verificationBig = keyPair.verify(signedDataBig, MBbyte)
assertTrue(verificationBig)
// test on malformed signatures (even if they change for 1 bit)
for (i in 0..signedData.size - 1) {
val b = signedData[i]
signedData[i] = b.inc()
try {
keyPair.verify(signedData, testBytes)
fail()
} catch (e: Exception) {
// expected
}
signedData[i] = b.dec()
}
}
@Test
fun `ECDSA secp256k1 full process keygen-sign-verify`() {
val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
// test for some data
val signedData = keyPair.sign(testBytes)
val verification = keyPair.verify(signedData, testBytes)
assertTrue(verification)
// test for empty data signing
try {
keyPair.sign(ByteArray(0))
fail()
} catch (e: Exception) {
// expected
}
// test for empty source data when verifying
try {
keyPair.verify(testBytes, ByteArray(0))
fail()
} catch (e: Exception) {
// expected
}
// test for empty signed data when verifying
try {
keyPair.verify(ByteArray(0), testBytes)
fail()
} catch (e: Exception) {
// expected
}
// test for zero bytes data
val signedDataZeros = keyPair.sign(ByteArray(100))
val verificationZeros = keyPair.verify(signedDataZeros, ByteArray(100))
assertTrue(verificationZeros)
// test for 1MB of data (I successfully tested it locally for 1GB as well)
val MBbyte = ByteArray(1000000) // 1.000.000
Random().nextBytes(MBbyte)
val signedDataBig = keyPair.sign(MBbyte)
val verificationBig = keyPair.verify(signedDataBig, MBbyte)
val signedDataBig = Crypto.doSign(privKey, MBbyte)
val verificationBig = Crypto.doVerify(pubKey, signedDataBig, MBbyte)
assertTrue(verificationBig)
// test on malformed signatures (even if they change for 1 bit)
signedData[0] = signedData[0].inc()
try {
keyPair.verify(signedData, testBytes)
Crypto.doVerify(pubKey, signedData, testBytes)
fail()
} catch (e: Exception) {
// expected
}
}
@Test
fun `ECDSA secp256k1 full process keygen-sign-verify`() {
val keyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val (privKey, pubKey) = keyPair
// test for some data
val signedData = Crypto.doSign(privKey, testBytes)
val verification = Crypto.doVerify(pubKey, signedData, testBytes)
assertTrue(verification)
// test for empty data signing
try {
Crypto.doSign(privKey, ByteArray(0))
fail()
} catch (e: Exception) {
// expected
}
// test for empty source data when verifying
try {
Crypto.doVerify(pubKey, testBytes, ByteArray(0))
fail()
} catch (e: Exception) {
// expected
}
// test for empty signed data when verifying
try {
Crypto.doVerify(pubKey, ByteArray(0), testBytes)
fail()
} catch (e: Exception) {
// expected
}
// test for zero bytes data
val signedDataZeros = Crypto.doSign(privKey, ByteArray(100))
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, ByteArray(100))
assertTrue(verificationZeros)
// test for 1MB of data (I successfully tested it locally for 1GB as well)
val MBbyte = ByteArray(1000000) // 1.000.000
Random().nextBytes(MBbyte)
val signedDataBig = Crypto.doSign(privKey, MBbyte)
val verificationBig = Crypto.doVerify(pubKey, signedDataBig, MBbyte)
assertTrue(verificationBig)
// test on malformed signatures (even if they change for 1 bit)
signedData[0] = signedData[0].inc()
try {
Crypto.doVerify(pubKey, signedData, testBytes)
fail()
} catch (e: Exception) {
// expected
@ -181,17 +172,16 @@ class CryptoUtilsTest {
@Test
fun `ECDSA secp256r1 full process keygen-sign-verify`() {
val keyPair = Crypto.generateKeyPair("ECDSA_SECP256R1_SHA256")
val keyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val (privKey, pubKey) = keyPair
// test for some data
val signedData = keyPair.sign(testBytes)
val verification = keyPair.verify(signedData, testBytes)
val signedData = Crypto.doSign(privKey, testBytes)
val verification = Crypto.doVerify(pubKey, signedData, testBytes)
assertTrue(verification)
// test for empty data signing
try {
keyPair.sign(ByteArray(0))
Crypto.doSign(privKey, ByteArray(0))
fail()
} catch (e: Exception) {
// expected
@ -199,7 +189,7 @@ class CryptoUtilsTest {
// test for empty source data when verifying
try {
keyPair.verify(testBytes, ByteArray(0))
Crypto.doVerify(pubKey, testBytes, ByteArray(0))
fail()
} catch (e: Exception) {
// expected
@ -207,28 +197,28 @@ class CryptoUtilsTest {
// test for empty signed data when verifying
try {
keyPair.verify(ByteArray(0), testBytes)
Crypto.doVerify(pubKey, ByteArray(0), testBytes)
fail()
} catch (e: Exception) {
// expected
}
// test for zero bytes data
val signedDataZeros = keyPair.sign(ByteArray(100))
val verificationZeros = keyPair.verify(signedDataZeros, ByteArray(100))
val signedDataZeros = Crypto.doSign(privKey, ByteArray(100))
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, ByteArray(100))
assertTrue(verificationZeros)
// test for 1MB of data (I successfully tested it locally for 1GB as well)
val MBbyte = ByteArray(1000000) // 1.000.000
Random().nextBytes(MBbyte)
val signedDataBig = keyPair.sign(MBbyte)
val verificationBig = keyPair.verify(signedDataBig, MBbyte)
val signedDataBig = Crypto.doSign(privKey, MBbyte)
val verificationBig = Crypto.doVerify(pubKey, signedDataBig, MBbyte)
assertTrue(verificationBig)
// test on malformed signatures (even if they change for 1 bit)
signedData[0] = signedData[0].inc()
try {
keyPair.verify(signedData, testBytes)
Crypto.doVerify(pubKey, signedData, testBytes)
fail()
} catch (e: Exception) {
// expected
@ -237,17 +227,16 @@ class CryptoUtilsTest {
@Test
fun `EDDSA ed25519 full process keygen-sign-verify`() {
val keyPair = Crypto.generateKeyPair("EDDSA_ED25519_SHA512")
val keyPair = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
val (privKey, pubKey) = keyPair
// test for some data
val signedData = keyPair.sign(testBytes)
val verification = keyPair.verify(signedData, testBytes)
val signedData = Crypto.doSign(privKey, testBytes)
val verification = Crypto.doVerify(pubKey, signedData, testBytes)
assertTrue(verification)
// test for empty data signing
try {
keyPair.sign(ByteArray(0))
Crypto.doSign(privKey, ByteArray(0))
fail()
} catch (e: Exception) {
// expected
@ -255,7 +244,7 @@ class CryptoUtilsTest {
// test for empty source data when verifying
try {
keyPair.verify(testBytes, ByteArray(0))
Crypto.doVerify(pubKey, testBytes, ByteArray(0))
fail()
} catch (e: Exception) {
// expected
@ -263,28 +252,28 @@ class CryptoUtilsTest {
// test for empty signed data when verifying
try {
keyPair.verify(ByteArray(0), testBytes)
Crypto.doVerify(pubKey, ByteArray(0), testBytes)
fail()
} catch (e: Exception) {
// expected
}
// test for zero bytes data
val signedDataZeros = keyPair.sign(ByteArray(100))
val verificationZeros = keyPair.verify(signedDataZeros, ByteArray(100))
val signedDataZeros = Crypto.doSign(privKey, ByteArray(100))
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, ByteArray(100))
assertTrue(verificationZeros)
// test for 1MB of data (I successfully tested it locally for 1GB as well)
val MBbyte = ByteArray(1000000) // 1.000.000
Random().nextBytes(MBbyte)
val signedDataBig = keyPair.sign(MBbyte)
val verificationBig = keyPair.verify(signedDataBig, MBbyte)
val signedDataBig = Crypto.doSign(privKey, MBbyte)
val verificationBig = Crypto.doVerify(pubKey, signedDataBig, MBbyte)
assertTrue(verificationBig)
// test on malformed signatures (even if they change for 1 bit)
signedData[0] = signedData[0].inc()
try {
keyPair.verify(signedData, testBytes)
Crypto.doVerify(pubKey, signedData, testBytes)
fail()
} catch (e: Exception) {
// expected
@ -293,17 +282,16 @@ class CryptoUtilsTest {
@Test
fun `SPHINCS-256 full process keygen-sign-verify`() {
val keyPair = Crypto.generateKeyPair("SPHINCS-256_SHA512")
val keyPair = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
val (privKey, pubKey) = keyPair
// test for some data
val signedData = keyPair.sign(testBytes)
val verification = keyPair.verify(signedData, testBytes)
val signedData = Crypto.doSign(privKey, testBytes)
val verification = Crypto.doVerify(pubKey, signedData, testBytes)
assertTrue(verification)
// test for empty data signing
try {
keyPair.sign(ByteArray(0))
Crypto.doSign(privKey, ByteArray(0))
fail()
} catch (e: Exception) {
// expected
@ -311,7 +299,7 @@ class CryptoUtilsTest {
// test for empty source data when verifying
try {
keyPair.verify(testBytes, ByteArray(0))
Crypto.doVerify(pubKey, testBytes, ByteArray(0))
fail()
} catch (e: Exception) {
// expected
@ -319,28 +307,28 @@ class CryptoUtilsTest {
// test for empty signed data when verifying
try {
keyPair.verify(ByteArray(0), testBytes)
Crypto.doVerify(pubKey, ByteArray(0), testBytes)
fail()
} catch (e: Exception) {
// expected
}
// test for zero bytes data
val signedDataZeros = keyPair.sign(ByteArray(100))
val verificationZeros = keyPair.verify(signedDataZeros, ByteArray(100))
val signedDataZeros = Crypto.doSign(privKey, ByteArray(100))
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, ByteArray(100))
assertTrue(verificationZeros)
// test for 1MB of data (I successfully tested it locally for 1GB as well)
val MBbyte = ByteArray(1000000) // 1.000.000
Random().nextBytes(MBbyte)
val signedDataBig = keyPair.sign(MBbyte)
val verificationBig = keyPair.verify(signedDataBig, MBbyte)
val signedDataBig = Crypto.doSign(privKey, MBbyte)
val verificationBig = Crypto.doVerify(pubKey, signedDataBig, MBbyte)
assertTrue(verificationBig)
// test on malformed signatures (even if they change for 1 bit)
signedData[0] = signedData[0].inc()
try {
keyPair.verify(signedData, testBytes)
Crypto.doVerify(pubKey, signedData, testBytes)
fail()
} catch (e: Exception) {
// expected
@ -350,7 +338,7 @@ class CryptoUtilsTest {
// test list of supported algorithms
@Test
fun `Check supported algorithms`() {
val algList: List<String> = Crypto.listSupportedSignatureSchemes()
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")
assertTrue { Sets.symmetricDifference(expectedAlgSet, algList.toSet()).isEmpty(); }
}
@ -359,88 +347,76 @@ class CryptoUtilsTest {
@Test
fun `RSA encode decode keys - required for serialization`() {
// Generate key pair.
val keyPair = Crypto.generateKeyPair("RSA_SHA256")
val keyPair = Crypto.generateKeyPair(Crypto.RSA_SHA256)
val (privKey, pubKey) = keyPair
val keyFactory = KeyFactory.getInstance("RSA", "BC")
// Encode and decode private key.
val privKey2 = keyFactory.generatePrivate(PKCS8EncodedKeySpec(privKey.encoded))
val privKey2 = Crypto.decodePrivateKey(privKey.encoded)
assertEquals(privKey2, privKey)
// Encode and decode public key.
val pubKey2 = keyFactory.generatePublic(X509EncodedKeySpec(pubKey.encoded))
val pubKey2 = Crypto.decodePublicKey(pubKey.encoded)
assertEquals(pubKey2, pubKey)
}
@Test
fun `ECDSA secp256k1 encode decode keys - required for serialization`() {
// Generate key pair.
val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
val keyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val (privKey, pubKey) = keyPair
val kf = KeyFactory.getInstance("ECDSA", "BC")
// Encode and decode private key.
val privKey2 = kf.generatePrivate(PKCS8EncodedKeySpec(privKey.encoded))
val privKey2 = Crypto.decodePrivateKey(privKey.encoded)
assertEquals(privKey2, privKey)
// Encode and decode public key.
val pubKey2 = kf.generatePublic(X509EncodedKeySpec(pubKey.encoded))
val pubKey2 = Crypto.decodePublicKey(pubKey.encoded)
assertEquals(pubKey2, pubKey)
}
@Test
fun `ECDSA secp256r1 encode decode keys - required for serialization`() {
// Generate key pair.
val keyPair = Crypto.generateKeyPair("ECDSA_SECP256R1_SHA256")
val keyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val (privKey, pubKey) = keyPair
val kf = KeyFactory.getInstance("ECDSA", "BC")
// Encode and decode private key.
val privKey2 = kf.generatePrivate(PKCS8EncodedKeySpec(privKey.encoded))
val privKey2 = Crypto.decodePrivateKey(privKey.encoded)
assertEquals(privKey2, privKey)
// Encode and decode public key.
val pubKey2 = kf.generatePublic(X509EncodedKeySpec(pubKey.encoded))
val pubKey2 = Crypto.decodePublicKey(pubKey.encoded)
assertEquals(pubKey2, pubKey)
}
@Test
fun `EdDSA encode decode keys - required for serialization`() {
// Generate key pair.
val keyPair = Crypto.generateKeyPair("EDDSA_ED25519_SHA512")
val keyPair = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
val (privKey, pubKey) = keyPair
val kf = KeyFactory.getInstance("EDDSA", "I2P")
// Encode and decode private key.
val privKey2 = kf.generatePrivate(PKCS8EncodedKeySpec(privKey.encoded))
val privKey2 = Crypto.decodePrivateKey(privKey.encoded)
assertEquals(privKey2, privKey)
// Encode and decode public key.
val pubKey2 = kf.generatePublic(X509EncodedKeySpec(pubKey.encoded))
val pubKey2 = Crypto.decodePublicKey(pubKey.encoded)
assertEquals(pubKey2, pubKey)
}
@Test
fun `SPHINCS-256 encode decode keys - required for serialization`() {
// Generate key pair.
val keyPair = Crypto.generateKeyPair("SPHINCS-256_SHA512")
val keyPair = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
val privKey: BCSphincs256PrivateKey = keyPair.private as BCSphincs256PrivateKey
val pubKey: BCSphincs256PublicKey = keyPair.public as BCSphincs256PublicKey
//1st method for encoding/decoding
val keyFactory = KeyFactory.getInstance("SPHINCS256", "BCPQC")
// Encode and decode private key.
val privKey2 = keyFactory.generatePrivate(PKCS8EncodedKeySpec(privKey.encoded))
val privKey2 = Crypto.decodePrivateKey(privKey.encoded)
assertEquals(privKey2, privKey)
// Encode and decode public key.
val pubKey2 = keyFactory.generatePublic(X509EncodedKeySpec(pubKey.encoded))
val pubKey2 = Crypto.decodePublicKey(pubKey.encoded)
assertEquals(pubKey2, pubKey)
//2nd method for encoding/decoding
@ -453,14 +429,14 @@ class CryptoUtilsTest {
// Encode and decode public key.
val pubKeyInfo: SubjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(pubKey.encoded)
val extractedPubKey = BCSphincs256PublicKey(pubKeyInfo)
val decodedPubKey = BCSphincs256PublicKey(pubKeyInfo)
// Check that decoded private key is equal to the initial one.
assertEquals(extractedPubKey, pubKey)
assertEquals(decodedPubKey, pubKey)
}
@Test
fun `RSA scheme finder by key type`() {
val keyPairRSA = Crypto.generateKeyPair("RSA_SHA256")
val keyPairRSA = Crypto.generateKeyPair(Crypto.RSA_SHA256)
val (privRSA, pubRSA) = keyPairRSA
assertEquals(privRSA.algorithm, "RSA")
assertEquals(pubRSA.algorithm, "RSA")
@ -468,23 +444,22 @@ class CryptoUtilsTest {
@Test
fun `ECDSA secp256k1 scheme finder by key type`() {
val keyPairK1 = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
val (privK1, pubK1) = keyPairK1
val keyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val (privKey, pubKey) = keyPair
// Encode and decode keys as they would be transferred.
val kf = KeyFactory.getInstance("ECDSA", "BC")
val privK1Decoded = kf.generatePrivate(PKCS8EncodedKeySpec(privK1.encoded))
val pubK1Decoded = kf.generatePublic(X509EncodedKeySpec(pubK1.encoded))
// Encode and decode private key.
val privKeyDecoded = Crypto.decodePrivateKey(privKey.encoded)
val pubKeyDecoded = Crypto.decodePublicKey(pubKey.encoded)
assertEquals(privK1Decoded.algorithm, "ECDSA")
assertEquals((privK1Decoded as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256k1"))
assertEquals(pubK1Decoded.algorithm, "ECDSA")
assertEquals((pubK1Decoded as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256k1"))
assertEquals(privKeyDecoded.algorithm, "ECDSA")
assertEquals((privKeyDecoded as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256k1"))
assertEquals(pubKeyDecoded.algorithm, "ECDSA")
assertEquals((pubKeyDecoded as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256k1"))
}
@Test
fun `ECDSA secp256r1 scheme finder by key type`() {
val keyPairR1 = Crypto.generateKeyPair("ECDSA_SECP256R1_SHA256")
val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val (privR1, pubR1) = keyPairR1
assertEquals(privR1.algorithm, "ECDSA")
assertEquals((privR1 as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256r1"))
@ -494,7 +469,7 @@ class CryptoUtilsTest {
@Test
fun `EdDSA scheme finder by key type`() {
val keyPairEd = Crypto.generateKeyPair("EDDSA_ED25519_SHA512")
val keyPairEd = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
val (privEd, pubEd) = keyPairEd
assertEquals(privEd.algorithm, "EdDSA")
@ -505,7 +480,7 @@ class CryptoUtilsTest {
@Test
fun `SPHINCS-256 scheme finder by key type`() {
val keyPairSP = Crypto.generateKeyPair("SPHINCS-256_SHA512")
val keyPairSP = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
val (privSP, pubSP) = keyPairSP
assertEquals(privSP.algorithm, "SPHINCS-256")
assertEquals(pubSP.algorithm, "SPHINCS-256")
@ -513,7 +488,7 @@ class CryptoUtilsTest {
@Test
fun `Automatic EdDSA key-type detection and decoding`() {
val keyPairEd = Crypto.generateKeyPair("EDDSA_ED25519_SHA512")
val keyPairEd = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
val (privEd, pubEd) = keyPairEd
val encodedPrivEd = privEd.encoded
val encodedPubEd = pubEd.encoded
@ -529,7 +504,7 @@ class CryptoUtilsTest {
@Test
fun `Automatic ECDSA secp256k1 key-type detection and decoding`() {
val keyPairK1 = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val (privK1, pubK1) = keyPairK1
val encodedPrivK1 = privK1.encoded
val encodedPubK1 = pubK1.encoded
@ -545,7 +520,7 @@ class CryptoUtilsTest {
@Test
fun `Automatic ECDSA secp256r1 key-type detection and decoding`() {
val keyPairR1 = Crypto.generateKeyPair("ECDSA_SECP256R1_SHA256")
val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val (privR1, pubR1) = keyPairR1
val encodedPrivR1 = privR1.encoded
val encodedPubR1 = pubR1.encoded
@ -561,7 +536,7 @@ class CryptoUtilsTest {
@Test
fun `Automatic RSA key-type detection and decoding`() {
val keyPairRSA = Crypto.generateKeyPair("RSA_SHA256")
val keyPairRSA = Crypto.generateKeyPair(Crypto.RSA_SHA256)
val (privRSA, pubRSA) = keyPairRSA
val encodedPrivRSA = privRSA.encoded
val encodedPubRSA = pubRSA.encoded
@ -577,7 +552,7 @@ class CryptoUtilsTest {
@Test
fun `Automatic SPHINCS-256 key-type detection and decoding`() {
val keyPairSP = Crypto.generateKeyPair("SPHINCS-256_SHA512")
val keyPairSP = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
val (privSP, pubSP) = keyPairSP
val encodedPrivSP = privSP.encoded
val encodedPubSP = pubSP.encoded
@ -593,12 +568,12 @@ class CryptoUtilsTest {
@Test
fun `Failure test between K1 and R1 keys`() {
val keyPairK1 = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val privK1 = keyPairK1.private
val encodedPrivK1 = privK1.encoded
val decodedPrivK1 = Crypto.decodePrivateKey(encodedPrivK1)
val keyPairR1 = Crypto.generateKeyPair("ECDSA_SECP256R1_SHA256")
val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val privR1 = keyPairR1.private
val encodedPrivR1 = privR1.encoded
val decodedPrivR1 = Crypto.decodePrivateKey(encodedPrivR1)
@ -608,7 +583,7 @@ class CryptoUtilsTest {
@Test
fun `Decoding Failure on randomdata as key`() {
val keyPairK1 = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val privK1 = keyPairK1.private
val encodedPrivK1 = privK1.encoded
@ -628,7 +603,7 @@ class CryptoUtilsTest {
@Test
fun `Decoding Failure on malformed keys`() {
val keyPairK1 = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val privK1 = keyPairK1.private
val encodedPrivK1 = privK1.encoded

View File

@ -12,7 +12,7 @@ class SignedDataTest {
@Test
fun `make sure correctly signed data is released`() {
val keyPair = generateKeyPair()
val sig = keyPair.private.signWithECDSA(serialized.bytes, keyPair.public)
val sig = keyPair.private.sign(serialized.bytes, keyPair.public)
val wrappedData = SignedData(serialized, sig)
val unwrappedData = wrappedData.verified()
@ -23,7 +23,7 @@ class SignedDataTest {
fun `make sure incorrectly signed data raises an exception`() {
val keyPairA = generateKeyPair()
val keyPairB = generateKeyPair()
val sig = keyPairA.private.signWithECDSA(serialized.bytes, keyPairB.public)
val sig = keyPairA.private.sign(serialized.bytes, keyPairB.public)
val wrappedData = SignedData(serialized, sig)
wrappedData.verified()
}

View File

@ -76,15 +76,14 @@ class KryoTests {
val keyPair = generateKeyPair()
val bitsToSign: ByteArray = Ints.toByteArray(0x01234567)
val wrongBits: ByteArray = Ints.toByteArray(0x76543210)
val signature = keyPair.signWithECDSA(bitsToSign)
signature.verifyWithECDSA(bitsToSign)
assertThatThrownBy { signature.verifyWithECDSA(wrongBits) }
val signature = keyPair.sign(bitsToSign)
signature.verify(bitsToSign)
assertThatThrownBy { signature.verify(wrongBits) }
val deserialisedKeyPair = keyPair.serialize(kryo).deserialize(kryo)
val deserialisedSignature = deserialisedKeyPair.signWithECDSA(bitsToSign)
assertThat(deserialisedSignature).isEqualTo(signature)
deserialisedSignature.verifyWithECDSA(bitsToSign)
assertThatThrownBy { deserialisedSignature.verifyWithECDSA(wrongBits) }
val deserialisedSignature = deserialisedKeyPair.sign(bitsToSign)
deserialisedSignature.verify(bitsToSign)
assertThatThrownBy { deserialisedSignature.verify(wrongBits) }
}
@Test

View File

@ -9,7 +9,7 @@ import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.signWithECDSA
import net.corda.core.crypto.sign
import net.corda.core.flows.FlowLogic
import net.corda.core.node.PluginServiceHub
import net.corda.core.node.ServiceHub
@ -258,7 +258,7 @@ class ForeignExchangeRemoteFlow(val source: Party) : FlowLogic<Unit>() {
}
// assuming we have completed state and business level validation we can sign the trade
val ourSignature = serviceHub.legalIdentityKey.signWithECDSA(proposedTrade.id)
val ourSignature = serviceHub.legalIdentityKey.sign(proposedTrade.id)
// send the other side our signature.
send(source, ourSignature)

View File

@ -250,7 +250,7 @@ class RecordCompletionFlow(val source: Party) : FlowLogic<Unit>() {
}
// DOCEND 3
// Having verified the SignedTransaction passed to us we can sign it too
val ourSignature = serviceHub.legalIdentityKey.signWithECDSA(completeTx.tx.id)
val ourSignature = serviceHub.legalIdentityKey.sign(completeTx.tx.id)
// Send our signature to the other party.
send(source, ourSignature)
// N.B. The FinalityProtocol will be responsible for Notarising the SignedTransaction

View File

@ -135,7 +135,7 @@ object TwoPartyTradeFlow {
open fun calculateOurSignature(partialTX: SignedTransaction): DigitalSignature.WithKey {
progressTracker.currentStep = SIGNING
return myKeyPair.signWithECDSA(partialTX.id)
return myKeyPair.sign(partialTX.id)
}
}

View File

@ -323,7 +323,7 @@ data class NodeRegistration(val node: NodeInfo, val serial: Long, val type: AddO
*/
fun toWire(privateKey: PrivateKey): WireNodeRegistration {
val regSerialized = this.serialize()
val regSig = privateKey.signWithECDSA(regSerialized.bytes, node.legalIdentity.owningKey)
val regSig = privateKey.sign(regSerialized.bytes, node.legalIdentity.owningKey)
return WireNodeRegistration(regSerialized, regSig)
}

View File

@ -198,7 +198,7 @@ object BFTSMaRt {
protected fun sign(bytes: ByteArray): DigitalSignature.WithKey {
val mySigningKey = db.transaction { services.notaryIdentityKey }
return mySigningKey.signWithECDSA(bytes)
return mySigningKey.sign(bytes)
}
// TODO:

View File

@ -54,7 +54,7 @@ class NotaryServiceTests {
val future = runNotaryClient(stx)
val signatures = future.getOrThrow()
signatures.forEach { it.verifyWithECDSA(stx.id) }
signatures.forEach { it.verify(stx.id) }
}
@Test fun `should sign a unique transaction without a timestamp`() {
@ -67,7 +67,7 @@ class NotaryServiceTests {
val future = runNotaryClient(stx)
val signatures = future.getOrThrow()
signatures.forEach { it.verifyWithECDSA(stx.id) }
signatures.forEach { it.verify(stx.id) }
}
@Test fun `should report error for transaction with an invalid timestamp`() {

View File

@ -7,7 +7,7 @@ import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.MerkleTreeException
import net.corda.core.crypto.Party
import net.corda.core.crypto.keys
import net.corda.core.crypto.signWithECDSA
import net.corda.core.crypto.sign
import net.corda.core.flows.FlowLogic
import net.corda.core.math.CubicSplineInterpolator
import net.corda.core.math.Interpolator
@ -224,7 +224,7 @@ object NodeInterestRates {
// Note that we will happily sign an invalid transaction, as we are only being presented with a filtered
// version so we can't resolve or check it ourselves. However, that doesn't matter much, as if we sign
// an invalid transaction the signature is worthless.
return signingKey.signWithECDSA(ftx.rootHash.bytes, identity)
return signingKey.sign(ftx.rootHash.bytes, identity)
}
// DOCEND 1
}

View File

@ -337,7 +337,7 @@ fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: List<KeyPair>)
}
wtx.mustSign.expandedCompositeKeys.forEach {
val key = keyLookup[it] ?: throw IllegalArgumentException("Missing required key for ${it.toStringShort()}")
signatures += key.signWithECDSA(wtx.id)
signatures += key.sign(wtx.id)
}
SignedTransaction(bits, signatures)
}