diff --git a/core/src/main/kotlin/com/r3corda/core/crypto/CryptoUtilities.kt b/core/src/main/kotlin/com/r3corda/core/crypto/CryptoUtilities.kt index a87430a359..a70c5b60c8 100644 --- a/core/src/main/kotlin/com/r3corda/core/crypto/CryptoUtilities.kt +++ b/core/src/main/kotlin/com/r3corda/core/crypto/CryptoUtilities.kt @@ -1,9 +1,6 @@ package com.r3corda.core.crypto -import com.google.common.io.BaseEncoding 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 net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPublicKey @@ -22,45 +19,6 @@ fun newSecureRandom(): SecureRandom { } } -// "sealed" here means there can't be any subclasses other than the ones defined here. -sealed class SecureHash(bits: ByteArray) : OpaqueBytes(bits) { - class SHA256(bits: ByteArray) : SecureHash(bits) { - init { - require(bits.size == 32) - } - - override val signatureAlgorithmName: String get() = "SHA256withECDSA" - } - - override fun toString() = BaseEncoding.base16().encode(bits) - - fun prefixChars(prefixLen: Int = 6) = toString().substring(0, prefixLen) - - // Like static methods in Java, except the 'companion' is a singleton that can have state. - companion object { - @JvmStatic - fun parse(str: String) = BaseEncoding.base16().decode(str.toUpperCase()).let { - when (it.size) { - 32 -> SHA256(it) - else -> throw IllegalArgumentException("Provided string is ${it.size} bytes not 32 bytes in hex: $str") - } - } - - @JvmStatic fun sha256(bits: ByteArray) = SHA256(MessageDigest.getInstance("SHA-256").digest(bits)) - @JvmStatic fun sha256Twice(bits: ByteArray) = sha256(sha256(bits).bits) - @JvmStatic fun sha256(str: String) = sha256(str.toByteArray()) - - @JvmStatic fun randomSHA256() = sha256(newSecureRandom().generateSeed(32)) - } - - abstract val signatureAlgorithmName: String - - // In future, maybe SHA3, truncated hashes etc. -} - -fun ByteArray.sha256(): SecureHash.SHA256 = SecureHash.sha256(this) -fun OpaqueBytes.sha256(): SecureHash.SHA256 = SecureHash.sha256(this.bits) - /** * A wrapper around a digital signature. The covering field is a generic tag usable by whatever is interpreting the * signature. It isn't used currently, but experience from Bitcoin suggests such a feature is useful, especially when @@ -76,40 +34,6 @@ open class DigitalSignature(bits: ByteArray) : OpaqueBytes(bits) { class LegallyIdentifiable(val signer: Party, bits: ByteArray) : WithKey(signer.owningKey, bits) } -/** - * A serialized piece of data and its signature. Enforces signature validity in order to deserialize the data - * contained within. - * - * @param raw the raw serialized data. - * @param sig the (unverified) signature for the data. - */ -open class SignedData(val raw: SerializedBytes, val sig: DigitalSignature.WithKey) { - /** - * Return the deserialized data if the signature can be verified. - * - * @throws IllegalArgumentException if the data is invalid (only used if verifyData() is overloaded). - * @throws SignatureException if the signature is invalid. - */ - @Throws(SignatureException::class) - fun verified(): T { - sig.by.verifyWithECDSA(raw.bits, sig) - val data = raw.deserialize() - verifyData(data) - return data - } - - /** - * Verify the wrapped data after the signature has been verified and the data deserialised. Provided as an extension - * point for subclasses. - * - * @throws IllegalArgumentException if the data is invalid. - */ - @Throws(IllegalArgumentException::class) - open protected fun verifyData(data: T) { - // By default we accept anything - } -} - object NullPublicKey : PublicKey, Comparable { override fun getAlgorithm() = "NULL" override fun getEncoded() = byteArrayOf(0) @@ -179,6 +103,7 @@ fun Iterable.toStringsShort(): String = map { it.toStringShort() }.to // Allow Kotlin destructuring: val (private, public) = keypair 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 */ diff --git a/core/src/main/kotlin/com/r3corda/core/crypto/SecureHash.kt b/core/src/main/kotlin/com/r3corda/core/crypto/SecureHash.kt new file mode 100644 index 0000000000..e19bdfd7f8 --- /dev/null +++ b/core/src/main/kotlin/com/r3corda/core/crypto/SecureHash.kt @@ -0,0 +1,43 @@ +package com.r3corda.core.crypto + +import com.google.common.io.BaseEncoding +import com.r3corda.core.serialization.OpaqueBytes +import java.security.MessageDigest + +sealed class SecureHash(bits: ByteArray) : OpaqueBytes(bits) { + class SHA256(bits: ByteArray) : SecureHash(bits) { + init { + require(bits.size == 32) + } + + override val signatureAlgorithmName: String get() = "SHA256withECDSA" + } + + override fun toString() = BaseEncoding.base16().encode(bits) + + fun prefixChars(prefixLen: Int = 6) = toString().substring(0, prefixLen) + + // Like static methods in Java, except the 'companion' is a singleton that can have state. + companion object { + @JvmStatic + fun parse(str: String) = BaseEncoding.base16().decode(str.toUpperCase()).let { + when (it.size) { + 32 -> SHA256(it) + else -> throw IllegalArgumentException("Provided string is ${it.size} bytes not 32 bytes in hex: $str") + } + } + + @JvmStatic fun sha256(bits: ByteArray) = SHA256(MessageDigest.getInstance("SHA-256").digest(bits)) + @JvmStatic fun sha256Twice(bits: ByteArray) = sha256(sha256(bits).bits) + @JvmStatic fun sha256(str: String) = sha256(str.toByteArray()) + + @JvmStatic fun randomSHA256() = sha256(newSecureRandom().generateSeed(32)) + } + + abstract val signatureAlgorithmName: String + + // In future, maybe SHA3, truncated hashes etc. +} + +fun ByteArray.sha256(): SecureHash.SHA256 = SecureHash.sha256(this) +fun OpaqueBytes.sha256(): SecureHash.SHA256 = SecureHash.sha256(this.bits) \ No newline at end of file diff --git a/core/src/main/kotlin/com/r3corda/core/crypto/SignedData.kt b/core/src/main/kotlin/com/r3corda/core/crypto/SignedData.kt new file mode 100644 index 0000000000..c2afd6f683 --- /dev/null +++ b/core/src/main/kotlin/com/r3corda/core/crypto/SignedData.kt @@ -0,0 +1,39 @@ +package com.r3corda.core.crypto + +import com.r3corda.core.serialization.SerializedBytes +import com.r3corda.core.serialization.deserialize +import java.security.SignatureException + +/** + * A serialized piece of data and its signature. Enforces signature validity in order to deserialize the data + * contained within. + * + * @param raw the raw serialized data. + * @param sig the (unverified) signature for the data. + */ +open class SignedData(val raw: SerializedBytes, val sig: DigitalSignature.WithKey) { + /** + * Return the deserialized data if the signature can be verified. + * + * @throws IllegalArgumentException if the data is invalid (only used if verifyData() is overloaded). + * @throws SignatureException if the signature is invalid. + */ + @Throws(SignatureException::class) + fun verified(): T { + sig.by.verifyWithECDSA(raw.bits, sig) + val data = raw.deserialize() + verifyData(data) + return data + } + + /** + * Verify the wrapped data after the signature has been verified and the data deserialised. Provided as an extension + * point for subclasses. + * + * @throws IllegalArgumentException if the data is invalid. + */ + @Throws(IllegalArgumentException::class) + open protected fun verifyData(data: T) { + // By default we accept anything + } +} \ No newline at end of file