Extract methods and classes from CryptoUtilities. Keep one utils file.

This commit is contained in:
Konstantinos Chalkias 2017-04-18 12:36:57 +01:00 committed by GitHub
parent a1122c0804
commit 66890d845a
4 changed files with 221 additions and 223 deletions

View File

@ -1,216 +0,0 @@
@file:JvmName("CryptoUtilities")
package net.corda.core.crypto
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.i2p.crypto.eddsa.EdDSAEngine
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import net.i2p.crypto.eddsa.KeyPairGenerator
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
import java.math.BigInteger
import java.security.InvalidKeyException
import java.security.KeyPair
import java.security.PrivateKey
import java.security.PublicKey
import java.security.SignatureException
// TODO: Is there a use-case for bare [DigitalSignature], or is everything a [DigitalSignature.WithKey]? If there's no
// actual use-case, we should merge the with key version into the parent class. In that case [CompositeSignatureWithKeys]
// should be renamed to match.
/** A wrapper around a digital signature. */
@CordaSerializable
open class DigitalSignature(bits: ByteArray) : OpaqueBytes(bits) {
/** A digital signature that identifies who the public key is owned by. */
open class WithKey(val by: PublicKey, bits: ByteArray) : DigitalSignature(bits) {
/**
* Utility to simplify the act of verifying a signature.
*
* @throws InvalidKeyException if the key to verify the signature with is not valid (i.e. wrong key type for the
* signature).
* @throws SignatureException if the signature is invalid (i.e. damaged), or does not match the key (incorrect).
*/
@Throws(InvalidKeyException::class, SignatureException::class)
fun verifyWithECDSA(content: ByteArray) = by.verifyWithECDSA(content, this)
/**
* Utility to simplify the act of verifying a signature.
*
* @throws InvalidKeyException if the key to verify the signature with is not valid (i.e. wrong key type for the
* signature).
* @throws SignatureException if the signature is invalid (i.e. damaged), or does not match the key (incorrect).
*/
@Throws(InvalidKeyException::class, SignatureException::class)
fun verifyWithECDSA(content: OpaqueBytes) = by.verifyWithECDSA(content.bytes, this)
/**
* Utility to simplify the act of verifying a signature. In comparison to [verifyWithECDSA] doesn't throw an
* exception, making it more suitable where a boolean is required, but normally you should use the function
* which throws, as it avoids the risk of failing to test the result.
*
* @throws InvalidKeyException if the key to verify the signature with is not valid (i.e. wrong key type for the
* signature).
* @throws SignatureException if the signature is invalid (i.e. damaged).
* @return whether the signature is correct for this key.
*/
@Throws(InvalidKeyException::class, SignatureException::class)
fun isValidForECDSA(content: ByteArray) = by.isValidForECDSA(content, this)
}
// TODO: consider removing this as whoever needs to identify the signer should be able to derive it from the public key
class LegallyIdentifiable(val signer: Party, bits: ByteArray) : WithKey(signer.owningKey, bits)
}
@CordaSerializable
object NullPublicKey : PublicKey, Comparable<PublicKey> {
override fun getAlgorithm() = "NULL"
override fun getEncoded() = byteArrayOf(0)
override fun getFormat() = "NULL"
override fun compareTo(other: PublicKey): Int = if (other == NullPublicKey) 0 else -1
override fun toString() = "NULL_KEY"
}
// TODO: Clean up this duplication between Null and Dummy public key
@CordaSerializable
class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {
override fun getAlgorithm() = "DUMMY"
override fun getEncoded() = s.toByteArray()
override fun getFormat() = "ASN.1"
override fun compareTo(other: PublicKey): Int = BigInteger(encoded).compareTo(BigInteger(other.encoded))
override fun equals(other: Any?) = other is DummyPublicKey && other.s == s
override fun hashCode(): Int = s.hashCode()
override fun toString() = "PUBKEY[$s]"
}
/** A signature with a key and value of zero. Useful when you want a signature object that you know won't ever be used. */
@CordaSerializable
object NullSignature : DigitalSignature.WithKey(NullPublicKey, ByteArray(32))
/** Utility to simplify the act of signing a byte array */
fun PrivateKey.signWithECDSA(bytes: ByteArray): DigitalSignature {
val signer = EdDSAEngine()
signer.initSign(this)
signer.update(bytes)
val sig = signer.sign()
return DigitalSignature(sig)
}
fun PrivateKey.signWithECDSA(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
return DigitalSignature.WithKey(publicKey, signWithECDSA(bytesToSign).bytes)
}
val ed25519Curve: EdDSANamedCurveSpec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512)
// TODO We use for both CompositeKeys and EdDSAPublicKey custom Kryo serializers and deserializers. We need to specify encoding.
// TODO: follow the crypto-conditions ASN.1 spec, some changes are needed to be compatible with the condition
// structure, e.g. mapping a PublicKey to a condition with the specific feature (ED25519).
fun parsePublicKeyBase58(base58String: String): PublicKey = Base58.decode(base58String).deserialize<PublicKey>()
fun PublicKey.toBase58String(): String = Base58.encode(this.serialize().bytes)
fun KeyPair.signWithECDSA(bytesToSign: ByteArray) = private.signWithECDSA(bytesToSign, public)
fun KeyPair.signWithECDSA(bytesToSign: OpaqueBytes) = private.signWithECDSA(bytesToSign.bytes, public)
fun KeyPair.signWithECDSA(bytesToSign: OpaqueBytes, party: Party) = signWithECDSA(bytesToSign.bytes, party)
// TODO This case will need more careful thinking, as party owningKey can be a CompositeKey. One way of doing that is
// implementation of CompositeSignature.
@Throws(InvalidKeyException::class)
fun KeyPair.signWithECDSA(bytesToSign: ByteArray, party: Party): DigitalSignature.LegallyIdentifiable {
val sig = signWithECDSA(bytesToSign)
val sigKey = when (party.owningKey) { // Quick workaround when we have CompositeKey as Party owningKey.
is CompositeKey -> throw InvalidKeyException("Signing for parties with CompositeKey not supported.")
else -> party.owningKey
}
sigKey.verifyWithECDSA(bytesToSign, sig)
return DigitalSignature.LegallyIdentifiable(party, sig.bytes)
}
/**
* Utility to simplify the act of verifying a signature.
*
* @throws InvalidKeyException if the key to verify the signature with is not valid (i.e. wrong key type for the
* signature).
* @throws SignatureException if the signature is invalid (i.e. damaged), or does not match the key (incorrect).
*/
// TODO: SignatureException should be used only for a damaged signature, as per `java.security.Signature.verify()`,
// we should use another exception (perhaps IllegalArgumentException) for indicating the signature is valid but does
// not match.
@Throws(IllegalStateException::class, SignatureException::class)
fun PublicKey.verifyWithECDSA(content: ByteArray, signature: DigitalSignature) {
if (!isValidForECDSA(content, signature))
throw SignatureException("Signature did not match")
}
/**
* Utility to simplify the act of verifying a signature. In comparison to [verifyWithECDSA] if the key and signature
* do not match it returns false rather than throwing an exception. Normally you should use the function which throws,
* as it avoids the risk of failing to test the result, but this is for uses such as [java.security.Signature.verify]
* implementations.
*
* @throws InvalidKeyException if the key to verify the signature with is not valid (i.e. wrong key type for the
* signature).
* @throws SignatureException if the signature is invalid (i.e. damaged).
* @return whether the signature is correct for this key.
*/
@Throws(IllegalStateException::class, SignatureException::class)
fun PublicKey.isValidForECDSA(content: ByteArray, signature: DigitalSignature) : Boolean {
val pubKey = when (this) {
is CompositeKey -> throw IllegalStateException("Verification of CompositeKey signatures currently not supported.") // TODO CompositeSignature verification.
else -> this
}
val verifier = EdDSAEngine()
verifier.initVerify(pubKey)
verifier.update(content)
return verifier.verify(signature.bytes)
}
/** Render a public key to a string, using a short form if it's an elliptic curve public key */
fun PublicKey.toStringShort(): String {
return (this as? EdDSAPublicKey)?.let { key ->
"DL" + Base58.encode(key.abyte) // DL -> Distributed Ledger
} ?: toString()
}
val PublicKey.keys: Set<PublicKey> get() {
return if (this is CompositeKey) this.leavesKeys
else setOf(this)
}
fun PublicKey.isFulfilledBy(otherKey: PublicKey): Boolean = isFulfilledBy(setOf(otherKey))
fun PublicKey.isFulfilledBy(otherKeys: Iterable<PublicKey>): Boolean {
return if (this is CompositeKey) this.isFulfilledBy(otherKeys)
else this in otherKeys
}
/** Checks whether any of the given [keys] matches a leaf on the CompositeKey tree or a single PublicKey */
fun PublicKey.containsAny(otherKeys: Iterable<PublicKey>): Boolean {
return if (this is CompositeKey) keys.intersect(otherKeys).isNotEmpty()
else this in otherKeys
}
/** Returns the set of all [PublicKey]s of the signatures */
fun Iterable<DigitalSignature.WithKey>.byKeys() = map { it.by }.toSet()
// Allow Kotlin destructuring: val (private, public) = keyPair
operator fun KeyPair.component1(): PrivateKey = this.private
operator fun KeyPair.component2(): PublicKey = this.public
/** A simple wrapper that will make it easier to swap out the EC algorithm we use in future */
fun generateKeyPair(): KeyPair = KeyPairGenerator().generateKeyPair()
/**
* Returns a key pair derived from the given private key entropy. This is useful for unit tests and other cases where
* you want hard-coded private keys.
*/
fun entropyToKeyPair(entropy: BigInteger): KeyPair {
val params = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512)
val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8)
val priv = EdDSAPrivateKeySpec(bytes, params)
val pub = EdDSAPublicKeySpec(priv.a, params)
val key = KeyPair(EdDSAPublicKey(pub), EdDSAPrivateKey(priv))
return key
}

