TransactionSignature MetaData support (#1040)

Support signature metadata
This commit is contained in:
Konstantinos Chalkias 2017-08-07 16:21:52 +01:00 committed by GitHub
parent 3a3ead2dfe
commit bd0944e799
55 changed files with 394 additions and 429 deletions

View File

@ -438,7 +438,7 @@ fun JarInputStream.extractFile(path: String, outputTo: OutputStream) {
/** /**
* A privacy salt is required to compute nonces per transaction component in order to ensure that an adversary cannot * A privacy salt is required to compute nonces per transaction component in order to ensure that an adversary cannot
* use brute force techniques and reveal the content of a merkle-leaf hashed value. * use brute force techniques and reveal the content of a Merkle-leaf hashed value.
* Because this salt serves the role of the seed to compute nonces, its size and entropy should be equal to the * Because this salt serves the role of the seed to compute nonces, its size and entropy should be equal to the
* underlying hash function used for Merkle tree generation, currently [SHA256], which has an output of 32 bytes. * underlying hash function used for Merkle tree generation, currently [SHA256], which has an output of 32 bytes.
* There are two constructors, one that generates a new 32-bytes random salt, and another that takes a [ByteArray] input. * There are two constructors, one that generates a new 32-bytes random salt, and another that takes a [ByteArray] input.

View File

@ -4,6 +4,7 @@ import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.crypto.composite.CompositeSignature import net.corda.core.crypto.composite.CompositeSignature
import net.corda.core.crypto.provider.CordaObjectIdentifier import net.corda.core.crypto.provider.CordaObjectIdentifier
import net.corda.core.crypto.provider.CordaSecurityProvider import net.corda.core.crypto.provider.CordaSecurityProvider
import net.corda.core.serialization.serialize
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
@ -401,23 +402,23 @@ object Crypto {
} }
/** /**
* Generic way to sign [MetaData] objects with a [PrivateKey]. * Generic way to sign [SignableData] objects with a [PrivateKey].
* [MetaData] is a wrapper over the transaction's Merkle root in order to attach extra information, such as a timestamp or partial and blind signature indicators. * [SignableData] is a wrapper over the transaction's id (Merkle root) in order to attach extra information, such as a timestamp or partial and blind signature indicators.
* @param privateKey the signer's [PrivateKey]. * @param privateKey the signer's [PrivateKey].
* @param metaData a [MetaData] object that adds extra information to a transaction. * @param signableData a [SignableData] object that adds extra information to a transaction.
* @return a [TransactionSignature] object than contains the output of a successful signing and the metaData. * @return a [TransactionSignature] object than contains the output of a successful signing, signer's public key and the signature metadata.
* @throws IllegalArgumentException if the signature scheme is not supported for this private key or * @throws IllegalArgumentException if the signature scheme is not supported for this private key.
* if metaData.schemeCodeName is not aligned with key type.
* @throws InvalidKeyException if the private key is invalid. * @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key. * @throws SignatureException if signing is not possible due to malformed data or private key.
*/ */
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class) @Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
fun doSign(privateKey: PrivateKey, metaData: MetaData): TransactionSignature { fun doSign(keyPair: KeyPair, signableData: SignableData): TransactionSignature {
val sigKey: SignatureScheme = findSignatureScheme(privateKey) val sigKey: SignatureScheme = findSignatureScheme(keyPair.private)
val sigMetaData: SignatureScheme = findSignatureScheme(metaData.schemeCodeName) val sigMetaData: SignatureScheme = findSignatureScheme(keyPair.public)
if (sigKey != sigMetaData) throw IllegalArgumentException("Metadata schemeCodeName: ${metaData.schemeCodeName} is not aligned with the key type.") if (sigKey != sigMetaData) throw IllegalArgumentException("Metadata schemeCodeName: ${sigMetaData.schemeCodeName}" +
val signatureData = doSign(sigKey.schemeCodeName, privateKey, metaData.bytes()) " is not aligned with the key type: ${sigKey.schemeCodeName}.")
return TransactionSignature(signatureData, metaData) val signatureBytes = doSign(sigKey.schemeCodeName, keyPair.private, signableData.serialize().bytes)
return TransactionSignature(signatureBytes, keyPair.public, signableData.signatureMetadata)
} }
/** /**
@ -434,7 +435,7 @@ object Crypto {
* if this signatureData scheme is unable to process the input data provided, if the verification is not possible. * if this signatureData scheme is unable to process the input data provided, if the verification is not possible.
* @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty. * @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty.
*/ */
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class) @Throws(InvalidKeyException::class, SignatureException::class)
fun doVerify(schemeCodeName: String, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray) = doVerify(findSignatureScheme(schemeCodeName), publicKey, signatureData, clearData) fun doVerify(schemeCodeName: String, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray) = doVerify(findSignatureScheme(schemeCodeName), publicKey, signatureData, clearData)
/** /**
@ -485,9 +486,9 @@ object Crypto {
/** /**
* Utility to simplify the act of verifying a [TransactionSignature]. * Utility to simplify the act of verifying a [TransactionSignature].
* It returns true if it succeeds, but it always throws an exception if verification fails. * It returns true if it succeeds, but it always throws an exception if verification fails.
* @param publicKey the signer's [PublicKey]. * @param txId transaction's id (Merkle root).
* @param transactionSignature the signatureData on a message. * @param transactionSignature the signature on the transaction.
* @return true if verification passes or throws an exception if verification fails. * @return true if verification passes or throw exception if verification fails.
* @throws InvalidKeyException if the key is invalid. * @throws InvalidKeyException if the key is invalid.
* @throws SignatureException if this signatureData object is not initialized properly, * @throws SignatureException if this signatureData object is not initialized properly,
* the passed-in signatureData is improperly encoded or of the wrong type, * the passed-in signatureData is improperly encoded or of the wrong type,
@ -495,9 +496,26 @@ object Crypto {
* @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty. * @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty.
*/ */
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class) @Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
fun doVerify(publicKey: PublicKey, transactionSignature: TransactionSignature): Boolean { fun doVerify(txId: SecureHash, transactionSignature: TransactionSignature): Boolean {
if (publicKey != transactionSignature.metaData.publicKey) IllegalArgumentException("MetaData's publicKey: ${transactionSignature.metaData.publicKey.toStringShort()} does not match") val signableData = SignableData(txId, transactionSignature.signatureMetadata)
return Crypto.doVerify(publicKey, transactionSignature.signatureData, transactionSignature.metaData.bytes()) return Crypto.doVerify(transactionSignature.by, transactionSignature.bytes, signableData.serialize().bytes)
}
/**
* Utility to simplify the act of verifying a digital signature by identifying the signature scheme used from the input public key's type.
* It returns true if it succeeds and false if not. In comparison to [doVerify] 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.
* @param txId transaction's id (Merkle root).
* @param transactionSignature the signature on the transaction.
* @throws SignatureException if this signatureData object is not initialized properly,
* the passed-in signatureData is improperly encoded or of the wrong type,
* if this signatureData scheme is unable to process the input data provided, if the verification is not possible.
*/
@Throws(SignatureException::class)
fun isValid(txId: SecureHash, transactionSignature: TransactionSignature): Boolean {
val signableData = SignableData(txId, transactionSignature.signatureMetadata)
return isValid(findSignatureScheme(transactionSignature.by), transactionSignature.by, transactionSignature.bytes, signableData.serialize().bytes)
} }
/** /**

View File

@ -34,7 +34,17 @@ fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignat
*/ */
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class) @Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
fun KeyPair.sign(bytesToSign: ByteArray) = private.sign(bytesToSign, public) fun KeyPair.sign(bytesToSign: ByteArray) = private.sign(bytesToSign, public)
fun KeyPair.sign(bytesToSign: OpaqueBytes) = private.sign(bytesToSign.bytes, public) fun KeyPair.sign(bytesToSign: OpaqueBytes) = sign(bytesToSign.bytes)
/**
* Helper function for signing a [SignableData] object.
* @param signableData the object to be signed.
* @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.
*/
@Throws(InvalidKeyException::class, SignatureException::class)
fun KeyPair.sign(signableData: SignableData): TransactionSignature = Crypto.doSign(this, signableData)
/** /**
* Utility to simplify the act of verifying a signature. * Utility to simplify the act of verifying a signature.
@ -88,7 +98,7 @@ fun PublicKey.containsAny(otherKeys: Iterable<PublicKey>): Boolean {
} }
/** Returns the set of all [PublicKey]s of the signatures */ /** Returns the set of all [PublicKey]s of the signatures */
fun Iterable<DigitalSignature.WithKey>.byKeys() = map { it.by }.toSet() fun Iterable<TransactionSignature>.byKeys() = map { it.by }.toSet()
// Allow Kotlin destructuring: val (private, public) = keyPair // Allow Kotlin destructuring: val (private, public) = keyPair
operator fun KeyPair.component1(): PrivateKey = this.private operator fun KeyPair.component1(): PrivateKey = this.private
@ -105,17 +115,6 @@ fun generateKeyPair(): KeyPair = Crypto.generateKeyPair()
*/ */
fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.deriveKeyPairFromEntropy(entropy) fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.deriveKeyPairFromEntropy(entropy)
/**
* Helper function for signing.
* @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.
*/
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
fun PrivateKey.sign(metaData: MetaData): TransactionSignature = Crypto.doSign(this, metaData)
/** /**
* Helper function to verify a signature. * Helper function to verify a signature.
* @param signatureData the signature on a message. * @param signatureData the signature on a message.
@ -129,21 +128,6 @@ fun PrivateKey.sign(metaData: MetaData): TransactionSignature = Crypto.doSign(th
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class) @Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
fun PublicKey.verify(signatureData: ByteArray, clearData: ByteArray): Boolean = Crypto.doVerify(this, signatureData, clearData) fun PublicKey.verify(signatureData: ByteArray, clearData: ByteArray): Boolean = Crypto.doVerify(this, signatureData, clearData)
/**
* Helper function to verify a metadata attached signature. It is noted that the transactionSignature contains
* signatureData and a [MetaData] object that contains the signer's public key and the transaction's Merkle root.
* @param transactionSignature a [TransactionSignature] object that .
* @throws InvalidKeyException if the key is invalid.
* @throws SignatureException if this signatureData object is not initialized properly,
* the passed-in signatureData is improperly encoded or of the wrong type,
* 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)
fun PublicKey.verify(transactionSignature: TransactionSignature): Boolean {
return Crypto.doVerify(this, transactionSignature)
}
/** /**
* Helper function for the signers to verify their own signature. * Helper function for the signers to verify their own signature.
* @param signatureData the signature on a message. * @param signatureData the signature on a message.

View File

@ -1,71 +0,0 @@
package net.corda.core.crypto
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.serialize
import net.corda.core.utilities.sequence
import java.security.PublicKey
import java.time.Instant
import java.util.*
/**
* A [MetaData] object adds extra information to a transaction. MetaData is used to support a universal
* digital signature model enabling full, partial, fully or partially blind and metaData attached signatures,
* (such as an attached timestamp). A MetaData object contains both the merkle root of the transaction and the signer's public key.
* When signatureType is set to FULL, then visibleInputs and signedInputs can be ignored.
* Note: We could omit signatureType as it can always be defined by combining visibleInputs and signedInputs,
* but it helps to speed up the process when FULL is used, and thus we can bypass the extra check on boolean arrays.
*
* @param schemeCodeName a signature scheme's code name (e.g. ECDSA_SECP256K1_SHA256).
* @param versionID DLT's version.
* @param signatureType type of the signature, see [SignatureType] (e.g. FULL, PARTIAL, BLIND, PARTIAL_AND_BLIND).
* @param timestamp the signature's timestamp as provided by the signer.
* @param visibleInputs for partially/fully blind signatures. We use Merkle tree boolean index flags (from left to right)
* indicating what parts of the transaction were visible when the signature was calculated.
* @param signedInputs for partial signatures. We use Merkle tree boolean index flags (from left to right)
* indicating what parts of the Merkle tree are actually signed.
* @param merkleRoot the Merkle root of the transaction.
* @param publicKey the signer's public key.
*/
@CordaSerializable
open class MetaData(
val schemeCodeName: String,
val versionID: String,
val signatureType: SignatureType = SignatureType.FULL,
val timestamp: Instant?,
val visibleInputs: BitSet?,
val signedInputs: BitSet?,
val merkleRoot: ByteArray,
val publicKey: PublicKey) {
fun bytes() = this.serialize().bytes
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other?.javaClass != javaClass) return false
other as MetaData
if (schemeCodeName != other.schemeCodeName) return false
if (versionID != other.versionID) return false
if (signatureType != other.signatureType) return false
if (timestamp != other.timestamp) return false
if (visibleInputs != other.visibleInputs) return false
if (signedInputs != other.signedInputs) return false
if (merkleRoot.sequence() != other.merkleRoot.sequence()) return false
if (publicKey != other.publicKey) return false
return true
}
override fun hashCode(): Int {
var result = schemeCodeName.hashCode()
result = 31 * result + versionID.hashCode()
result = 31 * result + signatureType.hashCode()
result = 31 * result + (timestamp?.hashCode() ?: 0)
result = 31 * result + (visibleInputs?.hashCode() ?: 0)
result = 31 * result + (signedInputs?.hashCode() ?: 0)
result = 31 * result + Arrays.hashCode(merkleRoot)
result = 31 * result + publicKey.hashCode()
return result
}
}

