CORDA-579: Move X509EdDSAEngine into net.corda.core.internal package (#1563)

* Add reflection based X509EdDSAEngine

* Rewrite X509EdDSAEngine to use public API rather than the direct equivalent functions

* Add unit tests for X509EdDSAEngine

* Remove unused imports

* Add unit tests for X509Key verification

* Add explicit x509 construct from eddsa key

This allows testing of conversion engine

* Review Comments
This commit is contained in:
Ross Nicoll 2017-09-21 18:18:37 +01:00 committed by josecoll
parent 29e648d11c
commit 22be9fd6df
4 changed files with 139 additions and 17 deletions

1
.idea/compiler.xml generated
View File

@ -93,6 +93,7 @@
<module name="smoke-test-utils_test" target="1.8" />
<module name="test-common_main" target="1.8" />
<module name="test-common_test" target="1.8" />
<module name="test-utils_integrationTest" target="1.8" />
<module name="test-utils_main" target="1.8" />
<module name="test-utils_test" target="1.8" />
<module name="testing-node-driver_integrationTest" target="1.8" />

View File

@ -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

View File

@ -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)
}

View File

@ -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<InvalidKeyException> {
engine.initVerify(publicKey)
engine.update(randomBytes)
engine.verify(signature)
}
}
}