View File

@ -1,7 +1,163 @@
@file:JvmName("CryptoUtils")
package net.corda.core.crypto
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes
import net.i2p.crypto.eddsa.EdDSAEngine
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import net.i2p.crypto.eddsa.KeyPairGenerator
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
import java.math.BigInteger
import java.security.*
@CordaSerializable
object NullPublicKey : PublicKey, Comparable<PublicKey> {
override fun getAlgorithm() = "NULL"
override fun getEncoded() = byteArrayOf(0)
override fun getFormat() = "NULL"
override fun compareTo(other: PublicKey): Int = if (other == NullPublicKey) 0 else -1
override fun toString() = "NULL_KEY"
}
// TODO: Clean up this duplication between Null and Dummy public key
@CordaSerializable
class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {
override fun getAlgorithm() = "DUMMY"
override fun getEncoded() = s.toByteArray()
override fun getFormat() = "ASN.1"
override fun compareTo(other: PublicKey): Int = BigInteger(encoded).compareTo(BigInteger(other.encoded))
override fun equals(other: Any?) = other is DummyPublicKey && other.s == s
override fun hashCode(): Int = s.hashCode()
override fun toString() = "PUBKEY[$s]"
}
/** A signature with a key and value of zero. Useful when you want a signature object that you know won't ever be used. */
@CordaSerializable
object NullSignature : DigitalSignature.WithKey(NullPublicKey, ByteArray(32))
/** Utility to simplify the act of signing a byte array */
fun PrivateKey.signWithECDSA(bytes: ByteArray): DigitalSignature {
val signer = EdDSAEngine()
signer.initSign(this)
signer.update(bytes)
val sig = signer.sign()
return DigitalSignature(sig)
}
fun PrivateKey.signWithECDSA(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
return DigitalSignature.WithKey(publicKey, signWithECDSA(bytesToSign).bytes)
}
val ed25519Curve: EdDSANamedCurveSpec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512)
fun KeyPair.signWithECDSA(bytesToSign: ByteArray) = private.signWithECDSA(bytesToSign, public)
fun KeyPair.signWithECDSA(bytesToSign: OpaqueBytes) = private.signWithECDSA(bytesToSign.bytes, public)
fun KeyPair.signWithECDSA(bytesToSign: OpaqueBytes, party: Party) = signWithECDSA(bytesToSign.bytes, party)
// TODO This case will need more careful thinking, as party owningKey can be a CompositeKey. One way of doing that is
// implementation of CompositeSignature.
@Throws(InvalidKeyException::class)
fun KeyPair.signWithECDSA(bytesToSign: ByteArray, party: Party): DigitalSignature.LegallyIdentifiable {
val sig = signWithECDSA(bytesToSign)
val sigKey = when (party.owningKey) { // Quick workaround when we have CompositeKey as Party owningKey.
is CompositeKey -> throw InvalidKeyException("Signing for parties with CompositeKey not supported.")
else -> party.owningKey
}
sigKey.verifyWithECDSA(bytesToSign, sig)
return DigitalSignature.LegallyIdentifiable(party, sig.bytes)
}
/**
* Utility to simplify the act of verifying a signature.
*
* @throws InvalidKeyException if the key to verify the signature with is not valid (i.e. wrong key type for the
* signature).
* @throws SignatureException if the signature is invalid (i.e. damaged), or does not match the key (incorrect).
*/
// TODO: SignatureException should be used only for a damaged signature, as per `java.security.Signature.verify()`,
// we should use another exception (perhaps IllegalArgumentException) for indicating the signature is valid but does
// not match.
@Throws(IllegalStateException::class, SignatureException::class)
fun PublicKey.verifyWithECDSA(content: ByteArray, signature: DigitalSignature) {
if (!isValidForECDSA(content, signature))
throw SignatureException("Signature did not match")
}
/**
* Utility to simplify the act of verifying a signature. In comparison to [verifyWithECDSA] if the key and signature
* do not match it returns false rather than throwing an exception. Normally you should use the function which throws,
* as it avoids the risk of failing to test the result, but this is for uses such as [java.security.Signature.verify]
* implementations.
*
* @throws InvalidKeyException if the key to verify the signature with is not valid (i.e. wrong key type for the
* signature).
* @throws SignatureException if the signature is invalid (i.e. damaged).
* @return whether the signature is correct for this key.
*/
@Throws(IllegalStateException::class, SignatureException::class)
fun PublicKey.isValidForECDSA(content: ByteArray, signature: DigitalSignature) : Boolean {
val pubKey = when (this) {
is CompositeKey -> throw IllegalStateException("Verification of CompositeKey signatures currently not supported.") // TODO CompositeSignature verification.
else -> this
}
val verifier = EdDSAEngine()
verifier.initVerify(pubKey)
verifier.update(content)
return verifier.verify(signature.bytes)
}
/** Render a public key to a string, using a short form if it's an elliptic curve public key */
fun PublicKey.toStringShort(): String {
return (this as? EdDSAPublicKey)?.let { key ->
"DL" + key.abyte.toBase58() // DL -> Distributed Ledger
} ?: toString()
}
val PublicKey.keys: Set<PublicKey> get() {
return if (this is CompositeKey) this.leavesKeys
else setOf(this)
}
fun PublicKey.isFulfilledBy(otherKey: PublicKey): Boolean = isFulfilledBy(setOf(otherKey))
fun PublicKey.isFulfilledBy(otherKeys: Iterable<PublicKey>): Boolean {
return if (this is CompositeKey) this.isFulfilledBy(otherKeys)
else this in otherKeys
}
/** Checks whether any of the given [keys] matches a leaf on the CompositeKey tree or a single PublicKey */
fun PublicKey.containsAny(otherKeys: Iterable<PublicKey>): Boolean {
return if (this is CompositeKey) keys.intersect(otherKeys).isNotEmpty()
else this in otherKeys
}
/** Returns the set of all [PublicKey]s of the signatures */
fun Iterable<DigitalSignature.WithKey>.byKeys() = map { it.by }.toSet()
// Allow Kotlin destructuring: val (private, public) = keyPair
operator fun KeyPair.component1(): PrivateKey = this.private
operator fun KeyPair.component2(): PublicKey = this.public
/** A simple wrapper that will make it easier to swap out the EC algorithm we use in future */
fun generateKeyPair(): KeyPair = KeyPairGenerator().generateKeyPair()
/**
* Returns a key pair derived from the given private key entropy. This is useful for unit tests and other cases where
* you want hard-coded private keys.
*/
fun entropyToKeyPair(entropy: BigInteger): KeyPair {
val params = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512)
val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8)
val priv = EdDSAPrivateKeySpec(bytes, params)
val pub = EdDSAPublicKeySpec(priv.a, params)
val key = KeyPair(EdDSAPublicKey(pub), EdDSAPrivateKey(priv))
return key
}
/**
* Helper function for signing.
* @param clearData the data/message to be signed in [ByteArray] form (usually the Merkle root).
@ -15,8 +171,8 @@ fun PrivateKey.sign(clearData: ByteArray): ByteArray = Crypto.doSign(this, clear
/**
* Helper function for signing.
* @param metaDataFull tha attached MetaData object.
* @return a [DSWithMetaDataFull] object.
* @param metaData tha attached MetaData object.
* @return a [TransactionSignature] object.
* @throws IllegalArgumentException if the signature scheme is not supported for this private key.
* @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key.
@ -65,7 +221,7 @@ fun PublicKey.verify(transactionSignature: TransactionSignature): Boolean {
/**
* Helper function for the signers to verify their own signature.
* @param signature the signature on a message.
* @param signatureData the signature on a message.
* @param clearData the clear data/message that was signed (usually the Merkle root).
* @throws InvalidKeyException if the key is invalid.
* @throws SignatureException if this signatureData object is not initialized properly,
@ -85,7 +241,7 @@ fun KeyPair.verify(signatureData: ByteArray, clearData: ByteArray): Boolean = Cr
* which should never happen and suggests an unusual JVM or non-standard Java library.
*/
@Throws(NoSuchAlgorithmException::class)
fun safeRandomBytes(numOfBytes: Int): ByteArray {
fun secureRandomBytes(numOfBytes: Int): ByteArray {
return newSecureRandom().generateSeed(numOfBytes)
}

View File

@ -0,0 +1,51 @@
package net.corda.core.crypto
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes
import java.security.InvalidKeyException
import java.security.PublicKey
import java.security.SignatureException
// TODO: Is there a use-case for bare [DigitalSignature], or is everything a [DigitalSignature.WithKey]? If there's no
// actual use-case, we should merge the with key version into the parent class. In that case [CompositeSignatureWithKeys]
// should be renamed to match.
/** A wrapper around a digital signature. */
@CordaSerializable
open class DigitalSignature(bits: ByteArray) : OpaqueBytes(bits) {
/** A digital signature that identifies who the public key is owned by. */
open class WithKey(val by: PublicKey, bits: ByteArray) : DigitalSignature(bits) {
/**
* Utility to simplify the act of verifying a signature.
*
* @throws InvalidKeyException if the key to verify the signature with is not valid (i.e. wrong key type for the
* signature).
* @throws SignatureException if the signature is invalid (i.e. damaged), or does not match the key (incorrect).
*/
@Throws(InvalidKeyException::class, SignatureException::class)
fun verifyWithECDSA(content: ByteArray) = by.verifyWithECDSA(content, this)
/**
* Utility to simplify the act of verifying a signature.
*
* @throws InvalidKeyException if the key to verify the signature with is not valid (i.e. wrong key type for the
* signature).
* @throws SignatureException if the signature is invalid (i.e. damaged), or does not match the key (incorrect).
*/
@Throws(InvalidKeyException::class, SignatureException::class)
fun verifyWithECDSA(content: OpaqueBytes) = by.verifyWithECDSA(content.bytes, this)
/**
* Utility to simplify the act of verifying a signature. In comparison to [verifyWithECDSA] doesn't throw an
* exception, making it more suitable where a boolean is required, but normally you should use the function
* which throws, as it avoids the risk of failing to test the result.
*
* @throws InvalidKeyException if the key to verify the signature with is not valid (i.e. wrong key type for the
* signature).
* @throws SignatureException if the signature is invalid (i.e. damaged).
* @return whether the signature is correct for this key.
*/
@Throws(InvalidKeyException::class, SignatureException::class)
fun isValidForECDSA(content: ByteArray) = by.isValidForECDSA(content, this)
}
// TODO: consider removing this as whoever needs to identify the signer should be able to derive it from the public key
class LegallyIdentifiable(val signer: Party, bits: ByteArray) : WithKey(signer.owningKey, bits)
}

View File

@ -1,10 +1,14 @@
@file:JvmName("EncodingUtils")
package net.corda.core.crypto
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import java.nio.charset.Charset
import java.security.PublicKey
import java.util.*
import javax.xml.bind.DatatypeConverter
// This file includes useful encoding methods and extension functions for the most common encoding/decoding operations.
// [ByteArray] encoders
@ -56,5 +60,8 @@ fun String.hexToBase58(): String = hexToByteArray().toBase58()
/** Encoding changer. Hex-[String] to Base64-[String], i.e. "48656C6C6F20576F726C64" -> "SGVsbG8gV29ybGQ=" */
fun String.hexToBase64(): String = hexToByteArray().toBase64()
// Helper vars.
private val HEX_ALPHABET = "0123456789ABCDEF".toCharArray()
// TODO We use for both CompositeKeys and EdDSAPublicKey custom Kryo serializers and deserializers. We need to specify encoding.
// TODO: follow the crypto-conditions ASN.1 spec, some changes are needed to be compatible with the condition
// structure, e.g. mapping a PublicKey to a condition with the specific feature (ED25519).
fun parsePublicKeyBase58(base58String: String): PublicKey = base58String.base58ToByteArray().deserialize<PublicKey>()
fun PublicKey.toBase58String(): String = this.serialize().bytes.toBase58()