Merged in switch-to-ed25519 (pull request )

Switch to ed25519
This commit is contained in:
Andras Slemmer 2016-06-17 10:11:06 +01:00
commit cb1b274d5c
5 changed files with 81 additions and 9 deletions
core
build.gradle
src
main/kotlin/com/r3corda/core
crypto
serialization
test/kotlin/com/r3corda/core/serialization
docs/source

View File

@ -67,6 +67,9 @@ dependencies {
// For JSON
compile "com.fasterxml.jackson.core:jackson-databind:2.5.5"
// Java ed25519 implementation. See https://github.com/str4d/ed25519-java/
compile 'net.i2p.crypto:eddsa:0.1.0'
// Quasar: for the bytecode rewriting for state machines.
quasar "co.paralleluniverse:quasar-core:${quasar_version}:jdk8@jar"
}

View File

@ -1,13 +1,14 @@
package com.r3corda.core.crypto
import com.google.common.io.BaseEncoding
import com.r3corda.core.crypto.Party
import com.r3corda.core.serialization.OpaqueBytes
import com.r3corda.core.serialization.SerializedBytes
import com.r3corda.core.serialization.deserialize
import net.i2p.crypto.eddsa.EdDSAEngine
import java.math.BigInteger
import java.security.*
import java.security.interfaces.ECPublicKey
import net.i2p.crypto.eddsa.KeyPairGenerator as EddsaKeyPairGenerator
// "sealed" here means there can't be any subclasses other than the ones defined here.
sealed class SecureHash private constructor(bits: ByteArray) : OpaqueBytes(bits) {
@ -118,7 +119,7 @@ class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {
/** Utility to simplify the act of signing a byte array */
fun PrivateKey.signWithECDSA(bits: ByteArray): DigitalSignature {
val signer = Signature.getInstance("SHA256withECDSA")
val signer = EdDSAEngine()
signer.initSign(this)
signer.update(bits)
val sig = signer.sign()
@ -140,7 +141,7 @@ fun KeyPair.signWithECDSA(bitsToSign: ByteArray, party: Party): DigitalSignature
/** Utility to simplify the act of verifying a signature */
fun PublicKey.verifyWithECDSA(content: ByteArray, signature: DigitalSignature) {
val verifier = Signature.getInstance("SHA256withECDSA")
val verifier = EdDSAEngine()
verifier.initVerify(this)
verifier.update(content)
if (verifier.verify(signature.bits) == false)
@ -160,4 +161,4 @@ operator fun KeyPair.component1() = this.private
operator fun KeyPair.component2() = this.public
/** A simple wrapper that will make it easier to swap out the EC algorithm we use in future */
fun generateKeyPair() = KeyPairGenerator.getInstance("EC").genKeyPair()
fun generateKeyPair() = EddsaKeyPairGenerator().generateKeyPair()

View File

@ -17,6 +17,11 @@ import com.r3corda.core.crypto.sha256
import com.r3corda.core.node.AttachmentsClassLoader
import com.r3corda.core.node.services.AttachmentStorage
import de.javakaffee.kryoserializers.ArraysAsListSerializer
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
import org.objenesis.strategy.StdInstantiatorStrategy
import java.io.ByteArrayOutputStream
import java.io.ObjectInputStream
@ -29,6 +34,7 @@ import java.util.*
import javax.annotation.concurrent.ThreadSafe
import kotlin.reflect.*
import kotlin.reflect.jvm.javaType
import java.security.PrivateKey
/**
* Serialization utilities, using the Kryo framework with a custom serialiser for immutable data classes and a dead
@ -205,6 +211,16 @@ inline fun <T> Kryo.useClassLoader(cl: ClassLoader, body: () -> T) : T {
}
}
inline fun Output.writeBytesWithLength(byteArray: ByteArray) {
this.writeInt(byteArray.size, true)
this.writeBytes(byteArray)
}
inline fun Input.readBytesWithLength(): ByteArray {
val size = this.readInt(true)
return this.readBytes(size)
}
/** Thrown during deserialisation to indicate that an attachment needed to construct the [WireTransaction] is not found */
class MissingAttachmentsException(val ids: List<SecureHash>) : Exception()
@ -250,6 +266,39 @@ object WireTransactionSerializer : Serializer<WireTransaction>() {
}
}
/** For serialising an ed25519 private key */
@ThreadSafe
object Ed25519PrivateKeySerializer : Serializer<EdDSAPrivateKey>() {
val ed25519Curve = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512)
override fun write(kryo: Kryo, output: Output, obj: EdDSAPrivateKey) {
check(obj.params == ed25519Curve)
output.writeBytesWithLength(obj.seed)
}
override fun read(kryo: Kryo, input: Input, type: Class<EdDSAPrivateKey>): EdDSAPrivateKey {
val seed = input.readBytesWithLength()
return EdDSAPrivateKey(EdDSAPrivateKeySpec(seed, ed25519Curve))
}
}
/** For serialising an ed25519 public key */
@ThreadSafe
object Ed25519PublicKeySerializer : Serializer<EdDSAPublicKey>() {
val ed25519Curve = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512)
override fun write(kryo: Kryo, output: Output, obj: EdDSAPublicKey) {
check(obj.params == ed25519Curve)
output.writeBytesWithLength(obj.abyte)
}
override fun read(kryo: Kryo, input: Input, type: Class<EdDSAPublicKey>): EdDSAPublicKey {
val A = input.readBytesWithLength()
return EdDSAPublicKey(EdDSAPublicKeySpec(A, ed25519Curve))
}
}
fun createKryo(k: Kryo = Kryo()): Kryo {
return k.apply {
// Allow any class to be deserialized (this is insecure but for prototyping we don't care)
@ -273,8 +322,8 @@ fun createKryo(k: Kryo = Kryo()): Kryo {
// Some things where the JRE provides an efficient custom serialisation.
val keyPair = generateKeyPair()
register(keyPair.public.javaClass, ReferencesAwareJavaSerializer)
register(keyPair.private.javaClass, ReferencesAwareJavaSerializer)
register(keyPair.public.javaClass, Ed25519PublicKeySerializer)
register(keyPair.private.javaClass, Ed25519PrivateKeySerializer)
register(Instant::class.java, ReferencesAwareJavaSerializer)
// Some classes have to be handled with the ImmutableClassSerializer because they need to have their

View File

@ -1,7 +1,11 @@
package com.r3corda.core.serialization
import com.google.common.primitives.Ints
import com.r3corda.core.crypto.generateKeyPair
import com.r3corda.core.crypto.signWithECDSA
import com.r3corda.core.crypto.verifyWithECDSA
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test
import java.time.Instant
import java.util.*
@ -54,6 +58,21 @@ class KryoTests {
assertThat(bits.deserialize(kryo)).isEqualTo(cyclic)
}
@Test
fun `deserialised keypair functions the same as serialised one`() {
val keyPair = generateKeyPair()
val bitsToSign: ByteArray = Ints.toByteArray(0x01234567)
val wrongBits: ByteArray = Ints.toByteArray(0x76543210)
val signature = keyPair.signWithECDSA(bitsToSign)
signature.verifyWithECDSA(bitsToSign)
assertThatThrownBy { signature.verifyWithECDSA(wrongBits) }
val deserialisedKeyPair = keyPair.serialize(kryo).deserialize(kryo)
val deserialisedSignature = deserialisedKeyPair.signWithECDSA(bitsToSign)
assertThat(deserialisedSignature).isEqualTo(signature)
deserialisedSignature.verifyWithECDSA(bitsToSign)
assertThatThrownBy { deserialisedSignature.verifyWithECDSA(wrongBits) }
}
private data class Person(val name: String, val birthday: Instant?)
@ -65,4 +84,4 @@ class KryoTests {
override fun toString(): String = "Cyclic($value)"
}
}
}

View File

@ -12,7 +12,7 @@ Here are changes in git master that haven't yet made it to a snapshot release:
* Amount class is now generic, to support non-currency types (such as assets, or currency with additional information).
* Refactored the Cash contract to have a new FungibleAsset superclass, to model all countable assets that can be merged
and split (currency, barrels of oil, etc.)
* Switched to the ed25519 elliptic curve from secp256r1. Note that this introduces a new external lib dependency.
Milestone 0
-----------
@ -24,4 +24,4 @@ This is the first release, which includes:
* The first version of the protocol/orchestration framework
* Some initial support for pluggable consensus mechanisms
* Tutorials and documentation explaining how it works
* Much more ...
* Much more ...