View File

@ -0,0 +1,14 @@
package net.corda.core.crypto
import net.corda.core.serialization.CordaSerializable
/**
* A [SignableData] object is the packet actually signed.
* It works as a wrapper over transaction id and signature metadata.
*
* @param txId transaction's id.
* @param signatureMetadata meta data required.
*/
@CordaSerializable
data class SignableData(val txId: SecureHash, val signatureMetadata: SignatureMetadata)

View File

@ -0,0 +1,15 @@
package net.corda.core.crypto
import net.corda.core.serialization.CordaSerializable
/**
* SignatureMeta is required to add extra meta-data to a transaction's signature.
* It currently supports platformVersion only, but it can be extended to support a universal digital
* signature model enabling partial signatures and attaching extra information, such as a user's timestamp or other
* application-specific fields.
*
* @param platformVersion current DLT version.
* @param schemeNumberID number id of the signature scheme used based on signer's key-pair, see [SignatureScheme.schemeNumberID].
*/
@CordaSerializable
data class SignatureMetadata(val platformVersion: Int, val schemeNumberID: Int)

View File

@ -6,7 +6,7 @@ import java.security.spec.AlgorithmParameterSpec
/** /**
* This class is used to define a digital signature scheme. * This class is used to define a digital signature scheme.
* @param schemeNumberID we assign a number ID for more efficient on-wire serialisation. Please ensure uniqueness between schemes. * @param schemeNumberID we assign a number ID for better efficiency on-wire serialisation. Please ensure uniqueness between schemes.
* @param schemeCodeName code name for this signature scheme (e.g. RSA_SHA256, ECDSA_SECP256K1_SHA256, ECDSA_SECP256R1_SHA256, EDDSA_ED25519_SHA512, SPHINCS-256_SHA512). * @param schemeCodeName code name for this signature scheme (e.g. RSA_SHA256, ECDSA_SECP256K1_SHA256, ECDSA_SECP256R1_SHA256, EDDSA_ED25519_SHA512, SPHINCS-256_SHA512).
* @param signatureOID ASN.1 algorithm identifier of the signature algorithm (e.g 1.3.101.112 for EdDSA) * @param signatureOID ASN.1 algorithm identifier of the signature algorithm (e.g 1.3.101.112 for EdDSA)
* @param alternativeOIDs ASN.1 algorithm identifiers for keys of the signature, where we want to map multiple keys to * @param alternativeOIDs ASN.1 algorithm identifiers for keys of the signature, where we want to map multiple keys to

View File

@ -1,17 +0,0 @@
package net.corda.core.crypto
import net.corda.core.serialization.CordaSerializable
/**
* Supported Signature types:
* <p><ul>
* <li>FULL = signature covers whole transaction, by the convention that signing the Merkle root, it is equivalent to signing all parts of the transaction.
* <li>PARTIAL = signature covers only a part of the transaction, see [MetaData].
* <li>BLIND = when an entity blindly signs without having full knowledge on the content, see [MetaData].
* <li>PARTIAL_AND_BLIND = combined PARTIAL and BLIND in the same time.
* </ul>
*/
@CordaSerializable
enum class SignatureType {
FULL, PARTIAL, BLIND, PARTIAL_AND_BLIND
}

View File

