mirror of
https://github.com/corda/corda.git
synced 2025-01-30 16:14:39 +00:00
Merged andrius-crypto-refactor into master
This commit is contained in:
commit
ac57c5cf86
@ -1,9 +1,6 @@
|
|||||||
package com.r3corda.core.crypto
|
package com.r3corda.core.crypto
|
||||||
|
|
||||||
import com.google.common.io.BaseEncoding
|
|
||||||
import com.r3corda.core.serialization.OpaqueBytes
|
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.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
|
||||||
@ -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
|
* 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
|
* 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)
|
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<T : Any>(val raw: SerializedBytes<T>, 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<PublicKey> {
|
object NullPublicKey : PublicKey, Comparable<PublicKey> {
|
||||||
override fun getAlgorithm() = "NULL"
|
override fun getAlgorithm() = "NULL"
|
||||||
override fun getEncoded() = byteArrayOf(0)
|
override fun getEncoded() = byteArrayOf(0)
|
||||||
@ -179,6 +103,7 @@ fun Iterable<PublicKey>.toStringsShort(): String = map { it.toStringShort() }.to
|
|||||||
|
|
||||||
// Allow Kotlin destructuring: val (private, public) = keyPair
|
// Allow Kotlin destructuring: val (private, public) = keyPair
|
||||||
operator fun KeyPair.component1() = this.private
|
operator fun KeyPair.component1() = this.private
|
||||||
|
|
||||||
operator fun KeyPair.component2() = this.public
|
operator fun KeyPair.component2() = this.public
|
||||||
|
|
||||||
/** A simple wrapper that will make it easier to swap out the EC algorithm we use in future */
|
/** A simple wrapper that will make it easier to swap out the EC algorithm we use in future */
|
||||||
|
44
core/src/main/kotlin/com/r3corda/core/crypto/SecureHash.kt
Normal file
44
core/src/main/kotlin/com/r3corda/core/crypto/SecureHash.kt
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package com.r3corda.core.crypto
|
||||||
|
|
||||||
|
import com.google.common.io.BaseEncoding
|
||||||
|
import com.r3corda.core.serialization.OpaqueBytes
|
||||||
|
import java.security.MessageDigest
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container for a cryptographically secure hash value.
|
||||||
|
* Provides utilities for generating a cryptographic hash using different algorithms (currently only SHA-256 supported).
|
||||||
|
*/
|
||||||
|
sealed class SecureHash(bits: ByteArray) : OpaqueBytes(bits) {
|
||||||
|
/** SHA-256 is part of the SHA-2 hash function family. Generated hash is fixed size, 256-bits (32-bytes) */
|
||||||
|
class SHA256(bits: ByteArray) : SecureHash(bits) {
|
||||||
|
init {
|
||||||
|
require(bits.size == 32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
// In future, maybe SHA3, truncated hashes etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ByteArray.sha256(): SecureHash.SHA256 = SecureHash.sha256(this)
|
||||||
|
fun OpaqueBytes.sha256(): SecureHash.SHA256 = SecureHash.sha256(this.bits)
|
39
core/src/main/kotlin/com/r3corda/core/crypto/SignedData.kt
Normal file
39
core/src/main/kotlin/com/r3corda/core/crypto/SignedData.kt
Normal file
@ -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<T : Any>(val raw: SerializedBytes<T>, 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
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user