diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index f455e61871..f0b65c5a84 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -93,6 +93,7 @@
+
diff --git a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt
index ab6ea15722..605b95466c 100644
--- a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt
+++ b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt
@@ -1,5 +1,6 @@
package net.corda.core.crypto
+import net.corda.core.internal.X509EdDSAEngine
import net.corda.core.serialization.serialize
import net.i2p.crypto.eddsa.*
import net.i2p.crypto.eddsa.math.GroupElement
diff --git a/core/src/main/kotlin/net/i2p/crypto/eddsa/X509EdDSAEngine.kt b/core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt
similarity index 67%
rename from core/src/main/kotlin/net/i2p/crypto/eddsa/X509EdDSAEngine.kt
rename to core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt
index 9ce017a484..0bebc73130 100644
--- a/core/src/main/kotlin/net/i2p/crypto/eddsa/X509EdDSAEngine.kt
+++ b/core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt
@@ -1,5 +1,7 @@
-package net.i2p.crypto.eddsa
+package net.corda.core.internal
+import net.i2p.crypto.eddsa.EdDSAEngine
+import net.i2p.crypto.eddsa.EdDSAPublicKey
import java.security.*
import java.security.spec.AlgorithmParameterSpec
import java.security.spec.X509EncodedKeySpec
@@ -16,33 +18,34 @@ class X509EdDSAEngine : Signature {
constructor() : super(EdDSAEngine.SIGNATURE_ALGORITHM) {
engine = EdDSAEngine()
}
+
constructor(digest: MessageDigest) : super(EdDSAEngine.SIGNATURE_ALGORITHM) {
engine = EdDSAEngine(digest)
}
- override fun engineInitSign(privateKey: PrivateKey) = engine.engineInitSign(privateKey)
+ override fun engineInitSign(privateKey: PrivateKey) = engine.initSign(privateKey)
+ override fun engineInitSign(privateKey: PrivateKey, random: SecureRandom) = engine.initSign(privateKey, random)
+
override fun engineInitVerify(publicKey: PublicKey) {
val parsedKey = if (publicKey is sun.security.x509.X509Key) {
EdDSAPublicKey(X509EncodedKeySpec(publicKey.encoded))
} else {
publicKey
}
- engine.engineInitVerify(parsedKey)
+
+ engine.initVerify(parsedKey)
}
- override fun engineVerify(sigBytes: ByteArray): Boolean = engine.engineVerify(sigBytes)
- override fun engineSign(): ByteArray = engine.engineSign()
- override fun engineUpdate(b: Byte) = engine.engineUpdate(b)
- override fun engineUpdate(b: ByteArray, off: Int, len: Int) = engine.engineUpdate(b, off, len)
- override fun engineGetParameters(): AlgorithmParameters {
- val method = engine.javaClass.getMethod("engineGetParameters")
- return method.invoke(engine) as AlgorithmParameters
- }
+ override fun engineSign(): ByteArray = engine.sign()
+ override fun engineVerify(sigBytes: ByteArray): Boolean = engine.verify(sigBytes)
+
+ override fun engineUpdate(b: Byte) = engine.update(b)
+ override fun engineUpdate(b: ByteArray, off: Int, len: Int) = engine.update(b, off, len)
+
+ override fun engineGetParameters(): AlgorithmParameters = engine.parameters
override fun engineSetParameter(params: AlgorithmParameterSpec) = engine.setParameter(params)
- override fun engineGetParameter(param: String): Any = engine.engineGetParameter(param)
- override fun engineSetParameter(param: String, value: Any?) = engine.engineSetParameter(param, value)
- override fun engineInitSign(privateKey: PrivateKey, random: SecureRandom) {
- val method = engine.javaClass.getMethod("engineInitSign", PrivateKey::class.java, SecureRandom::class.java)
- method.invoke(engine, privateKey, random)
- }
+ @Suppress("DEPRECATION")
+ override fun engineGetParameter(param: String): Any = engine.getParameter(param)
+ @Suppress("DEPRECATION")
+ override fun engineSetParameter(param: String, value: Any?) = engine.setParameter(param, value)
}
diff --git a/core/src/test/kotlin/net/corda/core/internal/X509EdDSAEngineTest.kt b/core/src/test/kotlin/net/corda/core/internal/X509EdDSAEngineTest.kt
new file mode 100644
index 0000000000..3f79a6ce36
--- /dev/null
+++ b/core/src/test/kotlin/net/corda/core/internal/X509EdDSAEngineTest.kt
@@ -0,0 +1,117 @@
+package net.corda.core.internal
+
+import net.corda.core.crypto.Crypto
+import net.i2p.crypto.eddsa.EdDSAEngine
+import net.i2p.crypto.eddsa.EdDSAPublicKey
+import org.junit.Test
+import sun.security.util.BitArray
+import sun.security.util.ObjectIdentifier
+import sun.security.x509.AlgorithmId
+import sun.security.x509.X509Key
+import java.math.BigInteger
+import java.security.InvalidKeyException
+import java.util.*
+import kotlin.test.assertFailsWith
+import kotlin.test.assertTrue
+
+class TestX509Key(algorithmId: AlgorithmId, key: BitArray) : X509Key() {
+ init {
+ this.algid = algorithmId
+ this.setKey(key)
+ this.encode()
+ }
+}
+
+class X509EdDSAEngineTest {
+ companion object {
+ private const val SEED = 20170920L
+ private const val TEST_DATA_SIZE = 2000
+
+ // offset into an EdDSA header indicating where the key header and actual key start
+ // in the underlying byte array
+ private const val keyHeaderStart = 9
+ private const val keyStart = 12
+
+ private fun toX509Key(publicKey: EdDSAPublicKey): X509Key {
+ val internals = publicKey.encoded
+
+ // key size in the header includes the count unused bits at the end of the key
+ // [keyHeaderStart + 2] but NOT the key header ID [keyHeaderStart] so the
+ // actual length of the key blob is size - 1
+ val keySize = (internals[keyHeaderStart + 1].toInt()) - 1
+
+ val key = ByteArray(keySize)
+ System.arraycopy(internals, keyStart, key, 0, keySize)
+
+ // 1.3.101.102 is the EdDSA OID
+ return TestX509Key(AlgorithmId(ObjectIdentifier("1.3.101.112")), BitArray(keySize * 8, key))
+ }
+ }
+
+ /**
+ * Put the X509EdDSA engine through basic tests to verify that the functions are hooked up correctly.
+ */
+ @Test
+ fun `sign and verify`() {
+ val engine = X509EdDSAEngine()
+ val keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger.valueOf(SEED))
+ val publicKey = keyPair.public as EdDSAPublicKey
+ val randomBytes = ByteArray(TEST_DATA_SIZE)
+ Random(SEED).nextBytes(randomBytes)
+ engine.initSign(keyPair.private)
+ engine.update(randomBytes[0])
+ engine.update(randomBytes, 1, randomBytes.size - 1)
+
+ // Now verify the signature
+ val signature = engine.sign()
+
+ engine.initVerify(publicKey)
+ engine.update(randomBytes)
+ assertTrue { engine.verify(signature) }
+ }
+
+ /**
+ * Verify that signing with an X509Key wrapped EdDSA key works.
+ */
+ @Test
+ fun `sign and verify with X509Key`() {
+ val engine = X509EdDSAEngine()
+ val keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger.valueOf(SEED + 1))
+ val publicKey = toX509Key(keyPair.public as EdDSAPublicKey)
+ val randomBytes = ByteArray(TEST_DATA_SIZE)
+ Random(SEED + 1).nextBytes(randomBytes)
+ engine.initSign(keyPair.private)
+ engine.update(randomBytes[0])
+ engine.update(randomBytes, 1, randomBytes.size - 1)
+
+ // Now verify the signature
+ val signature = engine.sign()
+
+ engine.initVerify(publicKey)
+ engine.update(randomBytes)
+ assertTrue { engine.verify(signature) }
+ }
+
+ /**
+ * Verify that signing with an X509Key wrapped EdDSA key fails when using the underlying EdDSAEngine.
+ */
+ @Test
+ fun `sign and verify with X509Key and old engine fails`() {
+ val engine = EdDSAEngine()
+ val keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger.valueOf(SEED + 1))
+ val publicKey = toX509Key(keyPair.public as EdDSAPublicKey)
+ val randomBytes = ByteArray(TEST_DATA_SIZE)
+ Random(SEED + 1).nextBytes(randomBytes)
+ engine.initSign(keyPair.private)
+ engine.update(randomBytes[0])
+ engine.update(randomBytes, 1, randomBytes.size - 1)
+
+ // Now verify the signature
+ val signature = engine.sign()
+ assertFailsWith {
+ engine.initVerify(publicKey)
+ engine.update(randomBytes)
+ engine.verify(signature)
+ }
+ }
+}
\ No newline at end of file