Clean up Crypto (#1866)

clean up Crypto comments + PublicKey.toSHA256Bytes() uses .encoded
This commit is contained in:
Konstantinos Chalkias 2017-10-11 17:47:48 +01:00 committed by GitHub
parent de391dc9f0
commit ed79db6864
3 changed files with 51 additions and 24 deletions

View File

@ -2,7 +2,10 @@ 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.EdDSAEngine
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import net.i2p.crypto.eddsa.EdDSASecurityProvider
import net.i2p.crypto.eddsa.math.GroupElement
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable
@ -39,8 +42,6 @@ import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey
import org.bouncycastle.pqc.jcajce.spec.SPHINCS256KeyGenParameterSpec
import java.math.BigInteger
import java.security.*
import java.security.KeyFactory
import java.security.KeyPairGenerator
import java.security.spec.InvalidKeySpecException
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
@ -148,7 +149,7 @@ object Crypto {
"at the cost of larger key sizes and loss of compatibility."
)
/** Corda composite key type */
/** Corda composite key type. */
@JvmField
val COMPOSITE_KEY = SignatureScheme(
6,
@ -823,7 +824,7 @@ object Crypto {
@JvmStatic
fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy)
// custom key pair generator from entropy.
// Custom key pair generator from entropy.
private fun deriveEdDSAKeyPairFromEntropy(entropy: BigInteger): KeyPair {
val params = EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec
val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8) // Need to pad the entropy to the valid seed length.
@ -882,7 +883,7 @@ object Crypto {
}
}
// return true if EdDSA publicKey is point at infinity.
// Return true if EdDSA publicKey is point at infinity.
// For EdDSA a custom function is required as it is not supported by the I2P implementation.
private fun isEdDSAPointAtInfinity(publicKey: EdDSAPublicKey): Boolean {
return publicKey.a.toP3() == (EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec).curve.getZero(GroupElement.Representation.P3)
@ -894,7 +895,7 @@ object Crypto {
return signatureScheme.schemeCodeName in signatureSchemeMap
}
// validate a key, by checking its algorithmic params.
// Validate a key, by checking its algorithmic params.
private fun validateKey(signatureScheme: SignatureScheme, key: Key): Boolean {
return when (key) {
is PublicKey -> validatePublicKey(signatureScheme, key)
@ -903,7 +904,7 @@ object Crypto {
}
}
// check if a public key satisfies algorithm specs (for ECC: key should lie on the curve and not being point-at-infinity).
// Check if a public key satisfies algorithm specs (for ECC: key should lie on the curve and not being point-at-infinity).
private fun validatePublicKey(signatureScheme: SignatureScheme, key: PublicKey): Boolean {
return when (key) {
is BCECPublicKey, is EdDSAPublicKey -> publicKeyOnCurve(signatureScheme, key)
@ -912,7 +913,7 @@ object Crypto {
}
}
// check if a private key satisfies algorithm specs.
// Check if a private key satisfies algorithm specs.
private fun validatePrivateKey(signatureScheme: SignatureScheme, key: PrivateKey): Boolean {
return when (key) {
is BCECPrivateKey -> key.parameters == signatureScheme.algSpec
@ -924,7 +925,6 @@ object Crypto {
/**
* Convert a public key to a supported implementation.
*
* @param key a public key.
* @return a supported implementation of the input public key.
* @throws IllegalArgumentException on not supported scheme or if the given key specification

View File

@ -20,9 +20,19 @@ import java.security.*
* @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key.
*/
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
@Throws(InvalidKeyException::class, SignatureException::class)
fun PrivateKey.sign(bytesToSign: ByteArray): DigitalSignature = DigitalSignature(Crypto.doSign(this, bytesToSign))
/**
* Utility to simplify the act of signing a byte array and return a [DigitalSignature.WithKey] object.
* Note that there is no check if the public key matches with the signing private key.
* @param bytesToSign the data/message to be signed in [ByteArray] form (usually the Merkle root).
* @return the [DigitalSignature.WithKey] object on the input message [bytesToSign] and [publicKey].
* @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.
*/
@Throws(InvalidKeyException::class, SignatureException::class)
fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey) = DigitalSignature.WithKey(publicKey, this.sign(bytesToSign).bytes)
/**
@ -33,10 +43,13 @@ fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey) = DigitalSigna
* @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key.
*/
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
@Throws(InvalidKeyException::class, SignatureException::class)
fun KeyPair.sign(bytesToSign: ByteArray) = private.sign(bytesToSign, public)
/** Helper function to sign the bytes of [bytesToSign] with a key pair. */
@Throws(InvalidKeyException::class, SignatureException::class)
fun KeyPair.sign(bytesToSign: OpaqueBytes) = sign(bytesToSign.bytes)
/**
* Helper function for signing a [SignableData] object.
* @param signableData the object to be signed.
@ -56,8 +69,8 @@ fun KeyPair.sign(signableData: SignableData): TransactionSignature = Crypto.doSi
* @throws SignatureException if the signature is invalid (i.e. damaged), or does not match the key (incorrect).
* @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty.
*/
// TODO: SignatureException should be used only for a damaged signature, as per `java.security.Signature.verify()`,
@Throws(SignatureException::class, IllegalArgumentException::class, InvalidKeyException::class)
// TODO: SignatureException should be used only for a damaged signature, as per `java.security.Signature.verify()`.
@Throws(SignatureException::class, InvalidKeyException::class)
fun PublicKey.verify(content: ByteArray, signature: DigitalSignature) = Crypto.doVerify(this, signature.bytes, content)
/**
@ -70,9 +83,10 @@ fun PublicKey.verify(content: ByteArray, signature: DigitalSignature) = Crypto.d
* signature).
* @throws SignatureException if the signature is invalid (i.e. damaged).
* @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty.
* @throws IllegalStateException if this is a [CompositeKey], because verification of composite key signatures is not supported.
* @return whether the signature is correct for this key.
*/
@Throws(IllegalStateException::class, SignatureException::class, IllegalArgumentException::class)
@Throws(SignatureException::class, InvalidKeyException::class)
fun PublicKey.isValid(content: ByteArray, signature: DigitalSignature): Boolean {
if (this is CompositeKey)
throw IllegalStateException("Verification of CompositeKey signatures currently not supported.") // TODO CompositeSignature verification.
@ -82,9 +96,12 @@ fun PublicKey.isValid(content: ByteArray, signature: DigitalSignature): Boolean
/** Render a public key to its hash (in Base58) of its serialised form using the DL prefix. */
fun PublicKey.toStringShort(): String = "DL" + this.toSHA256Bytes().toBase58()
/** Return a [Set] of the contained keys if this is a [CompositeKey]; otherwise, return a [Set] with a single element (this [PublicKey]). */
val PublicKey.keys: Set<PublicKey> get() = (this as? CompositeKey)?.leafKeys ?: setOf(this)
/** Return true if [otherKey] fulfils the requirements of this [PublicKey]. */
fun PublicKey.isFulfilledBy(otherKey: PublicKey): Boolean = isFulfilledBy(setOf(otherKey))
/** Return true if [otherKeys] fulfil the requirements of this [PublicKey]. */
fun PublicKey.isFulfilledBy(otherKeys: Iterable<PublicKey>): Boolean = (this as? CompositeKey)?.isFulfilledBy(otherKeys) ?: (this in otherKeys)
/** Checks whether any of the given [keys] matches a leaf on the [CompositeKey] tree or a single [PublicKey]. */
@ -98,8 +115,9 @@ fun Iterable<TransactionSignature>.byKeys() = map { it.by }.toSet()
// Allow Kotlin destructuring:
// val (private, public) = keyPair
/* The [PrivateKey] of this [KeyPair] .*/
operator fun KeyPair.component1(): PrivateKey = this.private
/* The [PublicKey] of this [KeyPair] .*/
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. */
@ -122,7 +140,7 @@ fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.deriveKeyPairFromEnt
* if this signatureData algorithm is unable to process the input data provided, etc.
* @throws IllegalArgumentException if the signature scheme is not supported for this private key or if any of the clear or signature data is empty.
*/
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
@Throws(InvalidKeyException::class, SignatureException::class)
fun PublicKey.verify(signatureData: ByteArray, clearData: ByteArray): Boolean = Crypto.doVerify(this, signatureData, clearData)
/**
@ -135,7 +153,7 @@ fun PublicKey.verify(signatureData: ByteArray, clearData: ByteArray): Boolean =
* if this signatureData algorithm is unable to process the input data provided, etc.
* @throws IllegalArgumentException if the signature scheme is not supported for this private key or if any of the clear or signature data is empty.
*/
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
@Throws(InvalidKeyException::class, SignatureException::class)
fun KeyPair.verify(signatureData: ByteArray, clearData: ByteArray): Boolean = Crypto.doVerify(this.public, signatureData, clearData)
/**

View File

@ -3,9 +3,8 @@
package net.corda.core.utilities
import net.corda.core.crypto.Base58
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.sha256
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import java.nio.charset.Charset
import java.security.PublicKey
import java.util.*
@ -15,11 +14,13 @@ import javax.xml.bind.DatatypeConverter
// [ByteArray] encoders
/** Convert a byte array to a Base58 encoded [String]. */
fun ByteArray.toBase58(): String = Base58.encode(this)
/** Convert a byte array to a Base64 encoded [String]. */
fun ByteArray.toBase64(): String = Base64.getEncoder().encodeToString(this)
/** Convert a byte array to a hex (base 16) capitalized encoded string.*/
/** Convert a byte array to a hex (Base16) capitalized encoded [String]. */
fun ByteArray.toHex(): String = DatatypeConverter.printHexBinary(this)
@ -65,7 +66,15 @@ fun String.hexToBase64(): String = hexToByteArray().toBase64()
// TODO We use for both CompositeKeys and EdDSAPublicKey custom 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>()
/**
* Method to return the [PublicKey] object given its Base58-[String] representation.
* @param base58String the Base58 encoded format of the serialised [PublicKey].
* @return the resulted [PublicKey] after decoding the [base58String] input and then deserialising to a [PublicKey] object.
*/
fun parsePublicKeyBase58(base58String: String): PublicKey = Crypto.decodePublicKey(base58String.base58ToByteArray())
fun PublicKey.toBase58String(): String = this.serialize().bytes.toBase58()
fun PublicKey.toSHA256Bytes(): ByteArray = this.serialize().bytes.sha256().bytes // TODO: decide on the format of hashed key (encoded Vs serialised).
/** Return the Base58 representation of the serialised public key. */
fun PublicKey.toBase58String(): String = this.encoded.toBase58()
/** Return the bytes of the SHA-256 output for this public key. */
fun PublicKey.toSHA256Bytes(): ByteArray = this.encoded.sha256().bytes