@ -1,22 +1,57 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.serialization.CordaSerializable
import java.security.InvalidKeyException import java.security.InvalidKeyException
import java.security.PublicKey
import java.security.SignatureException import java.security.SignatureException
import java.util.*
/** /**
* A wrapper around a digital signature accompanied with metadata, see [MetaData.Full] and [DigitalSignature]. * A wrapper over the signature output accompanied by signer's public key and signature metadata.
* The signature protocol works as follows: s = sign(MetaData.hashBytes). * This is similar to [DigitalSignature.WithKey], but targeted to DLT transaction signatures.
*/ */
open class TransactionSignature(val signatureData: ByteArray, val metaData: MetaData) : DigitalSignature(signatureData) { @CordaSerializable
class TransactionSignature(bytes: ByteArray, val by: PublicKey, val signatureMetadata: SignatureMetadata): DigitalSignature(bytes) {
/** /**
* Function to auto-verify a [MetaData] object's signature. * Function to verify a [SignableData] object's signature.
* Note that [MetaData] contains both public key and merkle root of the transaction. * Note that [SignableData] contains the id of the transaction and extra metadata, such as DLT's platform version.
*
* @param txId transaction's id (Merkle root), which along with [signatureMetadata] will be used to construct the [SignableData] object to be signed.
* @throws InvalidKeyException if the key is invalid. * @throws InvalidKeyException if the key is invalid.
* @throws SignatureException if this signatureData object is not initialized properly, * @throws SignatureException if this signatureData object is not initialized properly,
* the passed-in signatureData is improperly encoded or of the wrong type, * the passed-in signatureData is improperly encoded or of the wrong type,
* if this signatureData algorithm is unable to process the input data provided, etc. * 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 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 verify(): Boolean = Crypto.doVerify(metaData.publicKey, signatureData, metaData.bytes()) fun verify(txId: SecureHash) = Crypto.doVerify(txId, this)
/**
* Utility to simplify the act of verifying a signature. In comparison to [verify] 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 isValid(txId: SecureHash) = Crypto.isValid(txId, this)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is TransactionSignature) return false
return (Arrays.equals(bytes, other.bytes)
&& by == other.by
&& signatureMetadata == other.signatureMetadata)
}
override fun hashCode(): Int {
var result = super.hashCode()
result = 31 * result + by.hashCode()
result = 31 * result + signatureMetadata.hashCode()
return result
}
} }

View File

@ -1,8 +1,7 @@
package net.corda.core.crypto.composite package net.corda.core.crypto.composite
import net.corda.core.crypto.SecureHash
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import org.bouncycastle.asn1.ASN1ObjectIdentifier
import org.bouncycastle.asn1.x509.AlgorithmIdentifier
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.security.* import java.security.*
import java.security.spec.AlgorithmParameterSpec import java.security.spec.AlgorithmParameterSpec
@ -77,7 +76,7 @@ class CompositeSignature : Signature(SIGNATURE_ALGORITHM) {
fun engineVerify(sigBytes: ByteArray): Boolean { fun engineVerify(sigBytes: ByteArray): Boolean {
val sig = sigBytes.deserialize<CompositeSignaturesWithKeys>() val sig = sigBytes.deserialize<CompositeSignaturesWithKeys>()
return if (verifyKey.isFulfilledBy(sig.sigs.map { it.by })) { return if (verifyKey.isFulfilledBy(sig.sigs.map { it.by })) {
val clearData = buffer.toByteArray() val clearData = SecureHash.SHA256(buffer.toByteArray())
sig.sigs.all { it.isValid(clearData) } sig.sigs.all { it.isValid(clearData) }
} else { } else {
false false

View File

@ -1,6 +1,6 @@
package net.corda.core.crypto.composite package net.corda.core.crypto.composite
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.TransactionSignature
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
/** /**
@ -8,7 +8,7 @@ import net.corda.core.serialization.CordaSerializable
* serialization format (i.e. not Kryo). * serialization format (i.e. not Kryo).
*/ */
@CordaSerializable @CordaSerializable
data class CompositeSignaturesWithKeys(val sigs: List<DigitalSignature.WithKey>) { data class CompositeSignaturesWithKeys(val sigs: List<TransactionSignature>) {
companion object { companion object {
val EMPTY = CompositeSignaturesWithKeys(emptyList()) val EMPTY = CompositeSignaturesWithKeys(emptyList())
} }

View File

@ -1,6 +1,7 @@
package net.corda.core.crypto.testing package net.corda.core.crypto.testing
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.SignatureMetadata
import net.corda.core.crypto.TransactionSignature
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import java.math.BigInteger import java.math.BigInteger
@ -31,5 +32,4 @@ class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {
} }
/** A signature with a key and value of zero. Useful when you want a signature object that you know won't ever be used. */ /** 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 val NULL_SIGNATURE = TransactionSignature(ByteArray(32), NullPublicKey, SignatureMetadata(1, -1))
object NullSignature : DigitalSignature.WithKey(NullPublicKey, ByteArray(32))

View File

@ -4,7 +4,7 @@ import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.isFulfilledBy import net.corda.core.crypto.isFulfilledBy
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
@ -95,7 +95,7 @@ abstract class AbstractStateReplacementFlow {
abstract protected fun assembleTx(): UpgradeTx abstract protected fun assembleTx(): UpgradeTx
@Suspendable @Suspendable
private fun collectSignatures(participants: Iterable<PublicKey>, stx: SignedTransaction): List<DigitalSignature.WithKey> { private fun collectSignatures(participants: Iterable<PublicKey>, stx: SignedTransaction): List<TransactionSignature> {
val parties = participants.map { val parties = participants.map {
val participantNode = serviceHub.networkMapCache.getNodeByLegalIdentityKey(it) ?: val participantNode = serviceHub.networkMapCache.getNodeByLegalIdentityKey(it) ?:
throw IllegalStateException("Participant $it to state $originalState not found on the network") throw IllegalStateException("Participant $it to state $originalState not found on the network")
@ -113,10 +113,10 @@ abstract class AbstractStateReplacementFlow {
} }
@Suspendable @Suspendable
private fun getParticipantSignature(party: Party, stx: SignedTransaction): DigitalSignature.WithKey { private fun getParticipantSignature(party: Party, stx: SignedTransaction): TransactionSignature {
val proposal = Proposal(originalState.ref, modification) val proposal = Proposal(originalState.ref, modification)
subFlow(SendTransactionFlow(party, stx)) subFlow(SendTransactionFlow(party, stx))
return sendAndReceive<DigitalSignature.WithKey>(party, proposal).unwrap { return sendAndReceive<TransactionSignature>(party, proposal).unwrap {
check(party.owningKey.isFulfilledBy(it.by)) { "Not signed by the required participant" } check(party.owningKey.isFulfilledBy(it.by)) { "Not signed by the required participant" }
it.verify(stx.id) it.verify(stx.id)
it it
@ -124,7 +124,7 @@ abstract class AbstractStateReplacementFlow {
} }
@Suspendable @Suspendable
private fun getNotarySignatures(stx: SignedTransaction): List<DigitalSignature.WithKey> { private fun getNotarySignatures(stx: SignedTransaction): List<TransactionSignature> {
progressTracker.currentStep = NOTARY progressTracker.currentStep = NOTARY
try { try {
return subFlow(NotaryFlow.Client(stx)) return subFlow(NotaryFlow.Client(stx))
@ -165,7 +165,7 @@ abstract class AbstractStateReplacementFlow {
progressTracker.currentStep = APPROVING progressTracker.currentStep = APPROVING
val mySignature = sign(stx) val mySignature = sign(stx)
val swapSignatures = sendAndReceive<List<DigitalSignature.WithKey>>(otherSide, mySignature) val swapSignatures = sendAndReceive<List<TransactionSignature>>(otherSide, mySignature)
// TODO: This step should not be necessary, as signatures are re-checked in verifyRequiredSignatures. // TODO: This step should not be necessary, as signatures are re-checked in verifyRequiredSignatures.
val allSignatures = swapSignatures.unwrap { signatures -> val allSignatures = swapSignatures.unwrap { signatures ->
@ -203,7 +203,7 @@ abstract class AbstractStateReplacementFlow {
require(myKey in requiredKeys) { "Party is not a participant for any of the input states of transaction ${stx.id}" } require(myKey in requiredKeys) { "Party is not a participant for any of the input states of transaction ${stx.id}" }
} }
private fun sign(stx: SignedTransaction): DigitalSignature.WithKey { private fun sign(stx: SignedTransaction): TransactionSignature {
return serviceHub.createSignature(stx) return serviceHub.createSignature(stx)
} }
} }

View File

@ -1,7 +1,7 @@
package net.corda.core.flows package net.corda.core.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.isFulfilledBy import net.corda.core.crypto.isFulfilledBy
import net.corda.core.crypto.toBase58String import net.corda.core.crypto.toBase58String
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -124,10 +124,10 @@ class CollectSignaturesFlow(val partiallySignedTx: SignedTransaction,
/** /**
* Get and check the required signature. * Get and check the required signature.
*/ */
@Suspendable private fun collectSignature(counterparty: Party): DigitalSignature.WithKey { @Suspendable private fun collectSignature(counterparty: Party): TransactionSignature {
// SendTransactionFlow allows otherParty to access our data to resolve the transaction. // SendTransactionFlow allows otherParty to access our data to resolve the transaction.
subFlow(SendTransactionFlow(counterparty, partiallySignedTx)) subFlow(SendTransactionFlow(counterparty, partiallySignedTx))
return receive<DigitalSignature.WithKey>(counterparty).unwrap { return receive<TransactionSignature>(counterparty).unwrap {
require(counterparty.owningKey.isFulfilledBy(it.by)) { "Not signed by the required Party." } require(counterparty.owningKey.isFulfilledBy(it.by)) { "Not signed by the required Party." }
it it
} }

View File

@ -3,6 +3,9 @@ package net.corda.core.flows
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SignableData
import net.corda.core.crypto.SignatureMetadata
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.transactions.NotaryChangeWireTransaction import net.corda.core.transactions.NotaryChangeWireTransaction
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -36,7 +39,8 @@ class NotaryChangeFlow<out T : ContractState>(
val participantKeys = inputs.flatMap { it.state.data.participants }.map { it.owningKey }.toSet() val participantKeys = inputs.flatMap { it.state.data.participants }.map { it.owningKey }.toSet()
// TODO: We need a much faster way of finding our key in the transaction // TODO: We need a much faster way of finding our key in the transaction
val myKey = serviceHub.keyManagementService.filterMyKeys(participantKeys).single() val myKey = serviceHub.keyManagementService.filterMyKeys(participantKeys).single()
val mySignature = serviceHub.keyManagementService.sign(tx.id.bytes, myKey) val signableData = SignableData(tx.id, SignatureMetadata(serviceHub.myInfo.platformVersion, Crypto.findSignatureScheme(myKey).schemeNumberID))
val mySignature = serviceHub.keyManagementService.sign(signableData, myKey)
val stx = SignedTransaction(tx, listOf(mySignature)) val stx = SignedTransaction(tx, listOf(mySignature))
return AbstractStateReplacementFlow.UpgradeTx(stx, participantKeys, myKey) return AbstractStateReplacementFlow.UpgradeTx(stx, participantKeys, myKey)

View File

@ -3,9 +3,9 @@ package net.corda.core.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TimeWindow import net.corda.core.contracts.TimeWindow
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignedData import net.corda.core.crypto.SignedData
import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.keys import net.corda.core.crypto.keys
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.FetchDataFlow import net.corda.core.internal.FetchDataFlow
@ -32,7 +32,7 @@ object NotaryFlow {
*/ */
@InitiatingFlow @InitiatingFlow
open class Client(private val stx: SignedTransaction, open class Client(private val stx: SignedTransaction,
override val progressTracker: ProgressTracker) : FlowLogic<List<DigitalSignature.WithKey>>() { override val progressTracker: ProgressTracker) : FlowLogic<List<TransactionSignature>>() {
constructor(stx: SignedTransaction) : this(stx, tracker()) constructor(stx: SignedTransaction) : this(stx, tracker())
companion object { companion object {
@ -46,7 +46,7 @@ object NotaryFlow {
@Suspendable @Suspendable
@Throws(NotaryException::class) @Throws(NotaryException::class)
override fun call(): List<DigitalSignature.WithKey> { override fun call(): List<TransactionSignature> {
progressTracker.currentStep = REQUESTING progressTracker.currentStep = REQUESTING
notaryParty = stx.notary ?: throw IllegalStateException("Transaction does not specify a Notary") notaryParty = stx.notary ?: throw IllegalStateException("Transaction does not specify a Notary")
@ -67,7 +67,7 @@ object NotaryFlow {
val response = try { val response = try {
if (serviceHub.networkMapCache.isValidatingNotary(notaryParty)) { if (serviceHub.networkMapCache.isValidatingNotary(notaryParty)) {
subFlow(SendTransactionWithRetry(notaryParty, stx)) subFlow(SendTransactionWithRetry(notaryParty, stx))
receive<List<DigitalSignature.WithKey>>(notaryParty) receive<List<TransactionSignature>>(notaryParty)
} else { } else {
val tx: Any = if (stx.isNotaryChangeTransaction()) { val tx: Any = if (stx.isNotaryChangeTransaction()) {
stx.notaryChangeTx stx.notaryChangeTx
@ -84,14 +84,14 @@ object NotaryFlow {
} }
return response.unwrap { signatures -> return response.unwrap { signatures ->
signatures.forEach { validateSignature(it, stx.id.bytes) } signatures.forEach { validateSignature(it, stx.id) }
signatures signatures
} }
} }
private fun validateSignature(sig: DigitalSignature.WithKey, data: ByteArray) { private fun validateSignature(sig: TransactionSignature, txId: SecureHash) {
check(sig.by in notaryParty.owningKey.keys) { "Invalid signer for the notary result" } check(sig.by in notaryParty.owningKey.keys) { "Invalid signer for the notary result" }
sig.verify(data) sig.verify(txId)
} }
} }
@ -124,7 +124,7 @@ object NotaryFlow {
@Suspendable @Suspendable
private fun signAndSendResponse(txId: SecureHash) { private fun signAndSendResponse(txId: SecureHash) {
val signature = service.sign(txId.bytes) val signature = service.sign(txId)
send(otherSide, listOf(signature)) send(otherSide, listOf(signature))
} }
} }

View File

@ -1,7 +1,10 @@
package net.corda.core.node package net.corda.core.node
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SignableData
import net.corda.core.crypto.SignatureMetadata
import net.corda.core.crypto.TransactionSignature
import net.corda.core.node.services.* import net.corda.core.node.services.*
import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SerializeAsToken
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -123,7 +126,7 @@ interface ServiceHub : ServicesForResolution {
/** /**
* Helper property to shorten code for fetching the the [PublicKey] portion of the * Helper property to shorten code for fetching the the [PublicKey] portion of the
* Node's Notary signing identity. It is required that the Node hosts a notary service, * Node's Notary signing identity. It is required that the Node hosts a notary service,
* otherwise an IllegalArgumentException will be thrown. * otherwise an [IllegalArgumentException] will be thrown.
* Typical use is during signing in flows and for unit test signing. * Typical use is during signing in flows and for unit test signing.
* When this [PublicKey] is passed into the signing methods below, or on the KeyManagementService * When this [PublicKey] is passed into the signing methods below, or on the KeyManagementService
* the matching [java.security.PrivateKey] will be looked up internally and used to sign. * the matching [java.security.PrivateKey] will be looked up internally and used to sign.
@ -132,9 +135,17 @@ interface ServiceHub : ServicesForResolution {
*/ */
val notaryIdentityKey: PublicKey get() = this.myInfo.notaryIdentity.owningKey val notaryIdentityKey: PublicKey get() = this.myInfo.notaryIdentity.owningKey
// Helper method to construct an initial partially signed transaction from a [TransactionBuilder].
private fun signInitialTransaction(builder: TransactionBuilder, publicKey: PublicKey, signatureMetadata: SignatureMetadata): SignedTransaction {
val signableData = SignableData(builder.toWireTransaction().id, signatureMetadata)
val sig = keyManagementService.sign(signableData, publicKey)
builder.addSignatureUnchecked(sig)
return builder.toSignedTransaction(false)
}
/** /**
* Helper method to construct an initial partially signed transaction from a [TransactionBuilder] * Helper method to construct an initial partially signed transaction from a [TransactionBuilder]
* using keys stored inside the node. * using keys stored inside the node. Signature metadata is added automatically.
* @param builder The [TransactionBuilder] to seal with the node's signature. * @param builder The [TransactionBuilder] to seal with the node's signature.
* Any existing signatures on the builder will be preserved. * Any existing signatures on the builder will be preserved.
* @param publicKey The [PublicKey] matched to the internal [java.security.PrivateKey] to use in signing this transaction. * @param publicKey The [PublicKey] matched to the internal [java.security.PrivateKey] to use in signing this transaction.
@ -142,15 +153,12 @@ interface ServiceHub : ServicesForResolution {
* to sign with. * to sign with.
* @return Returns a SignedTransaction with the new node signature attached. * @return Returns a SignedTransaction with the new node signature attached.
*/ */
fun signInitialTransaction(builder: TransactionBuilder, publicKey: PublicKey): SignedTransaction { fun signInitialTransaction(builder: TransactionBuilder, publicKey: PublicKey) =
val sig = keyManagementService.sign(builder.toWireTransaction().id.bytes, publicKey) signInitialTransaction(builder, publicKey, SignatureMetadata(myInfo.platformVersion, Crypto.findSignatureScheme(publicKey).schemeNumberID))
builder.addSignatureUnchecked(sig)
return builder.toSignedTransaction(false)
}
/** /**
* Helper method to construct an initial partially signed transaction from a TransactionBuilder * Helper method to construct an initial partially signed transaction from a TransactionBuilder
* using the default identity key contained in the node. * using the default identity key contained in the node. The legal Indentity key is used to sign.
* @param builder The TransactionBuilder to seal with the node's signature. * @param builder The TransactionBuilder to seal with the node's signature.
* Any existing signatures on the builder will be preserved. * Any existing signatures on the builder will be preserved.
* @return Returns a SignedTransaction with the new node signature attached. * @return Returns a SignedTransaction with the new node signature attached.
@ -175,25 +183,30 @@ interface ServiceHub : ServicesForResolution {
return stx return stx
} }
// Helper method to create an additional signature for an existing (partially) [SignedTransaction].
private fun createSignature(signedTransaction: SignedTransaction, publicKey: PublicKey, signatureMetadata: SignatureMetadata): TransactionSignature {
val signableData = SignableData(signedTransaction.id, signatureMetadata)
return keyManagementService.sign(signableData, publicKey)
}
/** /**
* Helper method to create an additional signature for an existing (partially) [SignedTransaction]. * Helper method to create an additional signature for an existing (partially) [SignedTransaction].
* @param signedTransaction The [SignedTransaction] to which the signature will apply. * @param signedTransaction The [SignedTransaction] to which the signature will apply.
* @param publicKey The [PublicKey] matching to a signing [java.security.PrivateKey] hosted in the node. * @param publicKey The [PublicKey] matching to a signing [java.security.PrivateKey] hosted in the node.
* If the [PublicKey] is actually a [net.corda.core.crypto.CompositeKey] the first leaf key found locally will be used * If the [PublicKey] is actually a [net.corda.core.crypto.CompositeKey] the first leaf key found locally will be used
* for signing. * for signing.
* @return The [DigitalSignature.WithKey] generated by signing with the internally held [java.security.PrivateKey]. * @return The [TransactionSignature] generated by signing with the internally held [java.security.PrivateKey].
*/ */
fun createSignature(signedTransaction: SignedTransaction, publicKey: PublicKey): DigitalSignature.WithKey { fun createSignature(signedTransaction: SignedTransaction, publicKey: PublicKey) =
return keyManagementService.sign(signedTransaction.id.bytes, publicKey) createSignature(signedTransaction, publicKey, SignatureMetadata(myInfo.platformVersion, Crypto.findSignatureScheme(publicKey).schemeNumberID))
}
/** /**
* Helper method to create an additional signature for an existing (partially) SignedTransaction * Helper method to create an additional signature for an existing (partially) [SignedTransaction]
* using the default identity signing key of the node. * using the default identity signing key of the node. The legal identity key is used to sign.
* @param signedTransaction The SignedTransaction to which the signature will apply. * @param signedTransaction The SignedTransaction to which the signature will apply.
* @return The DigitalSignature.WithKey generated by signing with the internally held identity PrivateKey. * @return The DigitalSignature.WithKey generated by signing with the internally held identity PrivateKey.
*/ */
fun createSignature(signedTransaction: SignedTransaction): DigitalSignature.WithKey { fun createSignature(signedTransaction: SignedTransaction): TransactionSignature {
return createSignature(signedTransaction, legalIdentityKey) return createSignature(signedTransaction, legalIdentityKey)
} }
@ -210,7 +223,7 @@ interface ServiceHub : ServicesForResolution {
} }
/** /**
* Helper method to ap-pend an additional signature for an existing (partially) [SignedTransaction] * Helper method to append an additional signature for an existing (partially) [SignedTransaction]
* using the default identity signing key of the node. * using the default identity signing key of the node.
* @param signedTransaction The [SignedTransaction] to which the signature will be added. * @param signedTransaction The [SignedTransaction] to which the signature will be added.
* @return A new [SignedTransaction] with the addition of the new signature. * @return A new [SignedTransaction] with the addition of the new signature.

View File

@ -2,6 +2,8 @@ package net.corda.core.node.services
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SignableData
import net.corda.core.crypto.TransactionSignature
import net.corda.core.identity.AnonymousPartyAndPath import net.corda.core.identity.AnonymousPartyAndPath
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import java.security.PublicKey import java.security.PublicKey
@ -48,9 +50,18 @@ interface KeyManagementService {
* or previously generated via the [freshKey] method. * or previously generated via the [freshKey] method.
* If the [PublicKey] is actually a [CompositeKey] the first leaf signing key hosted by the node is used. * If the [PublicKey] is actually a [CompositeKey] the first leaf signing key hosted by the node is used.
* @throws IllegalArgumentException if the input key is not a member of [keys]. * @throws IllegalArgumentException if the input key is not a member of [keys].
* TODO A full [KeyManagementService] implementation needs to record activity to the [AuditService] and to limit signing to
* appropriately authorised contexts and initiating users.
*/ */
@Suspendable @Suspendable
fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey
}
/**
* Using the provided signing [PublicKey] internally looks up the matching [PrivateKey] and signs the [SignableData].
* @param signableData a wrapper over transaction id (Merkle root) and signature metadata.
* @param publicKey The [PublicKey] partner to an internally held [PrivateKey], either derived from the node's primary identity,
* or previously generated via the [freshKey] method.
* If the [PublicKey] is actually a [CompositeKey] the first leaf signing key hosted by the node is used.
* @throws IllegalArgumentException if the input key is not a member of [keys].
*/
@Suspendable
fun sign(signableData: SignableData, publicKey: PublicKey): TransactionSignature
}

View File

@ -2,9 +2,7 @@ package net.corda.core.node.services
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TimeWindow import net.corda.core.contracts.TimeWindow
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.*
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignedData
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.NotaryError import net.corda.core.flows.NotaryError
import net.corda.core.flows.NotaryException import net.corda.core.flows.NotaryException
@ -75,4 +73,9 @@ abstract class TrustedAuthorityNotaryService : NotaryService() {
fun sign(bits: ByteArray): DigitalSignature.WithKey { fun sign(bits: ByteArray): DigitalSignature.WithKey {
return services.keyManagementService.sign(bits, services.notaryIdentityKey) return services.keyManagementService.sign(bits, services.notaryIdentityKey)
} }
fun sign(txId: SecureHash): TransactionSignature {
val signableData = SignableData(txId, SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(services.notaryIdentityKey).schemeNumberID))
return services.keyManagementService.sign(signableData, services.notaryIdentityKey)
}
} }

View File

@ -11,7 +11,6 @@ import de.javakaffee.kryoserializers.ArraysAsListSerializer
import de.javakaffee.kryoserializers.BitSetSerializer import de.javakaffee.kryoserializers.BitSetSerializer
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer
import de.javakaffee.kryoserializers.guava.* import de.javakaffee.kryoserializers.guava.*
import net.corda.core.crypto.MetaData
import net.corda.core.crypto.composite.CompositeKey import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.CordaPluginRegistry
import net.corda.core.transactions.NotaryChangeWireTransaction import net.corda.core.transactions.NotaryChangeWireTransaction
@ -98,7 +97,6 @@ object DefaultKryoCustomizer {
addDefaultSerializer(SerializeAsToken::class.java, SerializeAsTokenSerializer<SerializeAsToken>()) addDefaultSerializer(SerializeAsToken::class.java, SerializeAsTokenSerializer<SerializeAsToken>())
register(MetaData::class.java, MetaDataSerializer)
register(BitSet::class.java, BitSetSerializer()) register(BitSet::class.java, BitSetSerializer())
register(Class::class.java, ClassSerializer) register(Class::class.java, ClassSerializer)

View File

@ -5,7 +5,9 @@ import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.util.MapReferenceResolver import com.esotericsoftware.kryo.util.MapReferenceResolver
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.* import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.composite.CompositeKey import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.VisibleForTesting
@ -30,8 +32,6 @@ import java.security.PrivateKey
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.CertificateFactory import java.security.cert.CertificateFactory
import java.security.spec.InvalidKeySpecException
import java.time.Instant
import java.util.* import java.util.*
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -306,7 +306,7 @@ object SignedTransactionSerializer : Serializer<SignedTransaction>() {
override fun read(kryo: Kryo, input: Input, type: Class<SignedTransaction>): SignedTransaction { override fun read(kryo: Kryo, input: Input, type: Class<SignedTransaction>): SignedTransaction {
return SignedTransaction( return SignedTransaction(
kryo.readClassAndObject(input) as SerializedBytes<CoreTransaction>, kryo.readClassAndObject(input) as SerializedBytes<CoreTransaction>,
kryo.readClassAndObject(input) as List<DigitalSignature.WithKey> kryo.readClassAndObject(input) as List<TransactionSignature>
) )
} }
} }
@ -495,35 +495,6 @@ fun <T> Kryo.withoutReferences(block: () -> T): T {
} }
} }
/** For serialising a MetaData object. */
@ThreadSafe
object MetaDataSerializer : Serializer<MetaData>() {
override fun write(kryo: Kryo, output: Output, obj: MetaData) {
output.writeString(obj.schemeCodeName)
output.writeString(obj.versionID)
kryo.writeClassAndObject(output, obj.signatureType)
kryo.writeClassAndObject(output, obj.timestamp)
kryo.writeClassAndObject(output, obj.visibleInputs)
kryo.writeClassAndObject(output, obj.signedInputs)
output.writeBytesWithLength(obj.merkleRoot)
output.writeBytesWithLength(obj.publicKey.encoded)
}
@Suppress("UNCHECKED_CAST")
@Throws(IllegalArgumentException::class, InvalidKeySpecException::class)
override fun read(kryo: Kryo, input: Input, type: Class<MetaData>): MetaData {
val schemeCodeName = input.readString()
val versionID = input.readString()
val signatureType = kryo.readClassAndObject(input) as SignatureType
val timestamp = kryo.readClassAndObject(input) as Instant?
val visibleInputs = kryo.readClassAndObject(input) as BitSet?
val signedInputs = kryo.readClassAndObject(input) as BitSet?
val merkleRoot = input.readBytesWithLength()
val publicKey = Crypto.decodePublicKey(schemeCodeName, input.readBytesWithLength())
return MetaData(schemeCodeName, versionID, signatureType, timestamp, visibleInputs, signedInputs, merkleRoot, publicKey)
}
}
/** For serialising a Logger. */ /** For serialising a Logger. */
@ThreadSafe @ThreadSafe
object LoggerSerializer : Serializer<Logger>() { object LoggerSerializer : Serializer<Logger>() {

View File

@ -10,7 +10,7 @@ import java.nio.ByteBuffer
import java.util.function.Predicate import java.util.function.Predicate
/** /**
* If a privacy salt is provided, the resulted output (merkle-leaf) is computed as * If a privacy salt is provided, the resulted output (Merkle-leaf) is computed as
* Hash(serializedObject || Hash(privacy_salt || obj_index_in_merkle_tree)). * Hash(serializedObject || Hash(privacy_salt || obj_index_in_merkle_tree)).
*/ */
fun <T : Any> serializedHash(x: T, privacySalt: PrivacySalt?, index: Int): SecureHash { fun <T : Any> serializedHash(x: T, privacySalt: PrivacySalt?, index: Int): SecureHash {

View File

@ -1,8 +1,8 @@
package net.corda.core.transactions package net.corda.core.transactions
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.toBase58String import net.corda.core.crypto.toBase58String
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
@ -36,7 +36,7 @@ data class NotaryChangeWireTransaction(
*/ */
override val id: SecureHash by lazy { serializedHash(inputs + notary + newNotary) } override val id: SecureHash by lazy { serializedHash(inputs + notary + newNotary) }
fun resolve(services: ServiceHub, sigs: List<DigitalSignature.WithKey>): NotaryChangeLedgerTransaction { fun resolve(services: ServiceHub, sigs: List<TransactionSignature>): NotaryChangeLedgerTransaction {
val resolvedInputs = inputs.map { ref -> val resolvedInputs = inputs.map { ref ->
services.loadState(ref).let { StateAndRef(it, ref) } services.loadState(ref).let { StateAndRef(it, ref) }
} }
@ -54,7 +54,7 @@ data class NotaryChangeLedgerTransaction(
override val notary: Party, override val notary: Party,
val newNotary: Party, val newNotary: Party,
override val id: SecureHash, override val id: SecureHash,
override val sigs: List<DigitalSignature.WithKey> override val sigs: List<TransactionSignature>
) : FullTransaction(), TransactionWithSignatures { ) : FullTransaction(), TransactionWithSignatures {
init { init {
checkEncumbrances() checkEncumbrances()

View File

@ -1,8 +1,8 @@
package net.corda.core.transactions package net.corda.core.transactions
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.TransactionSignature
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
@ -29,10 +29,10 @@ import java.util.*
*/ */
// DOCSTART 1 // DOCSTART 1
data class SignedTransaction(val txBits: SerializedBytes<CoreTransaction>, data class SignedTransaction(val txBits: SerializedBytes<CoreTransaction>,
override val sigs: List<DigitalSignature.WithKey> override val sigs: List<TransactionSignature>
) : TransactionWithSignatures { ) : TransactionWithSignatures {
// DOCEND 1 // DOCEND 1
constructor(ctx: CoreTransaction, sigs: List<DigitalSignature.WithKey>) : this(ctx.serialize(), sigs) { constructor(ctx: CoreTransaction, sigs: List<TransactionSignature>) : this(ctx.serialize(), sigs) {
cachedTransaction = ctx cachedTransaction = ctx
} }
@ -75,16 +75,16 @@ data class SignedTransaction(val txBits: SerializedBytes<CoreTransaction>,
} }
/** Returns the same transaction but with an additional (unchecked) signature. */ /** Returns the same transaction but with an additional (unchecked) signature. */
fun withAdditionalSignature(sig: DigitalSignature.WithKey) = copyWithCache(listOf(sig)) fun withAdditionalSignature(sig: TransactionSignature) = copyWithCache(listOf(sig))
/** Returns the same transaction but with an additional (unchecked) signatures. */ /** Returns the same transaction but with an additional (unchecked) signatures. */
fun withAdditionalSignatures(sigList: Iterable<DigitalSignature.WithKey>) = copyWithCache(sigList) fun withAdditionalSignatures(sigList: Iterable<TransactionSignature>) = copyWithCache(sigList)
/** /**
* Creates a copy of the SignedTransaction that includes the provided [sigList]. Also propagates the [cachedTransaction] * Creates a copy of the SignedTransaction that includes the provided [sigList]. Also propagates the [cachedTransaction]
* so the contained transaction does not need to be deserialized again. * so the contained transaction does not need to be deserialized again.
*/ */
private fun copyWithCache(sigList: Iterable<DigitalSignature.WithKey>): SignedTransaction { private fun copyWithCache(sigList: Iterable<TransactionSignature>): SignedTransaction {
val cached = cachedTransaction val cached = cachedTransaction
return copy(sigs = sigs + sigList).apply { return copy(sigs = sigs + sigList).apply {
cachedTransaction = cached cachedTransaction = cached
@ -92,10 +92,10 @@ data class SignedTransaction(val txBits: SerializedBytes<CoreTransaction>,
} }
/** Alias for [withAdditionalSignature] to let you use Kotlin operator overloading. */ /** Alias for [withAdditionalSignature] to let you use Kotlin operator overloading. */
operator fun plus(sig: DigitalSignature.WithKey) = withAdditionalSignature(sig) operator fun plus(sig: TransactionSignature) = withAdditionalSignature(sig)
/** Alias for [withAdditionalSignatures] to let you use Kotlin operator overloading. */ /** Alias for [withAdditionalSignatures] to let you use Kotlin operator overloading. */
operator fun plus(sigList: Collection<DigitalSignature.WithKey>) = withAdditionalSignatures(sigList) operator fun plus(sigList: Collection<TransactionSignature>) = withAdditionalSignatures(sigList)
/** /**
* Checks the transaction's signatures are valid, optionally calls [verifyRequiredSignatures] to * Checks the transaction's signatures are valid, optionally calls [verifyRequiredSignatures] to

View File

@ -151,18 +151,18 @@ open class TransactionBuilder(
/** The signatures that have been collected so far - might be incomplete! */ /** The signatures that have been collected so far - might be incomplete! */
@Deprecated("Signatures should be gathered on a SignedTransaction instead.") @Deprecated("Signatures should be gathered on a SignedTransaction instead.")
protected val currentSigs = arrayListOf<DigitalSignature.WithKey>() protected val currentSigs = arrayListOf<TransactionSignature>()
@Deprecated("Use ServiceHub.signInitialTransaction() instead.") @Deprecated("Use ServiceHub.signInitialTransaction() instead.")
fun signWith(key: KeyPair): TransactionBuilder { fun signWith(key: KeyPair): TransactionBuilder {
val data = toWireTransaction().id val signableData = SignableData(toWireTransaction().id, SignatureMetadata(1, Crypto.findSignatureScheme(key.public).schemeNumberID)) // A dummy platformVersion.
addSignatureUnchecked(key.sign(data.bytes)) addSignatureUnchecked(key.sign(signableData))
return this return this
} }
/** Adds the signature directly to the transaction, without checking it for validity. */ /** Adds the signature directly to the transaction, without checking it for validity. */
@Deprecated("Use ServiceHub.signInitialTransaction() instead.") @Deprecated("Use ServiceHub.signInitialTransaction() instead.")
fun addSignatureUnchecked(sig: DigitalSignature.WithKey): TransactionBuilder { fun addSignatureUnchecked(sig: TransactionSignature): TransactionBuilder {
currentSigs.add(sig) currentSigs.add(sig)
return this return this
} }
@ -188,7 +188,7 @@ open class TransactionBuilder(
* @throws IllegalArgumentException if the signature key doesn't appear in any command. * @throws IllegalArgumentException if the signature key doesn't appear in any command.
*/ */
@Deprecated("Use WireTransaction.checkSignature() instead.") @Deprecated("Use WireTransaction.checkSignature() instead.")
fun checkAndAddSignature(sig: DigitalSignature.WithKey) { fun checkAndAddSignature(sig: TransactionSignature) {
checkSignature(sig) checkSignature(sig)
addSignatureUnchecked(sig) addSignatureUnchecked(sig)
} }
@ -200,7 +200,7 @@ open class TransactionBuilder(
* @throws IllegalArgumentException if the signature key doesn't appear in any command. * @throws IllegalArgumentException if the signature key doesn't appear in any command.
*/ */
@Deprecated("Use WireTransaction.checkSignature() instead.") @Deprecated("Use WireTransaction.checkSignature() instead.")
fun checkSignature(sig: DigitalSignature.WithKey) { fun checkSignature(sig: TransactionSignature) {
require(commands.any { it.signers.any { sig.by in it.keys } }) { "Signature key doesn't match any command" } require(commands.any { it.signers.any { sig.by in it.keys } }) { "Signature key doesn't match any command" }
sig.verify(toWireTransaction().id) sig.verify(toWireTransaction().id)
} }

View File

@ -1,7 +1,7 @@
package net.corda.core.transactions package net.corda.core.transactions
import net.corda.core.contracts.NamedByHash import net.corda.core.contracts.NamedByHash
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.isFulfilledBy import net.corda.core.crypto.isFulfilledBy
import net.corda.core.transactions.SignedTransaction.SignaturesMissingException import net.corda.core.transactions.SignedTransaction.SignaturesMissingException
import net.corda.core.utilities.toNonEmptySet import net.corda.core.utilities.toNonEmptySet
@ -10,7 +10,7 @@ import java.security.SignatureException
/** An interface for transactions containing signatures, with logic for signature verification */ /** An interface for transactions containing signatures, with logic for signature verification */
interface TransactionWithSignatures : NamedByHash { interface TransactionWithSignatures : NamedByHash {
val sigs: List<DigitalSignature.WithKey> val sigs: List<TransactionSignature>
/** Specifies all the public keys that require signatures for the transaction to be valid */ /** Specifies all the public keys that require signatures for the transaction to be valid */
val requiredSigningKeys: Set<PublicKey> val requiredSigningKeys: Set<PublicKey>
@ -57,7 +57,7 @@ interface TransactionWithSignatures : NamedByHash {
@Throws(SignatureException::class) @Throws(SignatureException::class)
fun checkSignaturesAreValid() { fun checkSignaturesAreValid() {
for (sig in sigs) { for (sig in sigs) {
sig.verify(id.bytes) sig.verify(id)
} }
} }

View File

@ -1,9 +1,9 @@
package net.corda.core.transactions package net.corda.core.transactions
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.MerkleTree import net.corda.core.crypto.MerkleTree
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.keys import net.corda.core.crypto.keys
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.Emoji import net.corda.core.internal.Emoji
@ -172,7 +172,7 @@ data class WireTransaction(
* @throws SignatureException if the signature didn't match the transaction contents. * @throws SignatureException if the signature didn't match the transaction contents.
* @throws IllegalArgumentException if the signature key doesn't appear in any command. * @throws IllegalArgumentException if the signature key doesn't appear in any command.
*/ */
fun checkSignature(sig: DigitalSignature.WithKey) { fun checkSignature(sig: TransactionSignature) {
require(commands.any { it.signers.any { sig.by in it.keys } }) { "Signature key doesn't match any command" } require(commands.any { it.signers.any { sig.by in it.keys } }) { "Signature key doesn't match any command" }
sig.verify(id) sig.verify(id)
} }

View File

@ -1,10 +1,8 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.*
import net.corda.core.crypto.composite.CompositeKey import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.sign
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -19,8 +17,12 @@ import kotlin.test.assertNotEquals
class TransactionTests : TestDependencyInjectionBase() { class TransactionTests : TestDependencyInjectionBase() {
private fun makeSigned(wtx: WireTransaction, vararg keys: KeyPair, notarySig: Boolean = true): SignedTransaction { private fun makeSigned(wtx: WireTransaction, vararg keys: KeyPair, notarySig: Boolean = true): SignedTransaction {
val keySigs = keys.map { it.sign(wtx.id.bytes) } val keySigs = keys.map { it.sign(SignableData(wtx.id, SignatureMetadata(1, Crypto.findSignatureScheme(it.public).schemeNumberID))) }
val sigs = if (notarySig) keySigs + DUMMY_NOTARY_KEY.sign(wtx.id.bytes) else keySigs val sigs = if (notarySig) {
keySigs + DUMMY_NOTARY_KEY.sign(SignableData(wtx.id, SignatureMetadata(1, Crypto.findSignatureScheme(DUMMY_NOTARY_KEY.public).schemeNumberID)))
} else {
keySigs
}
return SignedTransaction(wtx, sigs) return SignedTransaction(wtx, sigs)
} }

View File

@ -36,13 +36,16 @@ class CompositeKeyTests : TestDependencyInjectionBase() {
val charliePublicKey: PublicKey = charlieKey.public val charliePublicKey: PublicKey = charlieKey.public
val message = OpaqueBytes("Transaction".toByteArray()) val message = OpaqueBytes("Transaction".toByteArray())
val secureHash = message.sha256()
val aliceSignature = aliceKey.sign(message) // By lazy is required so that the serialisers are configured before vals initialisation takes place (they internally invoke serialise).
val bobSignature = bobKey.sign(message) val aliceSignature by lazy { aliceKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(alicePublicKey).schemeNumberID))) }
val charlieSignature = charlieKey.sign(message) val bobSignature by lazy { bobKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(bobPublicKey).schemeNumberID))) }
val charlieSignature by lazy { charlieKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(charliePublicKey).schemeNumberID))) }
@Test @Test
fun `(Alice) fulfilled by Alice signature`() { fun `(Alice) fulfilled by Alice signature`() {
println(aliceKey.serialize().hash)
assertTrue { alicePublicKey.isFulfilledBy(aliceSignature.by) } assertTrue { alicePublicKey.isFulfilledBy(aliceSignature.by) }
assertFalse { alicePublicKey.isFulfilledBy(charlieSignature.by) } assertFalse { alicePublicKey.isFulfilledBy(charlieSignature.by) }
} }
@ -153,11 +156,12 @@ class CompositeKeyTests : TestDependencyInjectionBase() {
* Check that verifying a composite signature using the [CompositeSignature] engine works. * Check that verifying a composite signature using the [CompositeSignature] engine works.
*/ */
@Test @Test
fun `composite signature verification`() { fun `composite TransactionSignature verification `() {
val twoOfThree = CompositeKey.Builder().addKeys(alicePublicKey, bobPublicKey, charliePublicKey).build(threshold = 2) val twoOfThree = CompositeKey.Builder().addKeys(alicePublicKey, bobPublicKey, charliePublicKey).build(threshold = 2)
val engine = CompositeSignature() val engine = CompositeSignature()
engine.initVerify(twoOfThree) engine.initVerify(twoOfThree)
engine.update(message.bytes) engine.update(secureHash.bytes)
assertFalse { engine.verify(CompositeSignaturesWithKeys(listOf(aliceSignature)).serialize().bytes) } assertFalse { engine.verify(CompositeSignaturesWithKeys(listOf(aliceSignature)).serialize().bytes) }
assertFalse { engine.verify(CompositeSignaturesWithKeys(listOf(bobSignature)).serialize().bytes) } assertFalse { engine.verify(CompositeSignaturesWithKeys(listOf(bobSignature)).serialize().bytes) }
@ -168,7 +172,7 @@ class CompositeKeyTests : TestDependencyInjectionBase() {
assertTrue { engine.verify(CompositeSignaturesWithKeys(listOf(aliceSignature, bobSignature, charlieSignature)).serialize().bytes) } assertTrue { engine.verify(CompositeSignaturesWithKeys(listOf(aliceSignature, bobSignature, charlieSignature)).serialize().bytes) }
// Check the underlying signature is validated // Check the underlying signature is validated
val brokenBobSignature = DigitalSignature.WithKey(bobSignature.by, aliceSignature.bytes) val brokenBobSignature = TransactionSignature(aliceSignature.bytes, bobSignature.by, SignatureMetadata(1, Crypto.findSignatureScheme(bobSignature.by).schemeNumberID))
assertFalse { engine.verify(CompositeSignaturesWithKeys(listOf(aliceSignature, brokenBobSignature)).serialize().bytes) } assertFalse { engine.verify(CompositeSignaturesWithKeys(listOf(aliceSignature, brokenBobSignature)).serialize().bytes) }
} }
@ -282,19 +286,19 @@ class CompositeKeyTests : TestDependencyInjectionBase() {
@Test @Test
fun `CompositeKey from multiple signature schemes and signature verification`() { fun `CompositeKey from multiple signature schemes and signature verification`() {
val (privRSA, pubRSA) = Crypto.generateKeyPair(Crypto.RSA_SHA256) val keyPairRSA = Crypto.generateKeyPair(Crypto.RSA_SHA256)
val (privK1, pubK1) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256) val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val (privR1, pubR1) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val (privEd, pubEd) = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512) val keyPairEd = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
val (privSP, pubSP) = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256) val keyPairSP = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
val RSASignature = privRSA.sign(message.bytes, pubRSA) val RSASignature = keyPairRSA.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairRSA.public).schemeNumberID)))
val K1Signature = privK1.sign(message.bytes, pubK1) val K1Signature = keyPairK1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairK1.public).schemeNumberID)))
val R1Signature = privR1.sign(message.bytes, pubR1) val R1Signature = keyPairR1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairR1.public).schemeNumberID)))
val EdSignature = privEd.sign(message.bytes, pubEd) val EdSignature = keyPairEd.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairEd.public).schemeNumberID)))
val SPSignature = privSP.sign(message.bytes, pubSP) val SPSignature = keyPairSP.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairSP.public).schemeNumberID)))
val compositeKey = CompositeKey.Builder().addKeys(pubRSA, pubK1, pubR1, pubEd, pubSP).build() as CompositeKey val compositeKey = CompositeKey.Builder().addKeys(keyPairRSA.public, keyPairK1.public, keyPairR1.public, keyPairEd.public, keyPairSP.public).build() as CompositeKey
val signatures = listOf(RSASignature, K1Signature, R1Signature, EdSignature, SPSignature) val signatures = listOf(RSASignature, K1Signature, R1Signature, EdSignature, SPSignature)
assertTrue { compositeKey.isFulfilledBy(signatures.byKeys()) } assertTrue { compositeKey.isFulfilledBy(signatures.byKeys()) }
@ -307,19 +311,19 @@ class CompositeKeyTests : TestDependencyInjectionBase() {
@Test @Test
fun `Test save to keystore`() { fun `Test save to keystore`() {
// From test case [CompositeKey from multiple signature schemes and signature verification] // From test case [CompositeKey from multiple signature schemes and signature verification]
val (privRSA, pubRSA) = Crypto.generateKeyPair(Crypto.RSA_SHA256) val keyPairRSA = Crypto.generateKeyPair(Crypto.RSA_SHA256)
val (privK1, pubK1) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256) val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val (privR1, pubR1) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val (privEd, pubEd) = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512) val keyPairEd = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
val (privSP, pubSP) = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256) val keyPairSP = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
val RSASignature = privRSA.sign(message.bytes, pubRSA) val RSASignature = keyPairRSA.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairRSA.public).schemeNumberID)))
val K1Signature = privK1.sign(message.bytes, pubK1) val K1Signature = keyPairK1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairK1.public).schemeNumberID)))
val R1Signature = privR1.sign(message.bytes, pubR1) val R1Signature = keyPairR1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairR1.public).schemeNumberID)))
val EdSignature = privEd.sign(message.bytes, pubEd) val EdSignature = keyPairEd.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairEd.public).schemeNumberID)))
val SPSignature = privSP.sign(message.bytes, pubSP) val SPSignature = keyPairSP.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairSP.public).schemeNumberID)))
val compositeKey = CompositeKey.Builder().addKeys(pubRSA, pubK1, pubR1, pubEd, pubSP).build() as CompositeKey val compositeKey = CompositeKey.Builder().addKeys(keyPairRSA.public, keyPairK1.public, keyPairR1.public, keyPairEd.public, keyPairSP.public).build() as CompositeKey
val signatures = listOf(RSASignature, K1Signature, R1Signature, EdSignature, SPSignature) val signatures = listOf(RSASignature, K1Signature, R1Signature, EdSignature, SPSignature)
assertTrue { compositeKey.isFulfilledBy(signatures.byKeys()) } assertTrue { compositeKey.isFulfilledBy(signatures.byKeys()) }

