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>() { object PublicKeySerializer : JsonSerializer<EdDSAPublicKey>() {
override fun serialize(obj: EdDSAPublicKey, generator: JsonGenerator, provider: SerializerProvider) { 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()) generator.writeString(obj.toBase58String())
} }
} }

View File

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

View File

@ -1,6 +1,8 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.i2p.crypto.eddsa.EdDSAEngine
import net.i2p.crypto.eddsa.EdDSAKey import net.i2p.crypto.eddsa.EdDSAKey
import net.i2p.crypto.eddsa.EdDSASecurityProvider
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable
import org.bouncycastle.jce.ECNamedCurveTable import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.interfaces.ECKey import org.bouncycastle.jce.interfaces.ECKey
@ -26,62 +28,61 @@ import java.security.spec.X509EncodedKeySpec
* </ul> * </ul>
*/ */
object Crypto { 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. * RSA_SHA256 signature scheme using SHA256 as hash algorithm and MGF1 (with SHA256) as mask generation function.
* Note: Recommended key size >= 3072 bits. * Note: Recommended key size >= 3072 bits.
*/ */
private val RSA_SHA256 = SignatureScheme( val RSA_SHA256 = SignatureScheme(
1, 1,
"RSA_SHA256", "RSA_SHA256",
BouncyCastleProvider.PROVIDER_NAME,
"RSA", "RSA",
Signature.getInstance("SHA256WITHRSAANDMGF1", "BC"), "SHA256WITHRSAANDMGF1",
KeyFactory.getInstance("RSA", "BC"),
KeyPairGenerator.getInstance("RSA", "BC"),
null, null,
3072, 3072,
"RSA_SHA256 signature scheme using SHA256 as hash algorithm and MGF1 (with SHA256) as mask generation function." "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. */ /** ECDSA signature scheme using the secp256k1 Koblitz curve. */
private val ECDSA_SECP256K1_SHA256 = SignatureScheme( val ECDSA_SECP256K1_SHA256 = SignatureScheme(
2, 2,
"ECDSA_SECP256K1_SHA256", "ECDSA_SECP256K1_SHA256",
BouncyCastleProvider.PROVIDER_NAME,
"ECDSA", "ECDSA",
Signature.getInstance("SHA256withECDSA", "BC"), "SHA256withECDSA",
KeyFactory.getInstance("ECDSA", "BC"),
KeyPairGenerator.getInstance("ECDSA", "BC"),
ECNamedCurveTable.getParameterSpec("secp256k1"), ECNamedCurveTable.getParameterSpec("secp256k1"),
256, 256,
"ECDSA signature scheme using the secp256k1 Koblitz curve." "ECDSA signature scheme using the secp256k1 Koblitz curve."
) )
/** ECDSA signature scheme using the secp256r1 (NIST P-256) curve. */ /** ECDSA signature scheme using the secp256r1 (NIST P-256) curve. */
private val ECDSA_SECP256R1_SHA256 = SignatureScheme( val ECDSA_SECP256R1_SHA256 = SignatureScheme(
3, 3,
"ECDSA_SECP256R1_SHA256", "ECDSA_SECP256R1_SHA256",
BouncyCastleProvider.PROVIDER_NAME,
"ECDSA", "ECDSA",
Signature.getInstance("SHA256withECDSA", "BC"), "SHA256withECDSA",
KeyFactory.getInstance("ECDSA", "BC"),
KeyPairGenerator.getInstance("ECDSA", "BC"),
ECNamedCurveTable.getParameterSpec("secp256r1"), ECNamedCurveTable.getParameterSpec("secp256r1"),
256, 256,
"ECDSA signature scheme using the secp256r1 (NIST P-256) curve." "ECDSA signature scheme using the secp256r1 (NIST P-256) curve."
) )
/** EdDSA signature scheme using the ed255519 twisted Edwards curve. */ /** EdDSA signature scheme using the ed255519 twisted Edwards curve. */
private val EDDSA_ED25519_SHA512 = SignatureScheme( val EDDSA_ED25519_SHA512 = SignatureScheme(
4, 4,
"EDDSA_ED25519_SHA512", "EDDSA_ED25519_SHA512",
"EdDSA", EdDSASecurityProvider.PROVIDER_NAME,
Signature.getInstance("SHA512withEdDSA", "I2P"), EdDSAKey.KEY_ALGORITHM,
KeyFactory.getInstance("EdDSA", "I2P"), EdDSAEngine.SIGNATURE_ALGORITHM,
KeyPairGenerator.getInstance("EdDSA", "I2P"),
EdDSANamedCurveTable.getByName("ED25519"), EdDSANamedCurveTable.getByName("ED25519"),
256, 256,
"EdDSA signature scheme using the ed25519 twisted Edwards curve." "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 * 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. * at the cost of larger key sizes and loss of compatibility.
*/ */
private val SPHINCS256_SHA256 = SignatureScheme( val SPHINCS256_SHA256 = SignatureScheme(
5, 5,
"SPHINCS-256_SHA512", "SPHINCS-256_SHA512",
"SPHINCS-256", "BCPQC",
Signature.getInstance("SHA512WITHSPHINCS256", "BCPQC"), "SPHINCS256",
KeyFactory.getInstance("SPHINCS256", "BCPQC"), "SHA512WITHSPHINCS256",
KeyPairGenerator.getInstance("SPHINCS256", "BCPQC"),
SPHINCS256KeyGenParameterSpec(SPHINCS256KeyGenParameterSpec.SHA512_256), SPHINCS256KeyGenParameterSpec(SPHINCS256KeyGenParameterSpec.SHA512_256),
256, 256,
"SPHINCS-256 hash-based signature scheme. It provides 128bit security against post-quantum attackers " + "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). */ /** 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. * Supported digital signature schemes.
* Note: Only the schemes added in this map will be supported (see [Crypto]). * 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( val supportedSignatureSchemes = listOf(
RSA_SHA256.schemeCodeName to RSA_SHA256, RSA_SHA256,
ECDSA_SECP256K1_SHA256.schemeCodeName to ECDSA_SECP256K1_SHA256, ECDSA_SECP256K1_SHA256,
ECDSA_SECP256R1_SHA256.schemeCodeName to ECDSA_SECP256R1_SHA256, ECDSA_SECP256R1_SHA256,
EDDSA_ED25519_SHA512.schemeCodeName to EDDSA_ED25519_SHA512, EDDSA_ED25519_SHA512,
SPHINCS256_SHA256.schemeCodeName to SPHINCS256_SHA256 SPHINCS256_SHA256
) ).associateBy { it.schemeCodeName }
/** /**
* Factory pattern to retrieve the corresponding [SignatureScheme] based on the type of the [String] input. * 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. * @return a currently supported SignatureScheme.
* @throws IllegalArgumentException if the requested signature scheme is not supported. * @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") 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)
/** /**
* Retrieve the corresponding [SignatureScheme] based on the type of the input [Key]. * Retrieve the corresponding [SignatureScheme] based on the type of the input [Key].
@ -150,9 +139,11 @@ object Crypto {
* @return a currently supported SignatureScheme. * @return a currently supported SignatureScheme.
* @throws IllegalArgumentException if the requested key type is not supported. * @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) { 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 (algorithm == sig.algorithmName) {
// If more than one ECDSA schemes are supported, we should distinguish between them by checking their curve parameters. // 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. // 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()}") 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. * 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. * @param encodedKey a PKCS8 encoded private key.
* @throws IllegalArgumentException on not supported scheme or if the given key specification * @throws IllegalArgumentException on not supported scheme or if the given key specification
* is inappropriate for this key factory to produce a private key. * is inappropriate for this key factory to produce a private key.
*/ */
@Throws(IllegalArgumentException::class) @Throws(IllegalArgumentException::class)
fun decodePrivateKey(encodedKey: ByteArray): PrivateKey { fun decodePrivateKey(encodedKey: ByteArray): PrivateKey {
for ((_, _, _, _, keyFactory) in supportedSignatureSchemes.values) { for ((_, _, providerName, algorithmName) in supportedSignatureSchemes.values) {
try { try {
return keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey)) return KeyFactory.getInstance(algorithmName, providerMap[providerName]).generatePrivate(PKCS8EncodedKeySpec(encodedKey))
} catch (ikse: InvalidKeySpecException) { } catch (ikse: InvalidKeySpecException) {
// ignore it - only used to bypass the scheme that causes an exception. // 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. * Decode a PKCS8 encoded key to its [PrivateKey] object based on the input scheme code name.
* This will be used by Kryo deserialisation. * This should be used when the type key is known, e.g. during Kryo deserialisation or with key caches or key managers.
* @param encodedKey a PKCS8 encoded private key.
* @param schemeCodeName a [String] that should match a key in supportedSignatureSchemes map (e.g. ECDSA_SECP256K1_SHA256). * @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 * @throws IllegalArgumentException on not supported scheme or if the given key specification
* is inappropriate for this key factory to produce a private key. * is inappropriate for this key factory to produce a private key.
*/ */
@Throws(IllegalArgumentException::class, InvalidKeySpecException::class) @Throws(IllegalArgumentException::class, InvalidKeySpecException::class)
fun decodePrivateKey(encodedKey: ByteArray, schemeCodeName: String): PrivateKey { fun decodePrivateKey(schemeCodeName: String, encodedKey: ByteArray): PrivateKey = decodePrivateKey(findSignatureScheme(schemeCodeName), encodedKey)
val sig = findSignatureScheme(schemeCodeName)
/**
* 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 { try {
return sig.keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey)) return KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName]).generatePrivate(PKCS8EncodedKeySpec(encodedKey))
} catch (ikse: InvalidKeySpecException) { } 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) 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. * 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. * @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 * @throws IllegalArgumentException on not supported scheme or if the given key specification
* is inappropriate for this key factory to produce a private key. * is inappropriate for this key factory to produce a private key.
*/ */
@Throws(IllegalArgumentException::class) @Throws(IllegalArgumentException::class)
fun decodePublicKey(encodedKey: ByteArray): PublicKey { fun decodePublicKey(encodedKey: ByteArray): PublicKey {
for ((_, _, _, _, keyFactory) in supportedSignatureSchemes.values) { for ((_, _, providerName, algorithmName) in supportedSignatureSchemes.values) {
try { try {
return keyFactory.generatePublic(X509EncodedKeySpec(encodedKey)) return KeyFactory.getInstance(algorithmName, providerMap[providerName]).generatePublic(X509EncodedKeySpec(encodedKey))
} catch (ikse: InvalidKeySpecException) { } catch (ikse: InvalidKeySpecException) {
// ignore it - only used to bypass the scheme that causes an exception. // 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. * Decode an X509 encoded key to its [PrivateKey] object based on the input scheme code name.
* This will be used by Kryo deserialisation. * This should be used when the type key is known, e.g. during Kryo deserialisation or with key caches or key managers.
* @param encodedKey an X509 encoded public key.
* @param schemeCodeName a [String] that should match a key in supportedSignatureSchemes map (e.g. ECDSA_SECP256K1_SHA256). * @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 IllegalArgumentException if the requested scheme is not supported
* @throws InvalidKeySpecException if the given key specification * @throws InvalidKeySpecException if the given key specification
* is inappropriate for this key factory to produce a public key. * is inappropriate for this key factory to produce a public key.
*/ */
@Throws(IllegalArgumentException::class, InvalidKeySpecException::class) @Throws(IllegalArgumentException::class, InvalidKeySpecException::class)
fun decodePublicKey(encodedKey: ByteArray, schemeCodeName: String): PublicKey { fun decodePublicKey(schemeCodeName: String, encodedKey: ByteArray): PublicKey = decodePublicKey(findSignatureScheme(schemeCodeName), encodedKey)
val sig = findSignatureScheme(schemeCodeName)
/**
* 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 { try {
return sig.keyFactory.generatePublic(X509EncodedKeySpec(encodedKey)) return KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName]).generatePublic(X509EncodedKeySpec(encodedKey))
} catch (ikse: InvalidKeySpecException) { } 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) 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 * 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). * 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 SignatureException if signing is not possible due to malformed data or private key.
*/ */
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class) @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]. * 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 SignatureException if signing is not possible due to malformed data or private key.
*/ */
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class) @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]. * 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 privateKey the signer's [PrivateKey].
* @param clearData the data/message to be signed in [ByteArray] form (usually the Merkle root). * @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. * @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 SignatureException if signing is not possible due to malformed data or private key.
*/ */
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class) @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!") if (clearData.isEmpty()) throw Exception("Signing of an empty array is not permitted!")
signature.initSign(privateKey) signature.initSign(privateKey)
signature.update(clearData) signature.update(clearData)
@ -336,6 +327,7 @@ object Crypto {
/** /**
* Utility to simplify the act of verifying a digital signature. * 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. * 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 publicKey the signer's [PublicKey].
* @param signatureData the signatureData on a message. * @param signatureData the signatureData on a message.
* @param clearData the clear data/message that was signed (usually the Merkle root). * @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 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) @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. * 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 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) @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. * Method to verify a digital signature.
* It returns true if it succeeds, but it always throws an exception if verification fails. * 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 publicKey the signer's [PublicKey].
* @param signatureData the signatureData on a message. * @param signatureData the signatureData on a message.
* @param clearData the clear data/message that was signed (usually the Merkle root). * @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, * @throws SignatureException if this signatureData object is not initialized properly,
* the passed-in signatureData is improperly encoded or of the wrong type, * 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. * 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 (signatureData.isEmpty()) throw IllegalArgumentException("Signature data is empty!")
if (clearData.isEmpty()) throw IllegalArgumentException("Clear data is empty, nothing to verify!") if (clearData.isEmpty()) throw IllegalArgumentException("Clear data is empty, nothing to verify!")
signature.initVerify(publicKey) val verificationResult = isValid(signatureScheme, publicKey, signatureData, clearData)
signature.update(clearData)
val verificationResult = signature.verify(signatureData)
if (verificationResult) { if (verificationResult) {
return true return true
} else { } else {
@ -413,15 +406,82 @@ object Crypto {
} }
/** /**
* Check if the requested signature scheme is supported by the system. * Utility to simplify the act of verifying a digital signature by identifying the signature scheme used from the input public key's type.
* @param schemeCodeName a signature scheme's code name (e.g. ECDSA_SECP256K1_SHA256). * It returns true if it succeeds and false if not. In comparison to [doVerify] if the key and signature
* @return true if the signature scheme is supported. * 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.
*/ */
@Throws(SignatureException::class)
fun isValid(publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray) = isValid(findSignatureScheme(publicKey), publicKey, signatureData, clearData)
/**
* 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(schemeCodeName: String): Boolean = schemeCodeName in supportedSignatureSchemes
fun isSupportedSignatureScheme(signatureScheme: SignatureScheme): Boolean = signatureScheme.schemeCodeName in supportedSignatureSchemes
/** @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()
} }

View File

@ -4,11 +4,8 @@ package net.corda.core.crypto
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import net.i2p.crypto.eddsa.EdDSAEngine
import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey 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.EdDSANamedCurveTable
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
@ -40,34 +37,45 @@ class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {
@CordaSerializable @CordaSerializable
object NullSignature : DigitalSignature.WithKey(NullPublicKey, ByteArray(32)) object NullSignature : DigitalSignature.WithKey(NullPublicKey, ByteArray(32))
/** Utility to simplify the act of signing a byte array */ /**
fun PrivateKey.signWithECDSA(bytes: ByteArray): DigitalSignature { * Utility to simplify the act of signing a byte array.
val signer = EdDSAEngine() * @param bytesToSign the data/message to be signed in [ByteArray] form (usually the Merkle root).
signer.initSign(this) * @return the [DigitalSignature] object on the input message.
signer.update(bytes) * @throws IllegalArgumentException if the signature scheme is not supported for this private key.
val sig = signer.sign() * @throws InvalidKeyException if the private key is invalid.
return DigitalSignature(sig) * @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 { fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
return DigitalSignature.WithKey(publicKey, signWithECDSA(bytesToSign).bytes) 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 // TODO This case will need more careful thinking, as party owningKey can be a CompositeKey. One way of doing that is
// implementation of CompositeSignature. // implementation of CompositeSignature.
@Throws(InvalidKeyException::class) @Throws(InvalidKeyException::class)
fun KeyPair.signWithECDSA(bytesToSign: ByteArray, party: Party): DigitalSignature.LegallyIdentifiable { fun KeyPair.sign(bytesToSign: ByteArray, party: Party): DigitalSignature.LegallyIdentifiable {
val sig = signWithECDSA(bytesToSign) val sig = sign(bytesToSign)
val sigKey = when (party.owningKey) { // Quick workaround when we have CompositeKey as Party owningKey. 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.") is CompositeKey -> throw InvalidKeyException("Signing for parties with CompositeKey not supported.")
else -> party.owningKey else -> party.owningKey
} }
sigKey.verifyWithECDSA(bytesToSign, sig)
return DigitalSignature.LegallyIdentifiable(party, sig.bytes) 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 * @throws InvalidKeyException if the key to verify the signature with is not valid (i.e. wrong key type for the
* signature). * signature).
* @throws SignatureException if the signature is invalid (i.e. damaged), or does not match the key (incorrect). * @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()`, // 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 @Throws(SignatureException::class, IllegalArgumentException::class, InvalidKeyException::class)
// not match. fun PublicKey.verify(content: ByteArray, signature: DigitalSignature) = Crypto.doVerify(this, signature.bytes, content)
@Throws(IllegalStateException::class, SignatureException::class)
fun PublicKey.verifyWithECDSA(content: ByteArray, signature: DigitalSignature) {
if (!isValidForECDSA(content, signature))
throw SignatureException("Signature did not match")
}
/** /**
* 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, * 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] * as it avoids the risk of failing to test the result, but this is for uses such as [java.security.Signature.verify]
* implementations. * 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 * @throws InvalidKeyException if the key to verify the signature with is not valid (i.e. wrong key type for the
* signature). * signature).
* @throws SignatureException if the signature is invalid (i.e. damaged). * @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. * @return whether the signature is correct for this key.
*/ */
@Throws(IllegalStateException::class, SignatureException::class) @Throws(IllegalStateException::class, SignatureException::class, IllegalArgumentException::class)
fun PublicKey.isValidForECDSA(content: ByteArray, signature: DigitalSignature) : Boolean { fun PublicKey.isValid(content: ByteArray, signature: DigitalSignature) : Boolean {
val pubKey = when (this) { if (this is CompositeKey)
is CompositeKey -> throw IllegalStateException("Verification of CompositeKey signatures currently not supported.") // TODO CompositeSignature verification. throw IllegalStateException("Verification of CompositeKey signatures currently not supported.") // TODO CompositeSignature verification.
else -> this return Crypto.isValid(this, signature.bytes, content)
}
val verifier = EdDSAEngine()
verifier.initVerify(pubKey)
verifier.update(content)
return verifier.verify(signature.bytes)
} }
/** Render a public key to a string, using a short form if it's an elliptic curve public key */ /** 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 * 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. * you want hard-coded private keys.
* This currently works for EdDSA ED25519 only.
*/ */
fun entropyToKeyPair(entropy: BigInteger): KeyPair { fun entropyToKeyPair(entropy: BigInteger): KeyPair {
val params = ed25519Curve val params = EdDSANamedCurveTable.getByName("ED25519")
val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8) val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8)
val priv = EdDSAPrivateKeySpec(bytes, params) val priv = EdDSAPrivateKeySpec(bytes, params)
val pub = EdDSAPublicKeySpec(priv.a, params) val pub = EdDSAPublicKeySpec(priv.a, params)
val key = KeyPair(EdDSAPublicKey(pub), EdDSAPrivateKey(priv)) val keyPair = KeyPair(EdDSAPublicKey(pub), EdDSAPrivateKey(priv))
return key 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. * Helper function for signing.
* @param metaData tha attached MetaData object. * @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) @Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
fun PrivateKey.sign(metaData: MetaData): TransactionSignature = Crypto.doSign(this, metaData) 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. * Helper function to verify a signature.
* @param signatureData the signature on a message. * @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 SignatureException if the signature is invalid (i.e. damaged), or does not match the key (incorrect).
*/ */
@Throws(InvalidKeyException::class, SignatureException::class) @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. * 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 SignatureException if the signature is invalid (i.e. damaged), or does not match the key (incorrect).
*/ */
@Throws(InvalidKeyException::class, SignatureException::class) @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 * 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. * 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. * @return whether the signature is correct for this key.
*/ */
@Throws(InvalidKeyException::class, SignatureException::class) @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 // 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 package net.corda.core.crypto
import java.security.KeyFactory
import java.security.KeyPairGeneratorSpi
import java.security.Signature import java.security.Signature
import java.security.spec.AlgorithmParameterSpec import java.security.spec.AlgorithmParameterSpec
@ -9,12 +7,9 @@ import java.security.spec.AlgorithmParameterSpec
* This class is used to define a digital signature scheme. * 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 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 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 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. * @param signatureName a signature-scheme name as required to create [Signature] objects (e.g. "SHA256withECDSA")
* 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 algSpec parameter specs for the underlying algorithm. Note that RSA is defined by the key size rather than algSpec. * @param algSpec parameter specs for the underlying algorithm. Note that RSA is defined by the key size rather than algSpec.
* eg. ECGenParameterSpec("secp256k1"). * eg. ECGenParameterSpec("secp256k1").
* @param keySize the private key size (currently used for RSA only). * @param keySize the private key size (currently used for RSA only).
@ -23,22 +18,9 @@ import java.security.spec.AlgorithmParameterSpec
data class SignatureScheme( data class SignatureScheme(
val schemeNumberID: Int, val schemeNumberID: Int,
val schemeCodeName: String, val schemeCodeName: String,
val providerName: String,
val algorithmName: String, val algorithmName: String,
val sig: Signature, val signatureName: String,
val keyFactory: KeyFactory,
val keyPairGenerator: KeyPairGeneratorSpi,
val algSpec: AlgorithmParameterSpec?, val algSpec: AlgorithmParameterSpec?,
val keySize: Int, val keySize: Int,
val desc: String) { 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())
}
}

View File

@ -22,7 +22,7 @@ open class SignedData<T : Any>(val raw: SerializedBytes<T>, val sig: DigitalSign
*/ */
@Throws(SignatureException::class) @Throws(SignatureException::class)
fun verified(): T { fun verified(): T {
sig.by.verifyWithECDSA(raw.bytes, sig) sig.by.verify(raw.bytes, sig)
val data = raw.deserialize() val data = raw.deserialize()
verifyData(data) verifyData(data)
return 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.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey import net.i2p.crypto.eddsa.EdDSAPublicKey
import org.bouncycastle.asn1.x500.X500Name 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.objenesis.strategy.StdInstantiatorStrategy
import org.slf4j.Logger import org.slf4j.Logger
import java.io.BufferedInputStream import java.io.BufferedInputStream
@ -93,6 +99,13 @@ object DefaultKryoCustomizer {
register(X500Name::class.java, X500NameSerializer) 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) val customization = KryoSerializationCustomization(this)
pluginRegistries.forEach { it.customizeSerialization(customization) } 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.corda.core.transactions.WireTransaction
import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey 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.EdDSAPrivateKeySpec
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
import org.bouncycastle.asn1.ASN1InputStream import org.bouncycastle.asn1.ASN1InputStream
@ -22,6 +23,7 @@ import java.io.*
import java.lang.reflect.InvocationTargetException import java.lang.reflect.InvocationTargetException
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.security.PrivateKey
import java.security.PublicKey import java.security.PublicKey
import java.security.spec.InvalidKeySpecException import java.security.spec.InvalidKeySpecException
import java.time.Instant import java.time.Instant
@ -340,13 +342,13 @@ object WireTransactionSerializer : Serializer<WireTransaction>() {
@ThreadSafe @ThreadSafe
object Ed25519PrivateKeySerializer : Serializer<EdDSAPrivateKey>() { object Ed25519PrivateKeySerializer : Serializer<EdDSAPrivateKey>() {
override fun write(kryo: Kryo, output: Output, obj: 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) output.writeBytesWithLength(obj.seed)
} }
override fun read(kryo: Kryo, input: Input, type: Class<EdDSAPrivateKey>): EdDSAPrivateKey { override fun read(kryo: Kryo, input: Input, type: Class<EdDSAPrivateKey>): EdDSAPrivateKey {
val seed = input.readBytesWithLength() 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 @ThreadSafe
object Ed25519PublicKeySerializer : Serializer<EdDSAPublicKey>() { object Ed25519PublicKeySerializer : Serializer<EdDSAPublicKey>() {
override fun write(kryo: Kryo, output: Output, obj: 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) output.writeBytesWithLength(obj.abyte)
} }
override fun read(kryo: Kryo, input: Input, type: Class<EdDSAPublicKey>): EdDSAPublicKey { override fun read(kryo: Kryo, input: Input, type: Class<EdDSAPublicKey>): EdDSAPublicKey {
val A = input.readBytesWithLength() 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. * 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 * @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 visibleInputs = kryo.readClassAndObject(input) as BitSet?
val signedInputs = kryo.readClassAndObject(input) as BitSet? val signedInputs = kryo.readClassAndObject(input) as BitSet?
val merkleRoot = input.readBytesWithLength() 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) 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.DigitalSignature
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.isFulfilledBy 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.CordaSerializable
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import java.security.KeyPair import java.security.KeyPair
@ -93,7 +93,7 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
@Throws(SignatureException::class) @Throws(SignatureException::class)
fun checkSignaturesAreValid() { fun checkSignaturesAreValid() {
for (sig in sigs) { 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. * @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)" override fun toString(): String = "${javaClass.simpleName}(id=$id)"
} }

View File

@ -97,7 +97,7 @@ open class TransactionBuilder(
fun signWith(key: KeyPair): TransactionBuilder { fun signWith(key: KeyPair): TransactionBuilder {
check(currentSigs.none { it.by == key.public }) { "This partial transaction was already signed by ${key.public}" } check(currentSigs.none { it.by == key.public }) { "This partial transaction was already signed by ${key.public}" }
val data = toWireTransaction().id val data = toWireTransaction().id
addSignatureUnchecked(key.signWithECDSA(data.bytes)) addSignatureUnchecked(key.sign(data.bytes))
return this return this
} }
@ -121,7 +121,7 @@ open class TransactionBuilder(
*/ */
fun checkSignature(sig: DigitalSignature.WithKey) { fun checkSignature(sig: DigitalSignature.WithKey) {
require(commands.any { it.signers.any { sig.by in it.keys } }) { "Signature key doesn't match any command" } 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. */ /** 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) val response = sendAndReceive<DigitalSignature.WithKey>(party, proposal)
return response.unwrap { return response.unwrap {
check(party.owningKey.isFulfilledBy(it.by)) { "Not signed by the required participant" } check(party.owningKey.isFulfilledBy(it.by)) { "Not signed by the required participant" }
it.verifyWithECDSA(stx.id) it.verify(stx.id)
it it
} }
} }
@ -157,7 +157,7 @@ abstract class AbstractStateReplacementFlow {
// TODO: This step should not be necessary, as signatures are re-checked in verifySignatures. // TODO: This step should not be necessary, as signatures are re-checked in verifySignatures.
val allSignatures = swapSignatures.unwrap { signatures -> val allSignatures = swapSignatures.unwrap { signatures ->
signatures.forEach { it.verifyWithECDSA(stx.id) } signatures.forEach { it.verify(stx.id) }
signatures signatures
} }
@ -187,7 +187,7 @@ abstract class AbstractStateReplacementFlow {
private fun sign(stx: SignedTransaction): DigitalSignature.WithKey { private fun sign(stx: SignedTransaction): DigitalSignature.WithKey {
val myKey = serviceHub.legalIdentityKey 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) { private fun validateSignature(sig: DigitalSignature.WithKey, data: ByteArray) {
check(sig.by in notaryParty.owningKey.keys) { "Invalid signer for the notary result" } 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 { private fun sign(bits: ByteArray): DigitalSignature.WithKey {
val mySigningKey = serviceHub.notaryIdentityKey val mySigningKey = serviceHub.notaryIdentityKey
return mySigningKey.signWithECDSA(bits) return mySigningKey.sign(bits)
} }
private fun notaryException(txId: SecureHash, e: UniquenessException): NotaryException { private fun notaryException(txId: SecureHash, e: UniquenessException): NotaryException {

View File

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

View File

@ -5,7 +5,7 @@ import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.generateKeyPair 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.serialization.SerializedBytes
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -26,7 +26,7 @@ class TransactionTests {
private fun makeSigned(wtx: WireTransaction, vararg keys: KeyPair): SignedTransaction { private fun makeSigned(wtx: WireTransaction, vararg keys: KeyPair): SignedTransaction {
val bytes: SerializedBytes<WireTransaction> = wtx.serialized 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 @Test

View File

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

View File

@ -12,7 +12,7 @@ class SignedDataTest {
@Test @Test
fun `make sure correctly signed data is released`() { fun `make sure correctly signed data is released`() {
val keyPair = generateKeyPair() 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 wrappedData = SignedData(serialized, sig)
val unwrappedData = wrappedData.verified() val unwrappedData = wrappedData.verified()
@ -23,7 +23,7 @@ class SignedDataTest {
fun `make sure incorrectly signed data raises an exception`() { fun `make sure incorrectly signed data raises an exception`() {
val keyPairA = generateKeyPair() val keyPairA = generateKeyPair()
val keyPairB = 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) val wrappedData = SignedData(serialized, sig)
wrappedData.verified() wrappedData.verified()
} }

View File

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

View File

@ -9,7 +9,7 @@ import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash 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.flows.FlowLogic
import net.corda.core.node.PluginServiceHub import net.corda.core.node.PluginServiceHub
import net.corda.core.node.ServiceHub 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 // 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 the other side our signature.
send(source, ourSignature) send(source, ourSignature)

View File

@ -250,7 +250,7 @@ class RecordCompletionFlow(val source: Party) : FlowLogic<Unit>() {
} }
// DOCEND 3 // DOCEND 3
// Having verified the SignedTransaction passed to us we can sign it too // 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 our signature to the other party.
send(source, ourSignature) send(source, ourSignature)
// N.B. The FinalityProtocol will be responsible for Notarising the SignedTransaction // 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 { open fun calculateOurSignature(partialTX: SignedTransaction): DigitalSignature.WithKey {
progressTracker.currentStep = SIGNING 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 { fun toWire(privateKey: PrivateKey): WireNodeRegistration {
val regSerialized = this.serialize() 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) return WireNodeRegistration(regSerialized, regSig)
} }

View File

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

View File

@ -54,7 +54,7 @@ class NotaryServiceTests {
val future = runNotaryClient(stx) val future = runNotaryClient(stx)
val signatures = future.getOrThrow() 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`() { @Test fun `should sign a unique transaction without a timestamp`() {
@ -67,7 +67,7 @@ class NotaryServiceTests {
val future = runNotaryClient(stx) val future = runNotaryClient(stx)
val signatures = future.getOrThrow() 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`() { @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.MerkleTreeException
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.keys 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.flows.FlowLogic
import net.corda.core.math.CubicSplineInterpolator import net.corda.core.math.CubicSplineInterpolator
import net.corda.core.math.Interpolator 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 // 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 // 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. // an invalid transaction the signature is worthless.
return signingKey.signWithECDSA(ftx.rootHash.bytes, identity) return signingKey.sign(ftx.rootHash.bytes, identity)
} }
// DOCEND 1 // DOCEND 1
} }

View File

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