View File

@ -3,73 +3,39 @@ package net.corda.core.crypto
import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.TestDependencyInjectionBase
import org.junit.Test import org.junit.Test
import java.security.SignatureException import java.security.SignatureException
import java.time.Instant
import kotlin.test.assertTrue import kotlin.test.assertTrue
/** /**
* Digital signature MetaData tests * Digital signature MetaData tests.
*/ */
class TransactionSignatureTest : TestDependencyInjectionBase() { class TransactionSignatureTest : TestDependencyInjectionBase() {
val testBytes = "12345678901234567890123456789012".toByteArray() val testBytes = "12345678901234567890123456789012".toByteArray()
/** valid sign and verify. */ /** Valid sign and verify. */
@Test @Test
fun `MetaData Full sign and verify`() { fun `Signature metadata full sign and verify`() {
val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256") val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
// create a MetaData.Full object // Create a SignableData object.
val meta = MetaData("ECDSA_SECP256K1_SHA256", "M9", SignatureType.FULL, Instant.now(), null, null, testBytes, keyPair.public) val signableData = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(keyPair.public).schemeNumberID))
// sign the message // Sign the meta object.
val transactionSignature: TransactionSignature = keyPair.private.sign(meta) val transactionSignature: TransactionSignature = keyPair.sign(signableData)
// check auto-verification // Check auto-verification.
assertTrue(transactionSignature.verify()) assertTrue(transactionSignature.verify(testBytes.sha256()))
// check manual verification // Check manual verification.
assertTrue(keyPair.public.verify(transactionSignature)) assertTrue(Crypto.doVerify(testBytes.sha256(), transactionSignature))
} }
/** Signing should fail, as I sign with a secpK1 key, but set schemeCodeName is set to secpR1. */ /** Verification should fail; corrupted metadata - clearData (Merkle root) has changed. */
@Test(expected = IllegalArgumentException::class) @Test(expected = SignatureException::class)
fun `MetaData Full failure wrong scheme`() { fun `Signature metadata full failure clearData has changed`() {
val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256") val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
val meta = MetaData("ECDSA_SECP256R1_SHA256", "M9", SignatureType.FULL, Instant.now(), null, null, testBytes, keyPair.public) val signableData = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(keyPair.public).schemeNumberID))
keyPair.private.sign(meta) val transactionSignature = keyPair.sign(signableData)
} Crypto.doVerify((testBytes + testBytes).sha256(), transactionSignature)
/** Verification should fail; corrupted metadata - public key has changed. */
@Test(expected = SignatureException::class)
fun `MetaData Full failure public key has changed`() {
val keyPair1 = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
val keyPair2 = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
val meta = MetaData("ECDSA_SECP256K1_SHA256", "M9", SignatureType.FULL, Instant.now(), null, null, testBytes, keyPair2.public)
val transactionSignature = keyPair1.private.sign(meta)
transactionSignature.verify()
}
/** Verification should fail; corrupted metadata - clearData has changed. */
@Test(expected = SignatureException::class)
fun `MetaData Full failure clearData has changed`() {
val keyPair1 = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
val meta = MetaData("ECDSA_SECP256K1_SHA256", "M9", SignatureType.FULL, Instant.now(), null, null, testBytes, keyPair1.public)
val transactionSignature = keyPair1.private.sign(meta)
val meta2 = MetaData("ECDSA_SECP256K1_SHA256", "M9", SignatureType.FULL, Instant.now(), null, null, testBytes.plus(testBytes), keyPair1.public)
val transactionSignature2 = TransactionSignature(transactionSignature.signatureData, meta2)
keyPair1.public.verify(transactionSignature2)
}
/** Verification should fail; corrupted metadata - schemeCodeName has changed from K1 to R1. */
@Test(expected = SignatureException::class)
fun `MetaData Wrong schemeCodeName has changed`() {
val keyPair1 = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
val meta = MetaData("ECDSA_SECP256K1_SHA256", "M9", SignatureType.FULL, Instant.now(), null, null, testBytes, keyPair1.public)
val transactionSignature = keyPair1.private.sign(meta)
val meta2 = MetaData("ECDSA_SECP256R1_SHA256", "M9", SignatureType.FULL, Instant.now(), null, null, testBytes.plus(testBytes), keyPair1.public)
val transactionSignature2 = TransactionSignature(transactionSignature.signatureData, meta2)
keyPair1.public.verify(transactionSignature2)
} }
} }

View File

@ -9,6 +9,7 @@ import net.corda.nodeapi.serialization.KryoHeaderV0_1
import net.corda.nodeapi.serialization.SerializationContextImpl import net.corda.nodeapi.serialization.SerializationContextImpl
import net.corda.nodeapi.serialization.SerializationFactoryImpl import net.corda.nodeapi.serialization.SerializationFactoryImpl
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.testing.ALICE_PUBKEY
import net.corda.testing.BOB import net.corda.testing.BOB
import net.corda.testing.BOB_PUBKEY import net.corda.testing.BOB_PUBKEY
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
@ -117,16 +118,13 @@ class KryoTests {
} }
@Test @Test
fun `serialize - deserialize MetaData`() { fun `serialize - deserialize SignableData`() {
val testString = "Hello World" val testString = "Hello World"
val testBytes = testString.toByteArray() val testBytes = testString.toByteArray()
val keyPair1 = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
val bitSet = java.util.BitSet(10)
bitSet.set(3)
val meta = MetaData("ECDSA_SECP256K1_SHA256", "M9", SignatureType.FULL, Instant.now(), bitSet, bitSet, testBytes, keyPair1.public) val meta = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(ALICE_PUBKEY).schemeNumberID))
val serializedMetaData = meta.serialize(factory, context).bytes val serializedMetaData = meta.serialize(factory, context).bytes
val meta2 = serializedMetaData.deserialize<MetaData>(factory, context) val meta2 = serializedMetaData.deserialize<SignableData>(factory, context)
assertEquals(meta2, meta) assertEquals(meta2, meta)
} }

View File

@ -93,7 +93,7 @@ Where:
* ``outputs`` is a list of the transaction's outputs' * ``outputs`` is a list of the transaction's outputs'
* ``attachments`` is a list of the transaction's attachments' * ``attachments`` is a list of the transaction's attachments'
* ``commands`` is a list of the transaction's commands, and their associated signatures' * ``commands`` is a list of the transaction's commands, and their associated signatures'
* ``id`` is the transaction's merkle root hash' * ``id`` is the transaction's Merkle root hash'
* ``notary`` is the transaction's notary. If there are inputs these must have the same notary on their source transactions. * ``notary`` is the transaction's notary. If there are inputs these must have the same notary on their source transactions.
* ``timeWindow`` is the transaction's timestamp and defines the acceptable delay for notarisation. * ``timeWindow`` is the transaction's timestamp and defines the acceptable delay for notarisation.

View File

@ -5,8 +5,8 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import net.corda.contracts.asset.Cash; import net.corda.contracts.asset.Cash;
import net.corda.core.contracts.*; import net.corda.core.contracts.*;
import net.corda.core.crypto.DigitalSignature;
import net.corda.core.crypto.SecureHash; import net.corda.core.crypto.SecureHash;
import net.corda.core.crypto.TransactionSignature;
import net.corda.core.flows.*; import net.corda.core.flows.*;
import net.corda.core.identity.Party; import net.corda.core.identity.Party;
import net.corda.core.internal.FetchDataFlow; import net.corda.core.internal.FetchDataFlow;
@ -30,10 +30,8 @@ import java.security.PublicKey;
import java.security.SignatureException; import java.security.SignatureException;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import static net.corda.core.contracts.ContractsDSL.requireThat; import static net.corda.core.contracts.ContractsDSL.requireThat;
import static net.corda.testing.TestConstants.getDUMMY_PUBKEY_1; import static net.corda.testing.TestConstants.getDUMMY_PUBKEY_1;
@ -383,11 +381,11 @@ public class FlowCookbookJava {
// node does not need to check we haven't changed anything in the // node does not need to check we haven't changed anything in the
// transaction. // transaction.
// DOCSTART 40 // DOCSTART 40
DigitalSignature.WithKey sig = getServiceHub().createSignature(onceSignedTx); TransactionSignature sig = getServiceHub().createSignature(onceSignedTx);
// DOCEND 40 // DOCEND 40
// And again, if we wanted to use a different public key: // And again, if we wanted to use a different public key:
// DOCSTART 41 // DOCSTART 41
DigitalSignature.WithKey sig2 = getServiceHub().createSignature(onceSignedTx, otherKey2); TransactionSignature sig2 = getServiceHub().createSignature(onceSignedTx, otherKey2);
// DOCEND 41 // DOCEND 41
/*---------------------------- /*----------------------------

View File

@ -5,8 +5,8 @@ package net.corda.docs
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.contracts.asset.Cash import net.corda.contracts.asset.Cash
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.TransactionSignature
import net.corda.core.flows.* import net.corda.core.flows.*
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.FetchDataFlow import net.corda.core.internal.FetchDataFlow
@ -363,11 +363,11 @@ object FlowCookbook {
// node does not need to check we haven't changed anything in the // node does not need to check we haven't changed anything in the
// transaction. // transaction.
// DOCSTART 40 // DOCSTART 40
val sig: DigitalSignature.WithKey = serviceHub.createSignature(onceSignedTx) val sig: TransactionSignature = serviceHub.createSignature(onceSignedTx)
// DOCEND 40 // DOCEND 40
// And again, if we wanted to use a different public key: // And again, if we wanted to use a different public key:
// DOCSTART 41 // DOCSTART 41
val sig2: DigitalSignature.WithKey = serviceHub.createSignature(onceSignedTx, otherKey2) val sig2: TransactionSignature = serviceHub.createSignature(onceSignedTx, otherKey2)
// DOCEND 41 // DOCEND 41
// In practice, however, the process of gathering every signature // In practice, however, the process of gathering every signature

View File

@ -5,8 +5,11 @@ import net.corda.contracts.asset.Cash
import net.corda.core.contracts.Amount import net.corda.core.contracts.Amount
import net.corda.core.contracts.Issued import net.corda.core.contracts.Issued
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.TransactionSignature
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.* import net.corda.core.flows.*
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
@ -154,7 +157,7 @@ class ForeignExchangeFlow(val tradeId: String,
// pass transaction details to the counterparty to revalidate and confirm with a signature // pass transaction details to the counterparty to revalidate and confirm with a signature
// Allow otherParty to access our data to resolve the transaction. // Allow otherParty to access our data to resolve the transaction.
subFlow(SendTransactionFlow(remoteRequestWithNotary.owner, signedTransaction)) subFlow(SendTransactionFlow(remoteRequestWithNotary.owner, signedTransaction))
val allPartySignedTx = receive<DigitalSignature.WithKey>(remoteRequestWithNotary.owner).unwrap { val allPartySignedTx = receive<TransactionSignature>(remoteRequestWithNotary.owner).unwrap {
val withNewSignature = signedTransaction + it val withNewSignature = signedTransaction + it
// check all signatures are present except the notary // check all signatures are present except the notary
withNewSignature.verifySignaturesExcept(withNewSignature.tx.notary!!.owningKey) withNewSignature.verifySignaturesExcept(withNewSignature.tx.notary!!.owningKey)

View File

@ -2,8 +2,8 @@ package net.corda.docs
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.containsAny import net.corda.core.crypto.containsAny
import net.corda.core.flows.FinalityFlow import net.corda.core.flows.FinalityFlow
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
@ -188,7 +188,7 @@ class SubmitCompletionFlow(val ref: StateRef, val verdict: WorkflowState) : Flow
val selfSignedTx = serviceHub.signInitialTransaction(tx) val selfSignedTx = serviceHub.signInitialTransaction(tx)
//DOCEND 2 //DOCEND 2
// Send the signed transaction to the originator and await their signature to confirm // Send the signed transaction to the originator and await their signature to confirm
val allPartySignedTx = sendAndReceive<DigitalSignature.WithKey>(newState.source, selfSignedTx).unwrap { val allPartySignedTx = sendAndReceive<TransactionSignature>(newState.source, selfSignedTx).unwrap {
// Add their signature to our unmodified transaction. To check they signed the same tx. // Add their signature to our unmodified transaction. To check they signed the same tx.
val agreedTx = selfSignedTx + it val agreedTx = selfSignedTx + it
// Receive back their signature and confirm that it is for an unmodified transaction // Receive back their signature and confirm that it is for an unmodified transaction

View File

@ -112,7 +112,7 @@ Here is an extract from the ``NodeInterestRates.Oracle`` class and supporting ty
class Oracle { class Oracle {
fun query(queries: List<FixOf>, deadline: Instant): List<Fix> fun query(queries: List<FixOf>, deadline: Instant): List<Fix>
fun sign(ftx: FilteredTransaction, merkleRoot: SecureHash): DigitalSignature.WithKey fun sign(ftx: FilteredTransaction, txId: SecureHash): DigitalSignature.WithKey
} }
Because the fix contains a timestamp (the ``forDay`` field), that identifies the version of the data being requested, Because the fix contains a timestamp (the ``forDay`` field), that identifies the version of the data being requested,

View File

@ -8,7 +8,7 @@ import net.corda.contracts.asset.Cash
import net.corda.core.contracts.Command import net.corda.core.contracts.Command
import net.corda.core.contracts.CommandData import net.corda.core.contracts.CommandData
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.crypto.testing.NullSignature import net.corda.core.crypto.testing.NULL_SIGNATURE
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.testing.* import net.corda.core.testing.*
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -82,7 +82,7 @@ class SignedTransactionGenerator : Generator<SignedTransaction>(SignedTransactio
val wireTransaction = WiredTransactionGenerator().generate(random, status) val wireTransaction = WiredTransactionGenerator().generate(random, status)
return SignedTransaction( return SignedTransaction(
ctx = wireTransaction, ctx = wireTransaction,
sigs = listOf(NullSignature) sigs = listOf(NULL_SIGNATURE)
) )
} }
} }

View File

@ -1,12 +1,9 @@
package net.corda.node.services.keys package net.corda.node.services.keys
import net.corda.core.internal.ThreadBox import net.corda.core.crypto.*
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.keys
import net.corda.core.crypto.sign
import net.corda.core.identity.AnonymousPartyAndPath import net.corda.core.identity.AnonymousPartyAndPath
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.ThreadBox
import net.corda.core.node.services.IdentityService import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.KeyManagementService import net.corda.core.node.services.KeyManagementService
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
@ -75,7 +72,13 @@ class E2ETestKeyManagementService(val identityService: IdentityService,
override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey { override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
val keyPair = getSigningKeyPair(publicKey) val keyPair = getSigningKeyPair(publicKey)
val signature = keyPair.sign(bytes) return keyPair.sign(bytes)
return signature }
// TODO: A full KeyManagementService implementation needs to record activity to the Audit Service and to limit
// signing to appropriately authorised contexts and initiating users.
override fun sign(signableData: SignableData, publicKey: PublicKey): TransactionSignature {
val keyPair = getSigningKeyPair(publicKey)
return keyPair.sign(signableData)
} }
} }

View File

@ -1,12 +1,9 @@
package net.corda.node.services.keys package net.corda.node.services.keys
import net.corda.core.internal.ThreadBox import net.corda.core.crypto.*
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.keys
import net.corda.core.crypto.sign
import net.corda.core.identity.AnonymousPartyAndPath import net.corda.core.identity.AnonymousPartyAndPath
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.ThreadBox
import net.corda.core.node.services.IdentityService import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.KeyManagementService import net.corda.core.node.services.KeyManagementService
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
@ -86,8 +83,13 @@ class PersistentKeyManagementService(val identityService: IdentityService,
override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey { override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
val keyPair = getSigningKeyPair(publicKey) val keyPair = getSigningKeyPair(publicKey)
val signature = keyPair.sign(bytes) return keyPair.sign(bytes)
return signature
} }
// TODO: A full KeyManagementService implementation needs to record activity to the Audit Service and to limit
// signing to appropriately authorised contexts and initiating users.
override fun sign(signableData: SignableData, publicKey: PublicKey): TransactionSignature {
val keyPair = getSigningKeyPair(publicKey)
return keyPair.sign(signableData)
}
} }

View File

@ -2,7 +2,10 @@ package net.corda.node.services.transactions
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import com.google.common.util.concurrent.SettableFuture import com.google.common.util.concurrent.SettableFuture
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SignableData
import net.corda.core.crypto.SignatureMetadata
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.NotaryException import net.corda.core.flows.NotaryException
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -109,7 +112,8 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, c
commitInputStates(inputs, id, callerIdentity) commitInputStates(inputs, id, callerIdentity)
log.debug { "Inputs committed successfully, signing $id" } log.debug { "Inputs committed successfully, signing $id" }
val sig = sign(id.bytes) val signableData = SignableData(id, SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(services.notaryIdentityKey).schemeNumberID))
val sig = sign(signableData)
BFTSMaRt.ReplicaResponse.Signature(sig) BFTSMaRt.ReplicaResponse.Signature(sig)
} catch (e: NotaryException) { } catch (e: NotaryException) {
log.debug { "Error processing transaction: ${e.error}" } log.debug { "Error processing transaction: ${e.error}" }

View File

@ -14,21 +14,18 @@ import bftsmart.tom.server.defaultservices.DefaultReplier
import bftsmart.tom.util.Extractor import bftsmart.tom.util.Extractor
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TimeWindow import net.corda.core.contracts.TimeWindow
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.*
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignedData
import net.corda.core.crypto.sign
import net.corda.core.flows.NotaryError import net.corda.core.flows.NotaryError
import net.corda.core.flows.NotaryException import net.corda.core.flows.NotaryException
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.declaredField import net.corda.core.internal.declaredField
import net.corda.core.internal.toTypedArray
import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TimeWindowChecker
import net.corda.core.node.services.UniquenessProvider import net.corda.core.node.services.UniquenessProvider
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.internal.toTypedArray
import net.corda.core.transactions.FilteredTransaction import net.corda.core.transactions.FilteredTransaction
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.debug import net.corda.core.utilities.debug
@ -176,7 +173,7 @@ object BFTSMaRt {
abstract class Replica(config: BFTSMaRtConfig, abstract class Replica(config: BFTSMaRtConfig,
replicaId: Int, replicaId: Int,
tableName: String, tableName: String,
private val services: ServiceHubInternal, protected val services: ServiceHubInternal,
private val timeWindowChecker: TimeWindowChecker) : DefaultRecoverable() { private val timeWindowChecker: TimeWindowChecker) : DefaultRecoverable() {
companion object { companion object {
private val log = loggerFor<Replica>() private val log = loggerFor<Replica>()
@ -253,6 +250,10 @@ object BFTSMaRt {
return services.database.transaction { services.keyManagementService.sign(bytes, services.notaryIdentityKey) } return services.database.transaction { services.keyManagementService.sign(bytes, services.notaryIdentityKey) }
} }
protected fun sign(signableData: SignableData): TransactionSignature {
return services.database.transaction { services.keyManagementService.sign(signableData, services.notaryIdentityKey) }
}
// TODO: // TODO:
// - Test snapshot functionality with different bft-smart cluster configurations. // - Test snapshot functionality with different bft-smart cluster configurations.
// - Add streaming to support large data sets. // - Add streaming to support large data sets.

View File

@ -8,8 +8,7 @@ import net.corda.contracts.asset.Cash
import net.corda.contracts.asset.`issued by` import net.corda.contracts.asset.`issued by`
import net.corda.contracts.asset.`owned by` import net.corda.contracts.asset.`owned by`
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.*
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatedBy import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
@ -604,11 +603,11 @@ class TwoPartyTradeFlowTests {
val signed = wtxToSign.map { val signed = wtxToSign.map {
val id = it.id val id = it.id
val sigs = mutableListOf<DigitalSignature.WithKey>() val sigs = mutableListOf<TransactionSignature>()
sigs.add(node.services.keyManagementService.sign(id.bytes, node.services.legalIdentityKey)) sigs.add(node.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(node.services.legalIdentityKey).schemeNumberID)), node.services.legalIdentityKey))
sigs.add(notaryNode.services.keyManagementService.sign(id.bytes, notaryNode.services.notaryIdentityKey)) sigs.add(notaryNode.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(notaryNode.services.notaryIdentityKey).schemeNumberID)), notaryNode.services.notaryIdentityKey))
extraSigningNodes.forEach { currentNode -> extraSigningNodes.forEach { currentNode ->
sigs.add(currentNode.services.keyManagementService.sign(id.bytes, currentNode.info.legalIdentity.owningKey)) sigs.add(currentNode.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(currentNode.info.legalIdentity.owningKey).schemeNumberID)), currentNode.info.legalIdentity.owningKey))
} }
SignedTransaction(it, sigs) SignedTransaction(it, sigs)
} }

View File

@ -6,6 +6,7 @@ import net.corda.core.flows.FlowLogic
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.* import net.corda.core.node.services.*
import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SerializeAsToken
import net.corda.core.utilities.NonEmptySet
import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.internal.InitiatedFlowFactory
import net.corda.node.serialization.NodeClock import net.corda.node.serialization.NodeClock
import net.corda.node.services.api.* import net.corda.node.services.api.*
@ -16,6 +17,8 @@ import net.corda.node.services.statemachine.FlowStateMachineImpl
import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.statemachine.StateMachineManager
import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.services.transactions.InMemoryTransactionVerifierService
import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.CordaPersistence
import net.corda.testing.DUMMY_IDENTITY_1
import net.corda.testing.MOCK_HOST_AND_PORT
import net.corda.testing.MOCK_IDENTITY_SERVICE import net.corda.testing.MOCK_IDENTITY_SERVICE
import net.corda.testing.node.MockAttachmentStorage import net.corda.testing.node.MockAttachmentStorage
import net.corda.testing.node.MockNetworkMapCache import net.corda.testing.node.MockNetworkMapCache
@ -60,7 +63,7 @@ open class MockServiceHubInternal(
override val clock: Clock override val clock: Clock
get() = overrideClock ?: throw UnsupportedOperationException() get() = overrideClock ?: throw UnsupportedOperationException()
override val myInfo: NodeInfo override val myInfo: NodeInfo
get() = throw UnsupportedOperationException() get() = NodeInfo(listOf(MOCK_HOST_AND_PORT), DUMMY_IDENTITY_1, NonEmptySet.of(DUMMY_IDENTITY_1), 1) // Required to get a dummy platformVersion when required for tests.
override val monitoringService: MonitoringService = MonitoringService(MetricRegistry()) override val monitoringService: MonitoringService = MonitoringService(MetricRegistry())
override val rpcFlows: List<Class<out FlowLogic<*>>> override val rpcFlows: List<Class<out FlowLogic<*>>>
get() = throw UnsupportedOperationException() get() = throw UnsupportedOperationException()

View File

@ -4,10 +4,7 @@ import io.requery.Persistable
import io.requery.kotlin.eq import io.requery.kotlin.eq
import io.requery.sql.KotlinEntityDataStore import io.requery.sql.KotlinEntityDataStore
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.*
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.testing.NullPublicKey
import net.corda.core.crypto.toBase58String
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
@ -20,6 +17,7 @@ import net.corda.node.services.vault.schemas.requery.VaultSchema
import net.corda.node.services.vault.schemas.requery.VaultStatesEntity import net.corda.node.services.vault.schemas.requery.VaultStatesEntity
import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
import net.corda.testing.ALICE_PUBKEY
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.DUMMY_PUBKEY_1 import net.corda.testing.DUMMY_PUBKEY_1
import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.TestDependencyInjectionBase
@ -212,6 +210,6 @@ class RequeryConfigurationTest : TestDependencyInjectionBase() {
notary = DUMMY_NOTARY, notary = DUMMY_NOTARY,
timeWindow = null timeWindow = null
) )
return SignedTransaction(wtx, listOf(DigitalSignature.WithKey(NullPublicKey, ByteArray(1)))) return SignedTransaction(wtx, listOf(TransactionSignature(ByteArray(1), ALICE_PUBKEY, SignatureMetadata(1, Crypto.findSignatureScheme(ALICE_PUBKEY).schemeNumberID))))
} }
} }

View File

@ -19,7 +19,11 @@ import net.corda.node.services.persistence.DBCheckpointStorage
import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl
import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.statemachine.StateMachineManager
import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.NodeVaultService
import net.corda.node.utilities.* import net.corda.node.utilities.AffinityExecutor
import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.configureDatabase
import net.corda.testing.node.InMemoryMessagingNetwork
import net.corda.testing.node.MockKeyManagementService
import net.corda.testing.getTestX509Name import net.corda.testing.getTestX509Name
import net.corda.testing.testNodeConfiguration import net.corda.testing.testNodeConfiguration
import net.corda.testing.initialiseTestSerialization import net.corda.testing.initialiseTestSerialization
@ -35,7 +39,6 @@ import java.nio.file.Paths
import java.security.PublicKey import java.security.PublicKey
import java.time.Clock import java.time.Clock
import java.time.Instant import java.time.Instant
import java.util.*
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit

View File

@ -1,15 +1,17 @@
package net.corda.node.services.persistence package net.corda.node.services.persistence
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.testing.NullPublicKey import net.corda.core.crypto.SignatureMetadata
import net.corda.core.crypto.TransactionSignature
import net.corda.core.toFuture import net.corda.core.toFuture
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.node.services.transactions.PersistentUniquenessProvider import net.corda.node.services.transactions.PersistentUniquenessProvider
import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
import net.corda.testing.ALICE_PUBKEY
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.LogHelper import net.corda.testing.LogHelper
import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.TestDependencyInjectionBase
@ -150,6 +152,6 @@ class DBTransactionStorageTests : TestDependencyInjectionBase() {
notary = DUMMY_NOTARY, notary = DUMMY_NOTARY,
timeWindow = null timeWindow = null
) )
return SignedTransaction(wtx, listOf(DigitalSignature.WithKey(NullPublicKey, ByteArray(1)))) return SignedTransaction(wtx, listOf(TransactionSignature(ByteArray(1), ALICE_PUBKEY, SignatureMetadata(1, Crypto.findSignatureScheme(ALICE_PUBKEY).schemeNumberID))))
} }
} }

View File

@ -3,15 +3,15 @@ package net.corda.node.services.transactions
import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.CordaFuture
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.TransactionSignature
import net.corda.core.flows.NotaryError import net.corda.core.flows.NotaryError
import net.corda.core.flows.NotaryException import net.corda.core.flows.NotaryException
import net.corda.core.flows.NotaryFlow import net.corda.core.flows.NotaryFlow
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.core.utilities.seconds
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.getOrThrow
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.seconds
import net.corda.node.internal.AbstractNode import net.corda.node.internal.AbstractNode
import net.corda.node.services.network.NetworkMapService import net.corda.node.services.network.NetworkMapService
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
@ -132,7 +132,7 @@ class NotaryServiceTests {
notaryError.conflict.verified() notaryError.conflict.verified()
} }
private fun runNotaryClient(stx: SignedTransaction): CordaFuture<List<DigitalSignature.WithKey>> { private fun runNotaryClient(stx: SignedTransaction): CordaFuture<List<TransactionSignature>> {
val flow = NotaryFlow.Client(stx) val flow = NotaryFlow.Client(stx)
val future = clientNode.services.startFlow(flow).resultFuture val future = clientNode.services.startFlow(flow).resultFuture
mockNet.runNetwork() mockNet.runNetwork()

View File

@ -4,7 +4,7 @@ import net.corda.core.concurrent.CordaFuture
import net.corda.core.contracts.Command import net.corda.core.contracts.Command
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.TransactionSignature
import net.corda.core.flows.NotaryError import net.corda.core.flows.NotaryError
import net.corda.core.flows.NotaryException import net.corda.core.flows.NotaryException
import net.corda.core.flows.NotaryFlow import net.corda.core.flows.NotaryFlow
@ -85,7 +85,7 @@ class ValidatingNotaryServiceTests {
assertEquals(setOf(expectedMissingKey), missingKeys) assertEquals(setOf(expectedMissingKey), missingKeys)
} }
private fun runClient(stx: SignedTransaction): CordaFuture<List<DigitalSignature.WithKey>> { private fun runClient(stx: SignedTransaction): CordaFuture<List<TransactionSignature>> {
val flow = NotaryFlow.Client(stx) val flow = NotaryFlow.Client(stx)
val future = clientNode.services.startFlow(flow).resultFuture val future = clientNode.services.startFlow(flow).resultFuture
mockNet.runNetwork() mockNet.runNetwork()

View File

@ -5,7 +5,6 @@ import net.corda.contracts.asset.DUMMY_CASH_ISSUER
import net.corda.contracts.getCashBalance import net.corda.contracts.getCashBalance
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.sign
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.node.services.StatesNotAvailableException import net.corda.core.node.services.StatesNotAvailableException
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
@ -509,14 +508,14 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
val amount = Amount(1000, Issued(BOC.ref(1), GBP)) val amount = Amount(1000, Issued(BOC.ref(1), GBP))
// Issue some cash // Issue some cash
val issueTx = TransactionBuilder(notary).apply { val issueTxBuilder = TransactionBuilder(notary).apply {
Cash().generateIssue(this, amount, anonymousIdentity.party, notary) Cash().generateIssue(this, amount, anonymousIdentity.party, notary)
}.toWireTransaction() signWith(BOC_KEY)
}
// We need to record the issue transaction so inputs can be resolved for the notary change transaction // We need to record the issue transaction so inputs can be resolved for the notary change transaction
val signedIssueTx = SignedTransaction(issueTx, listOf(BOC_KEY.sign(issueTx.id))) services.validatedTransactions.addTransaction(issueTxBuilder.toSignedTransaction())
services.validatedTransactions.addTransaction(signedIssueTx)
val issueTx = issueTxBuilder.toWireTransaction()
val initialCashState = StateAndRef(issueTx.outputs.single(), StateRef(issueTx.id, 0)) val initialCashState = StateAndRef(issueTx.outputs.single(), StateRef(issueTx.id, 0))
// Change notary // Change notary

View File

@ -9,9 +9,7 @@ import net.corda.contracts.math.CubicSplineInterpolator
import net.corda.contracts.math.Interpolator import net.corda.contracts.math.Interpolator
import net.corda.contracts.math.InterpolatorFactory import net.corda.contracts.math.InterpolatorFactory
import net.corda.core.contracts.Command import net.corda.core.contracts.Command
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.*
import net.corda.core.crypto.MerkleTreeException
import net.corda.core.crypto.keys
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatedBy import net.corda.core.flows.InitiatedBy
@ -146,7 +144,7 @@ object NodeInterestRates {
// Oracle gets signing request for only some of them with a valid partial tree? We sign over a whole transaction. // Oracle gets signing request for only some of them with a valid partial tree? We sign over a whole transaction.
// It will be fixed by adding partial signatures later. // It will be fixed by adding partial signatures later.
// DOCSTART 1 // DOCSTART 1
fun sign(ftx: FilteredTransaction): DigitalSignature.WithKey { fun sign(ftx: FilteredTransaction): TransactionSignature {
if (!ftx.verify()) { if (!ftx.verify()) {
throw MerkleTreeException("Rate Fix Oracle: Couldn't verify partial Merkle tree.") throw MerkleTreeException("Rate Fix Oracle: Couldn't verify partial Merkle tree.")
} }
@ -177,8 +175,9 @@ object NodeInterestRates {
// Note that we will happily sign an invalid transaction, as we are only being presented with a filtered // Note that we will happily sign an invalid transaction, as we are only being presented with a filtered
// version so we can't resolve or check it ourselves. However, that doesn't matter much, as if we sign // version so we can't resolve or check it ourselves. However, that doesn't matter much, as if we sign
// an invalid transaction the signature is worthless. // an invalid transaction the signature is worthless.
val signature = services.keyManagementService.sign(ftx.rootHash.bytes, signingKey) val signableData = SignableData(ftx.rootHash, SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(signingKey).schemeNumberID))
return DigitalSignature.WithKey(signingKey, signature.bytes) val signature = services.keyManagementService.sign(signableData, signingKey)
return TransactionSignature(signature.bytes, signingKey, signableData.signatureMetadata)
} }
// DOCEND 1 // DOCEND 1

View File

@ -3,7 +3,7 @@ package net.corda.irs.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.contracts.Fix import net.corda.contracts.Fix
import net.corda.contracts.FixOf import net.corda.contracts.FixOf
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.isFulfilledBy import net.corda.core.crypto.isFulfilledBy
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
@ -112,10 +112,10 @@ open class RatesFixFlow(protected val tx: TransactionBuilder,
@InitiatingFlow @InitiatingFlow
class FixSignFlow(val tx: TransactionBuilder, val oracle: Party, class FixSignFlow(val tx: TransactionBuilder, val oracle: Party,
val partialMerkleTx: FilteredTransaction) : FlowLogic<DigitalSignature.WithKey>() { val partialMerkleTx: FilteredTransaction) : FlowLogic<TransactionSignature>() {
@Suspendable @Suspendable
override fun call(): DigitalSignature.WithKey { override fun call(): TransactionSignature {
val resp = sendAndReceive<DigitalSignature.WithKey>(oracle, SignRequest(partialMerkleTx)) val resp = sendAndReceive<TransactionSignature>(oracle, SignRequest(partialMerkleTx))
return resp.unwrap { sig -> return resp.unwrap { sig ->
check(oracle.owningKey.isFulfilledBy(listOf(sig.by))) check(oracle.owningKey.isFulfilledBy(listOf(sig.by)))
tx.toWireTransaction().checkSignature(sig) tx.toWireTransaction().checkSignature(sig)

View File

@ -77,6 +77,9 @@ val DUMMY_CA: CertificateAndKeyPair by lazy {
fun dummyCommand(vararg signers: PublicKey) = Command<TypeOnlyCommandData>(object : TypeOnlyCommandData() {}, signers.toList()) fun dummyCommand(vararg signers: PublicKey) = Command<TypeOnlyCommandData>(object : TypeOnlyCommandData() {}, signers.toList())
val DUMMY_IDENTITY_1: PartyAndCertificate get() = getTestPartyAndCertificate(DUMMY_PARTY)
val DUMMY_PARTY: Party get() = Party(X500Name("CN=Dummy,O=Dummy,L=Madrid,C=ES"), DUMMY_KEY_1.public)
// //
// Extensions to the Driver DSL to auto-manufacture nodes by name. // Extensions to the Driver DSL to auto-manufacture nodes by name.
// //

View File

@ -1,12 +1,9 @@
package net.corda.testing package net.corda.testing
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.*
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.composite.expandedCompositeKeys import net.corda.core.crypto.composite.expandedCompositeKeys
import net.corda.core.crypto.sign import net.corda.core.crypto.testing.NULL_SIGNATURE
import net.corda.core.crypto.testing.NullSignature
import net.corda.core.crypto.toStringShort
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -289,7 +286,7 @@ data class TestLedgerDSLInterpreter private constructor(
override fun verifies(): EnforceVerifyOrFail { override fun verifies(): EnforceVerifyOrFail {
try { try {
val usedInputs = mutableSetOf<StateRef>() val usedInputs = mutableSetOf<StateRef>()
services.recordTransactions(transactionsUnverified.map { SignedTransaction(it, listOf(NullSignature)) }) services.recordTransactions(transactionsUnverified.map { SignedTransaction(it, listOf(NULL_SIGNATURE)) })
for ((_, value) in transactionWithLocations) { for ((_, value) in transactionWithLocations) {
val wtx = value.transaction val wtx = value.transaction
val ltx = wtx.toLedgerTransaction(services) val ltx = wtx.toLedgerTransaction(services)
@ -301,7 +298,7 @@ data class TestLedgerDSLInterpreter private constructor(
throw DoubleSpentInputs(txIds) throw DoubleSpentInputs(txIds)
} }
usedInputs.addAll(wtx.inputs) usedInputs.addAll(wtx.inputs)
services.recordTransactions(SignedTransaction(wtx, listOf(NullSignature))) services.recordTransactions(SignedTransaction(wtx, listOf(NULL_SIGNATURE)))
} }
return EnforceVerifyOrFail.Token return EnforceVerifyOrFail.Token
} catch (exception: TransactionVerificationException) { } catch (exception: TransactionVerificationException) {
@ -335,7 +332,7 @@ data class TestLedgerDSLInterpreter private constructor(
*/ */
fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: List<KeyPair>) = transactionsToSign.map { wtx -> fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: List<KeyPair>) = transactionsToSign.map { wtx ->
check(wtx.requiredSigningKeys.isNotEmpty()) check(wtx.requiredSigningKeys.isNotEmpty())
val signatures = ArrayList<DigitalSignature.WithKey>() val signatures = ArrayList<TransactionSignature>()
val keyLookup = HashMap<PublicKey, KeyPair>() val keyLookup = HashMap<PublicKey, KeyPair>()
(ALL_TEST_KEYS + extraKeys).forEach { (ALL_TEST_KEYS + extraKeys).forEach {
@ -343,7 +340,7 @@ fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: List<KeyPair>)
} }
wtx.requiredSigningKeys.expandedCompositeKeys.forEach { wtx.requiredSigningKeys.expandedCompositeKeys.forEach {
val key = keyLookup[it] ?: throw IllegalArgumentException("Missing required key for ${it.toStringShort()}") val key = keyLookup[it] ?: throw IllegalArgumentException("Missing required key for ${it.toStringShort()}")
signatures += key.sign(wtx.id) signatures += key.sign(SignableData(wtx.id, SignatureMetadata(1, Crypto.findSignatureScheme(it).schemeNumberID)))
} }
SignedTransaction(wtx, signatures) SignedTransaction(wtx, signatures)
} }

View File

@ -124,8 +124,12 @@ class MockKeyManagementService(val identityService: IdentityService,
override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey { override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
val keyPair = getSigningKeyPair(publicKey) val keyPair = getSigningKeyPair(publicKey)
val signature = keyPair.sign(bytes) return keyPair.sign(bytes)
return signature }
override fun sign(signableData: SignableData, publicKey: PublicKey): TransactionSignature {
val keyPair = getSigningKeyPair(publicKey)
return keyPair.sign(signableData)
} }
} }