mirror of
https://github.com/corda/corda.git
synced 2024-12-19 21:17:58 +00:00
TransactionSignature MetaData support (#1040)
Support signature metadata
This commit is contained in:
parent
3a3ead2dfe
commit
bd0944e799
@ -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
|
||||
* 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
|
||||
* 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.
|
||||
|
@ -4,6 +4,7 @@ import net.corda.core.crypto.composite.CompositeKey
|
||||
import net.corda.core.crypto.composite.CompositeSignature
|
||||
import net.corda.core.crypto.provider.CordaObjectIdentifier
|
||||
import net.corda.core.crypto.provider.CordaSecurityProvider
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.i2p.crypto.eddsa.EdDSAEngine
|
||||
import net.i2p.crypto.eddsa.EdDSAPrivateKey
|
||||
import net.i2p.crypto.eddsa.EdDSAPublicKey
|
||||
@ -401,23 +402,23 @@ object Crypto {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic way to sign [MetaData] 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.
|
||||
* Generic way to sign [SignableData] objects with a [PrivateKey].
|
||||
* [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 metaData a [MetaData] object that adds extra information to a transaction.
|
||||
* @return a [TransactionSignature] object than contains the output of a successful signing and the metaData.
|
||||
* @throws IllegalArgumentException if the signature scheme is not supported for this private key or
|
||||
* if metaData.schemeCodeName is not aligned with key type.
|
||||
* @param signableData a [SignableData] object that adds extra information to a transaction.
|
||||
* @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.
|
||||
* @throws InvalidKeyException if the private key is invalid.
|
||||
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
||||
*/
|
||||
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
|
||||
fun doSign(privateKey: PrivateKey, metaData: MetaData): TransactionSignature {
|
||||
val sigKey: SignatureScheme = findSignatureScheme(privateKey)
|
||||
val sigMetaData: SignatureScheme = findSignatureScheme(metaData.schemeCodeName)
|
||||
if (sigKey != sigMetaData) throw IllegalArgumentException("Metadata schemeCodeName: ${metaData.schemeCodeName} is not aligned with the key type.")
|
||||
val signatureData = doSign(sigKey.schemeCodeName, privateKey, metaData.bytes())
|
||||
return TransactionSignature(signatureData, metaData)
|
||||
fun doSign(keyPair: KeyPair, signableData: SignableData): TransactionSignature {
|
||||
val sigKey: SignatureScheme = findSignatureScheme(keyPair.private)
|
||||
val sigMetaData: SignatureScheme = findSignatureScheme(keyPair.public)
|
||||
if (sigKey != sigMetaData) throw IllegalArgumentException("Metadata schemeCodeName: ${sigMetaData.schemeCodeName}" +
|
||||
" is not aligned with the key type: ${sigKey.schemeCodeName}.")
|
||||
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.
|
||||
* @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)
|
||||
|
||||
/**
|
||||
@ -485,9 +486,9 @@ object Crypto {
|
||||
/**
|
||||
* Utility to simplify the act of verifying a [TransactionSignature].
|
||||
* It returns true if it succeeds, but it always throws an exception if verification fails.
|
||||
* @param publicKey the signer's [PublicKey].
|
||||
* @param transactionSignature the signatureData on a message.
|
||||
* @return true if verification passes or throws an exception if verification fails.
|
||||
* @param txId transaction's id (Merkle root).
|
||||
* @param transactionSignature the signature on the transaction.
|
||||
* @return true if verification passes or throw exception if verification fails.
|
||||
* @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,
|
||||
@ -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(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
|
||||
fun doVerify(publicKey: PublicKey, transactionSignature: TransactionSignature): Boolean {
|
||||
if (publicKey != transactionSignature.metaData.publicKey) IllegalArgumentException("MetaData's publicKey: ${transactionSignature.metaData.publicKey.toStringShort()} does not match")
|
||||
return Crypto.doVerify(publicKey, transactionSignature.signatureData, transactionSignature.metaData.bytes())
|
||||
fun doVerify(txId: SecureHash, transactionSignature: TransactionSignature): Boolean {
|
||||
val signableData = SignableData(txId, transactionSignature.signatureMetadata)
|
||||
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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -34,7 +34,17 @@ fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignat
|
||||
*/
|
||||
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
|
||||
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.
|
||||
@ -88,7 +98,7 @@ fun PublicKey.containsAny(otherKeys: Iterable<PublicKey>): Boolean {
|
||||
}
|
||||
|
||||
/** 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
|
||||
operator fun KeyPair.component1(): PrivateKey = this.private
|
||||
@ -105,17 +115,6 @@ fun generateKeyPair(): KeyPair = Crypto.generateKeyPair()
|
||||
*/
|
||||
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.
|
||||
* @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)
|
||||
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.
|
||||
* @param signatureData the signature on a message.
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
14
core/src/main/kotlin/net/corda/core/crypto/SignableData.kt
Normal file
14
core/src/main/kotlin/net/corda/core/crypto/SignableData.kt
Normal 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)
|
||||
|
@ -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)
|
@ -6,7 +6,7 @@ import java.security.spec.AlgorithmParameterSpec
|
||||
|
||||
/**
|
||||
* 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 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
|
||||
|
@ -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
|
||||
}
|
@ -1,22 +1,57 @@
|
||||
package net.corda.core.crypto
|
||||
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.PublicKey
|
||||
import java.security.SignatureException
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* A wrapper around a digital signature accompanied with metadata, see [MetaData.Full] and [DigitalSignature].
|
||||
* The signature protocol works as follows: s = sign(MetaData.hashBytes).
|
||||
* A wrapper over the signature output accompanied by signer's public key and signature metadata.
|
||||
* 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.
|
||||
* Note that [MetaData] contains both public key and merkle root of the transaction.
|
||||
* Function to verify a [SignableData] object's signature.
|
||||
* 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 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 verify(): Boolean = Crypto.doVerify(metaData.publicKey, signatureData, metaData.bytes())
|
||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
package net.corda.core.crypto.composite
|
||||
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.serialization.deserialize
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier
|
||||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.security.*
|
||||
import java.security.spec.AlgorithmParameterSpec
|
||||
@ -77,7 +76,7 @@ class CompositeSignature : Signature(SIGNATURE_ALGORITHM) {
|
||||
fun engineVerify(sigBytes: ByteArray): Boolean {
|
||||
val sig = sigBytes.deserialize<CompositeSignaturesWithKeys>()
|
||||
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) }
|
||||
} else {
|
||||
false
|
||||
|
@ -1,6 +1,6 @@
|
||||
package net.corda.core.crypto.composite
|
||||
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.crypto.TransactionSignature
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
|
||||
/**
|
||||
@ -8,7 +8,7 @@ import net.corda.core.serialization.CordaSerializable
|
||||
* serialization format (i.e. not Kryo).
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class CompositeSignaturesWithKeys(val sigs: List<DigitalSignature.WithKey>) {
|
||||
data class CompositeSignaturesWithKeys(val sigs: List<TransactionSignature>) {
|
||||
companion object {
|
||||
val EMPTY = CompositeSignaturesWithKeys(emptyList())
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
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.serialization.CordaSerializable
|
||||
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. */
|
||||
@CordaSerializable
|
||||
object NullSignature : DigitalSignature.WithKey(NullPublicKey, ByteArray(32))
|
||||
val NULL_SIGNATURE = TransactionSignature(ByteArray(32), NullPublicKey, SignatureMetadata(1, -1))
|
@ -4,7 +4,7 @@ import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
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.identity.Party
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
@ -95,7 +95,7 @@ abstract class AbstractStateReplacementFlow {
|
||||
abstract protected fun assembleTx(): UpgradeTx
|
||||
|
||||
@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 participantNode = serviceHub.networkMapCache.getNodeByLegalIdentityKey(it) ?:
|
||||
throw IllegalStateException("Participant $it to state $originalState not found on the network")
|
||||
@ -113,10 +113,10 @@ abstract class AbstractStateReplacementFlow {
|
||||
}
|
||||
|
||||
@Suspendable
|
||||
private fun getParticipantSignature(party: Party, stx: SignedTransaction): DigitalSignature.WithKey {
|
||||
private fun getParticipantSignature(party: Party, stx: SignedTransaction): TransactionSignature {
|
||||
val proposal = Proposal(originalState.ref, modification)
|
||||
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" }
|
||||
it.verify(stx.id)
|
||||
it
|
||||
@ -124,7 +124,7 @@ abstract class AbstractStateReplacementFlow {
|
||||
}
|
||||
|
||||
@Suspendable
|
||||
private fun getNotarySignatures(stx: SignedTransaction): List<DigitalSignature.WithKey> {
|
||||
private fun getNotarySignatures(stx: SignedTransaction): List<TransactionSignature> {
|
||||
progressTracker.currentStep = NOTARY
|
||||
try {
|
||||
return subFlow(NotaryFlow.Client(stx))
|
||||
@ -165,7 +165,7 @@ abstract class AbstractStateReplacementFlow {
|
||||
progressTracker.currentStep = APPROVING
|
||||
|
||||
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.
|
||||
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}" }
|
||||
}
|
||||
|
||||
private fun sign(stx: SignedTransaction): DigitalSignature.WithKey {
|
||||
private fun sign(stx: SignedTransaction): TransactionSignature {
|
||||
return serviceHub.createSignature(stx)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.corda.core.flows
|
||||
|
||||
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.toBase58String
|
||||
import net.corda.core.identity.Party
|
||||
@ -124,10 +124,10 @@ class CollectSignaturesFlow(val partiallySignedTx: SignedTransaction,
|
||||
/**
|
||||
* 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.
|
||||
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." }
|
||||
it
|
||||
}
|
||||
|
@ -3,6 +3,9 @@ package net.corda.core.flows
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
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.transactions.NotaryChangeWireTransaction
|
||||
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()
|
||||
// TODO: We need a much faster way of finding our key in the transaction
|
||||
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))
|
||||
|
||||
return AbstractStateReplacementFlow.UpgradeTx(stx, participantKeys, myKey)
|
||||
|
@ -3,9 +3,9 @@ package net.corda.core.flows
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TimeWindow
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.crypto.TransactionSignature
|
||||
import net.corda.core.crypto.keys
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.FetchDataFlow
|
||||
@ -32,7 +32,7 @@ object NotaryFlow {
|
||||
*/
|
||||
@InitiatingFlow
|
||||
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())
|
||||
|
||||
companion object {
|
||||
@ -46,7 +46,7 @@ object NotaryFlow {
|
||||
|
||||
@Suspendable
|
||||
@Throws(NotaryException::class)
|
||||
override fun call(): List<DigitalSignature.WithKey> {
|
||||
override fun call(): List<TransactionSignature> {
|
||||
progressTracker.currentStep = REQUESTING
|
||||
|
||||
notaryParty = stx.notary ?: throw IllegalStateException("Transaction does not specify a Notary")
|
||||
@ -67,7 +67,7 @@ object NotaryFlow {
|
||||
val response = try {
|
||||
if (serviceHub.networkMapCache.isValidatingNotary(notaryParty)) {
|
||||
subFlow(SendTransactionWithRetry(notaryParty, stx))
|
||||
receive<List<DigitalSignature.WithKey>>(notaryParty)
|
||||
receive<List<TransactionSignature>>(notaryParty)
|
||||
} else {
|
||||
val tx: Any = if (stx.isNotaryChangeTransaction()) {
|
||||
stx.notaryChangeTx
|
||||
@ -84,14 +84,14 @@ object NotaryFlow {
|
||||
}
|
||||
|
||||
return response.unwrap { signatures ->
|
||||
signatures.forEach { validateSignature(it, stx.id.bytes) }
|
||||
signatures.forEach { validateSignature(it, stx.id) }
|
||||
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" }
|
||||
sig.verify(data)
|
||||
sig.verify(txId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,7 +124,7 @@ object NotaryFlow {
|
||||
|
||||
@Suspendable
|
||||
private fun signAndSendResponse(txId: SecureHash) {
|
||||
val signature = service.sign(txId.bytes)
|
||||
val signature = service.sign(txId)
|
||||
send(otherSide, listOf(signature))
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
package net.corda.core.node
|
||||
|
||||
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.serialization.SerializeAsToken
|
||||
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
|
||||
* 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.
|
||||
* 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.
|
||||
@ -132,9 +135,17 @@ interface ServiceHub : ServicesForResolution {
|
||||
*/
|
||||
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]
|
||||
* 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.
|
||||
* 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.
|
||||
@ -142,15 +153,12 @@ interface ServiceHub : ServicesForResolution {
|
||||
* to sign with.
|
||||
* @return Returns a SignedTransaction with the new node signature attached.
|
||||
*/
|
||||
fun signInitialTransaction(builder: TransactionBuilder, publicKey: PublicKey): SignedTransaction {
|
||||
val sig = keyManagementService.sign(builder.toWireTransaction().id.bytes, publicKey)
|
||||
builder.addSignatureUnchecked(sig)
|
||||
return builder.toSignedTransaction(false)
|
||||
}
|
||||
fun signInitialTransaction(builder: TransactionBuilder, publicKey: PublicKey) =
|
||||
signInitialTransaction(builder, publicKey, SignatureMetadata(myInfo.platformVersion, Crypto.findSignatureScheme(publicKey).schemeNumberID))
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Any existing signatures on the builder will be preserved.
|
||||
* @return Returns a SignedTransaction with the new node signature attached.
|
||||
@ -175,25 +183,30 @@ interface ServiceHub : ServicesForResolution {
|
||||
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].
|
||||
* @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.
|
||||
* If the [PublicKey] is actually a [net.corda.core.crypto.CompositeKey] the first leaf key found locally will be used
|
||||
* 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 {
|
||||
return keyManagementService.sign(signedTransaction.id.bytes, publicKey)
|
||||
}
|
||||
fun createSignature(signedTransaction: SignedTransaction, publicKey: PublicKey) =
|
||||
createSignature(signedTransaction, publicKey, SignatureMetadata(myInfo.platformVersion, Crypto.findSignatureScheme(publicKey).schemeNumberID))
|
||||
|
||||
/**
|
||||
* Helper method to create an additional signature for an existing (partially) SignedTransaction
|
||||
* using the default identity signing key of the node.
|
||||
* Helper method to create an additional signature for an existing (partially) [SignedTransaction]
|
||||
* 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.
|
||||
* @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)
|
||||
}
|
||||
|
||||
@ -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.
|
||||
* @param signedTransaction The [SignedTransaction] to which the signature will be added.
|
||||
* @return A new [SignedTransaction] with the addition of the new signature.
|
||||
|
@ -2,6 +2,8 @@ package net.corda.core.node.services
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
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.PartyAndCertificate
|
||||
import java.security.PublicKey
|
||||
@ -48,9 +50,18 @@ interface KeyManagementService {
|
||||
* 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].
|
||||
* TODO A full [KeyManagementService] implementation needs to record activity to the [AuditService] and to limit signing to
|
||||
* appropriately authorised contexts and initiating users.
|
||||
*/
|
||||
@Suspendable
|
||||
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
|
||||
}
|
||||
|
@ -2,9 +2,7 @@ package net.corda.core.node.services
|
||||
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TimeWindow
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.NotaryError
|
||||
import net.corda.core.flows.NotaryException
|
||||
@ -75,4 +73,9 @@ abstract class TrustedAuthorityNotaryService : NotaryService() {
|
||||
fun sign(bits: ByteArray): DigitalSignature.WithKey {
|
||||
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)
|
||||
}
|
||||
}
|
@ -11,7 +11,6 @@ import de.javakaffee.kryoserializers.ArraysAsListSerializer
|
||||
import de.javakaffee.kryoserializers.BitSetSerializer
|
||||
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer
|
||||
import de.javakaffee.kryoserializers.guava.*
|
||||
import net.corda.core.crypto.MetaData
|
||||
import net.corda.core.crypto.composite.CompositeKey
|
||||
import net.corda.core.node.CordaPluginRegistry
|
||||
import net.corda.core.transactions.NotaryChangeWireTransaction
|
||||
@ -98,7 +97,6 @@ object DefaultKryoCustomizer {
|
||||
|
||||
addDefaultSerializer(SerializeAsToken::class.java, SerializeAsTokenSerializer<SerializeAsToken>())
|
||||
|
||||
register(MetaData::class.java, MetaDataSerializer)
|
||||
register(BitSet::class.java, BitSetSerializer())
|
||||
register(Class::class.java, ClassSerializer)
|
||||
|
||||
|
@ -5,7 +5,9 @@ import com.esotericsoftware.kryo.io.Input
|
||||
import com.esotericsoftware.kryo.io.Output
|
||||
import com.esotericsoftware.kryo.util.MapReferenceResolver
|
||||
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.identity.Party
|
||||
import net.corda.core.internal.VisibleForTesting
|
||||
@ -30,8 +32,6 @@ import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
import java.security.cert.CertPath
|
||||
import java.security.cert.CertificateFactory
|
||||
import java.security.spec.InvalidKeySpecException
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import javax.annotation.concurrent.ThreadSafe
|
||||
import kotlin.reflect.KClass
|
||||
@ -306,7 +306,7 @@ object SignedTransactionSerializer : Serializer<SignedTransaction>() {
|
||||
override fun read(kryo: Kryo, input: Input, type: Class<SignedTransaction>): SignedTransaction {
|
||||
return SignedTransaction(
|
||||
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. */
|
||||
@ThreadSafe
|
||||
object LoggerSerializer : Serializer<Logger>() {
|
||||
|
@ -10,7 +10,7 @@ import java.nio.ByteBuffer
|
||||
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)).
|
||||
*/
|
||||
fun <T : Any> serializedHash(x: T, privacySalt: PrivacySalt?, index: Int): SecureHash {
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.corda.core.transactions
|
||||
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.TransactionSignature
|
||||
import net.corda.core.crypto.toBase58String
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.ServiceHub
|
||||
@ -36,7 +36,7 @@ data class NotaryChangeWireTransaction(
|
||||
*/
|
||||
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 ->
|
||||
services.loadState(ref).let { StateAndRef(it, ref) }
|
||||
}
|
||||
@ -54,7 +54,7 @@ data class NotaryChangeLedgerTransaction(
|
||||
override val notary: Party,
|
||||
val newNotary: Party,
|
||||
override val id: SecureHash,
|
||||
override val sigs: List<DigitalSignature.WithKey>
|
||||
override val sigs: List<TransactionSignature>
|
||||
) : FullTransaction(), TransactionWithSignatures {
|
||||
init {
|
||||
checkEncumbrances()
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.corda.core.transactions
|
||||
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.TransactionSignature
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
@ -29,10 +29,10 @@ import java.util.*
|
||||
*/
|
||||
// DOCSTART 1
|
||||
data class SignedTransaction(val txBits: SerializedBytes<CoreTransaction>,
|
||||
override val sigs: List<DigitalSignature.WithKey>
|
||||
override val sigs: List<TransactionSignature>
|
||||
) : TransactionWithSignatures {
|
||||
// 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
|
||||
}
|
||||
|
||||
@ -75,16 +75,16 @@ data class SignedTransaction(val txBits: SerializedBytes<CoreTransaction>,
|
||||
}
|
||||
|
||||
/** 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. */
|
||||
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]
|
||||
* 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
|
||||
return copy(sigs = sigs + sigList).apply {
|
||||
cachedTransaction = cached
|
||||
@ -92,10 +92,10 @@ data class SignedTransaction(val txBits: SerializedBytes<CoreTransaction>,
|
||||
}
|
||||
|
||||
/** 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. */
|
||||
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
|
||||
|
@ -151,18 +151,18 @@ open class TransactionBuilder(
|
||||
|
||||
/** The signatures that have been collected so far - might be incomplete! */
|
||||
@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.")
|
||||
fun signWith(key: KeyPair): TransactionBuilder {
|
||||
val data = toWireTransaction().id
|
||||
addSignatureUnchecked(key.sign(data.bytes))
|
||||
val signableData = SignableData(toWireTransaction().id, SignatureMetadata(1, Crypto.findSignatureScheme(key.public).schemeNumberID)) // A dummy platformVersion.
|
||||
addSignatureUnchecked(key.sign(signableData))
|
||||
return this
|
||||
}
|
||||
|
||||
/** Adds the signature directly to the transaction, without checking it for validity. */
|
||||
@Deprecated("Use ServiceHub.signInitialTransaction() instead.")
|
||||
fun addSignatureUnchecked(sig: DigitalSignature.WithKey): TransactionBuilder {
|
||||
fun addSignatureUnchecked(sig: TransactionSignature): TransactionBuilder {
|
||||
currentSigs.add(sig)
|
||||
return this
|
||||
}
|
||||
@ -188,7 +188,7 @@ open class TransactionBuilder(
|
||||
* @throws IllegalArgumentException if the signature key doesn't appear in any command.
|
||||
*/
|
||||
@Deprecated("Use WireTransaction.checkSignature() instead.")
|
||||
fun checkAndAddSignature(sig: DigitalSignature.WithKey) {
|
||||
fun checkAndAddSignature(sig: TransactionSignature) {
|
||||
checkSignature(sig)
|
||||
addSignatureUnchecked(sig)
|
||||
}
|
||||
@ -200,7 +200,7 @@ open class TransactionBuilder(
|
||||
* @throws IllegalArgumentException if the signature key doesn't appear in any command.
|
||||
*/
|
||||
@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" }
|
||||
sig.verify(toWireTransaction().id)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.corda.core.transactions
|
||||
|
||||
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.transactions.SignedTransaction.SignaturesMissingException
|
||||
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 */
|
||||
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 */
|
||||
val requiredSigningKeys: Set<PublicKey>
|
||||
@ -57,7 +57,7 @@ interface TransactionWithSignatures : NamedByHash {
|
||||
@Throws(SignatureException::class)
|
||||
fun checkSignaturesAreValid() {
|
||||
for (sig in sigs) {
|
||||
sig.verify(id.bytes)
|
||||
sig.verify(id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
package net.corda.core.transactions
|
||||
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.crypto.MerkleTree
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.TransactionSignature
|
||||
import net.corda.core.crypto.keys
|
||||
import net.corda.core.identity.Party
|
||||
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 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" }
|
||||
sig.verify(id)
|
||||
}
|
||||
|
@ -1,10 +1,8 @@
|
||||
package net.corda.core.contracts
|
||||
|
||||
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.generateKeyPair
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
@ -19,8 +17,12 @@ import kotlin.test.assertNotEquals
|
||||
|
||||
class TransactionTests : TestDependencyInjectionBase() {
|
||||
private fun makeSigned(wtx: WireTransaction, vararg keys: KeyPair, notarySig: Boolean = true): SignedTransaction {
|
||||
val keySigs = keys.map { it.sign(wtx.id.bytes) }
|
||||
val sigs = if (notarySig) keySigs + DUMMY_NOTARY_KEY.sign(wtx.id.bytes) else keySigs
|
||||
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(SignableData(wtx.id, SignatureMetadata(1, Crypto.findSignatureScheme(DUMMY_NOTARY_KEY.public).schemeNumberID)))
|
||||
} else {
|
||||
keySigs
|
||||
}
|
||||
return SignedTransaction(wtx, sigs)
|
||||
}
|
||||
|
||||
|
@ -36,13 +36,16 @@ class CompositeKeyTests : TestDependencyInjectionBase() {
|
||||
val charliePublicKey: PublicKey = charlieKey.public
|
||||
|
||||
val message = OpaqueBytes("Transaction".toByteArray())
|
||||
val secureHash = message.sha256()
|
||||
|
||||
val aliceSignature = aliceKey.sign(message)
|
||||
val bobSignature = bobKey.sign(message)
|
||||
val charlieSignature = charlieKey.sign(message)
|
||||
// By lazy is required so that the serialisers are configured before vals initialisation takes place (they internally invoke serialise).
|
||||
val aliceSignature by lazy { aliceKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(alicePublicKey).schemeNumberID))) }
|
||||
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
|
||||
fun `(Alice) fulfilled by Alice signature`() {
|
||||
println(aliceKey.serialize().hash)
|
||||
assertTrue { alicePublicKey.isFulfilledBy(aliceSignature.by) }
|
||||
assertFalse { alicePublicKey.isFulfilledBy(charlieSignature.by) }
|
||||
}
|
||||
@ -153,11 +156,12 @@ class CompositeKeyTests : TestDependencyInjectionBase() {
|
||||
* Check that verifying a composite signature using the [CompositeSignature] engine works.
|
||||
*/
|
||||
@Test
|
||||
fun `composite signature verification`() {
|
||||
fun `composite TransactionSignature verification `() {
|
||||
val twoOfThree = CompositeKey.Builder().addKeys(alicePublicKey, bobPublicKey, charliePublicKey).build(threshold = 2)
|
||||
|
||||
val engine = CompositeSignature()
|
||||
engine.initVerify(twoOfThree)
|
||||
engine.update(message.bytes)
|
||||
engine.update(secureHash.bytes)
|
||||
|
||||
assertFalse { engine.verify(CompositeSignaturesWithKeys(listOf(aliceSignature)).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) }
|
||||
|
||||
// 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) }
|
||||
}
|
||||
|
||||
@ -282,19 +286,19 @@ class CompositeKeyTests : TestDependencyInjectionBase() {
|
||||
|
||||
@Test
|
||||
fun `CompositeKey from multiple signature schemes and signature verification`() {
|
||||
val (privRSA, pubRSA) = Crypto.generateKeyPair(Crypto.RSA_SHA256)
|
||||
val (privK1, pubK1) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
||||
val (privR1, pubR1) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||
val (privEd, pubEd) = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
|
||||
val (privSP, pubSP) = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
|
||||
val keyPairRSA = Crypto.generateKeyPair(Crypto.RSA_SHA256)
|
||||
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
||||
val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||
val keyPairEd = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
|
||||
val keyPairSP = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
|
||||
|
||||
val RSASignature = privRSA.sign(message.bytes, pubRSA)
|
||||
val K1Signature = privK1.sign(message.bytes, pubK1)
|
||||
val R1Signature = privR1.sign(message.bytes, pubR1)
|
||||
val EdSignature = privEd.sign(message.bytes, pubEd)
|
||||
val SPSignature = privSP.sign(message.bytes, pubSP)
|
||||
val RSASignature = keyPairRSA.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairRSA.public).schemeNumberID)))
|
||||
val K1Signature = keyPairK1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairK1.public).schemeNumberID)))
|
||||
val R1Signature = keyPairR1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairR1.public).schemeNumberID)))
|
||||
val EdSignature = keyPairEd.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairEd.public).schemeNumberID)))
|
||||
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)
|
||||
assertTrue { compositeKey.isFulfilledBy(signatures.byKeys()) }
|
||||
@ -307,19 +311,19 @@ class CompositeKeyTests : TestDependencyInjectionBase() {
|
||||
@Test
|
||||
fun `Test save to keystore`() {
|
||||
// From test case [CompositeKey from multiple signature schemes and signature verification]
|
||||
val (privRSA, pubRSA) = Crypto.generateKeyPair(Crypto.RSA_SHA256)
|
||||
val (privK1, pubK1) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
||||
val (privR1, pubR1) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||
val (privEd, pubEd) = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
|
||||
val (privSP, pubSP) = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
|
||||
val keyPairRSA = Crypto.generateKeyPair(Crypto.RSA_SHA256)
|
||||
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
||||
val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||
val keyPairEd = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
|
||||
val keyPairSP = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
|
||||
|
||||
val RSASignature = privRSA.sign(message.bytes, pubRSA)
|
||||
val K1Signature = privK1.sign(message.bytes, pubK1)
|
||||
val R1Signature = privR1.sign(message.bytes, pubR1)
|
||||
val EdSignature = privEd.sign(message.bytes, pubEd)
|
||||
val SPSignature = privSP.sign(message.bytes, pubSP)
|
||||
val RSASignature = keyPairRSA.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairRSA.public).schemeNumberID)))
|
||||
val K1Signature = keyPairK1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairK1.public).schemeNumberID)))
|
||||
val R1Signature = keyPairR1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairR1.public).schemeNumberID)))
|
||||
val EdSignature = keyPairEd.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairEd.public).schemeNumberID)))
|
||||
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)
|
||||
assertTrue { compositeKey.isFulfilledBy(signatures.byKeys()) }
|
||||
|
@ -3,73 +3,39 @@ package net.corda.core.crypto
|
||||
import net.corda.testing.TestDependencyInjectionBase
|
||||
import org.junit.Test
|
||||
import java.security.SignatureException
|
||||
import java.time.Instant
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
/**
|
||||
* Digital signature MetaData tests
|
||||
* Digital signature MetaData tests.
|
||||
*/
|
||||
class TransactionSignatureTest : TestDependencyInjectionBase() {
|
||||
|
||||
val testBytes = "12345678901234567890123456789012".toByteArray()
|
||||
|
||||
/** valid sign and verify. */
|
||||
/** Valid sign and verify. */
|
||||
@Test
|
||||
fun `MetaData Full sign and verify`() {
|
||||
fun `Signature metadata full sign and verify`() {
|
||||
val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
|
||||
|
||||
// create a MetaData.Full object
|
||||
val meta = MetaData("ECDSA_SECP256K1_SHA256", "M9", SignatureType.FULL, Instant.now(), null, null, testBytes, keyPair.public)
|
||||
// Create a SignableData object.
|
||||
val signableData = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(keyPair.public).schemeNumberID))
|
||||
|
||||
// sign the message
|
||||
val transactionSignature: TransactionSignature = keyPair.private.sign(meta)
|
||||
// Sign the meta object.
|
||||
val transactionSignature: TransactionSignature = keyPair.sign(signableData)
|
||||
|
||||
// check auto-verification
|
||||
assertTrue(transactionSignature.verify())
|
||||
// Check auto-verification.
|
||||
assertTrue(transactionSignature.verify(testBytes.sha256()))
|
||||
|
||||
// check manual verification
|
||||
assertTrue(keyPair.public.verify(transactionSignature))
|
||||
// Check manual verification.
|
||||
assertTrue(Crypto.doVerify(testBytes.sha256(), transactionSignature))
|
||||
}
|
||||
|
||||
/** Signing should fail, as I sign with a secpK1 key, but set schemeCodeName is set to secpR1. */
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun `MetaData Full failure wrong scheme`() {
|
||||
/** Verification should fail; corrupted metadata - clearData (Merkle root) has changed. */
|
||||
@Test(expected = SignatureException::class)
|
||||
fun `Signature metadata full failure clearData has changed`() {
|
||||
val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
|
||||
val meta = MetaData("ECDSA_SECP256R1_SHA256", "M9", SignatureType.FULL, Instant.now(), null, null, testBytes, keyPair.public)
|
||||
keyPair.private.sign(meta)
|
||||
}
|
||||
|
||||
/** 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)
|
||||
val signableData = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(keyPair.public).schemeNumberID))
|
||||
val transactionSignature = keyPair.sign(signableData)
|
||||
Crypto.doVerify((testBytes + testBytes).sha256(), transactionSignature)
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import net.corda.nodeapi.serialization.KryoHeaderV0_1
|
||||
import net.corda.nodeapi.serialization.SerializationContextImpl
|
||||
import net.corda.nodeapi.serialization.SerializationFactoryImpl
|
||||
import net.corda.testing.ALICE
|
||||
import net.corda.testing.ALICE_PUBKEY
|
||||
import net.corda.testing.BOB
|
||||
import net.corda.testing.BOB_PUBKEY
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
@ -117,16 +118,13 @@ class KryoTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `serialize - deserialize MetaData`() {
|
||||
fun `serialize - deserialize SignableData`() {
|
||||
val testString = "Hello World"
|
||||
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 meta2 = serializedMetaData.deserialize<MetaData>(factory, context)
|
||||
val meta2 = serializedMetaData.deserialize<SignableData>(factory, context)
|
||||
assertEquals(meta2, meta)
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ Where:
|
||||
* ``outputs`` is a list of the transaction's outputs'
|
||||
* ``attachments`` is a list of the transaction's attachments'
|
||||
* ``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.
|
||||
* ``timeWindow`` is the transaction's timestamp and defines the acceptable delay for notarisation.
|
||||
|
||||
|
@ -5,8 +5,8 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import net.corda.contracts.asset.Cash;
|
||||
import net.corda.core.contracts.*;
|
||||
import net.corda.core.crypto.DigitalSignature;
|
||||
import net.corda.core.crypto.SecureHash;
|
||||
import net.corda.core.crypto.TransactionSignature;
|
||||
import net.corda.core.flows.*;
|
||||
import net.corda.core.identity.Party;
|
||||
import net.corda.core.internal.FetchDataFlow;
|
||||
@ -30,10 +30,8 @@ import java.security.PublicKey;
|
||||
import java.security.SignatureException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static net.corda.core.contracts.ContractsDSL.requireThat;
|
||||
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
|
||||
// transaction.
|
||||
// DOCSTART 40
|
||||
DigitalSignature.WithKey sig = getServiceHub().createSignature(onceSignedTx);
|
||||
TransactionSignature sig = getServiceHub().createSignature(onceSignedTx);
|
||||
// DOCEND 40
|
||||
// And again, if we wanted to use a different public key:
|
||||
// DOCSTART 41
|
||||
DigitalSignature.WithKey sig2 = getServiceHub().createSignature(onceSignedTx, otherKey2);
|
||||
TransactionSignature sig2 = getServiceHub().createSignature(onceSignedTx, otherKey2);
|
||||
// DOCEND 41
|
||||
|
||||
/*----------------------------
|
||||
|
@ -5,8 +5,8 @@ package net.corda.docs
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.contracts.asset.Cash
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.TransactionSignature
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.Party
|
||||
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
|
||||
// transaction.
|
||||
// DOCSTART 40
|
||||
val sig: DigitalSignature.WithKey = serviceHub.createSignature(onceSignedTx)
|
||||
val sig: TransactionSignature = serviceHub.createSignature(onceSignedTx)
|
||||
// DOCEND 40
|
||||
// And again, if we wanted to use a different public key:
|
||||
// DOCSTART 41
|
||||
val sig2: DigitalSignature.WithKey = serviceHub.createSignature(onceSignedTx, otherKey2)
|
||||
val sig2: TransactionSignature = serviceHub.createSignature(onceSignedTx, otherKey2)
|
||||
// DOCEND 41
|
||||
|
||||
// In practice, however, the process of gathering every signature
|
||||
|
@ -5,8 +5,11 @@ import net.corda.contracts.asset.Cash
|
||||
import net.corda.core.contracts.Amount
|
||||
import net.corda.core.contracts.Issued
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
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.identity.Party
|
||||
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
|
||||
// Allow otherParty to access our data to resolve the transaction.
|
||||
subFlow(SendTransactionFlow(remoteRequestWithNotary.owner, signedTransaction))
|
||||
val allPartySignedTx = receive<DigitalSignature.WithKey>(remoteRequestWithNotary.owner).unwrap {
|
||||
val allPartySignedTx = receive<TransactionSignature>(remoteRequestWithNotary.owner).unwrap {
|
||||
val withNewSignature = signedTransaction + it
|
||||
// check all signatures are present except the notary
|
||||
withNewSignature.verifySignaturesExcept(withNewSignature.tx.notary!!.owningKey)
|
||||
|
@ -2,8 +2,8 @@ package net.corda.docs
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.TransactionSignature
|
||||
import net.corda.core.crypto.containsAny
|
||||
import net.corda.core.flows.FinalityFlow
|
||||
import net.corda.core.flows.FlowLogic
|
||||
@ -188,7 +188,7 @@ class SubmitCompletionFlow(val ref: StateRef, val verdict: WorkflowState) : Flow
|
||||
val selfSignedTx = serviceHub.signInitialTransaction(tx)
|
||||
//DOCEND 2
|
||||
// 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.
|
||||
val agreedTx = selfSignedTx + it
|
||||
// Receive back their signature and confirm that it is for an unmodified transaction
|
||||
|
@ -112,7 +112,7 @@ Here is an extract from the ``NodeInterestRates.Oracle`` class and supporting ty
|
||||
class Oracle {
|
||||
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,
|
||||
|
@ -8,7 +8,7 @@ import net.corda.contracts.asset.Cash
|
||||
import net.corda.core.contracts.Command
|
||||
import net.corda.core.contracts.CommandData
|
||||
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.testing.*
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
@ -82,7 +82,7 @@ class SignedTransactionGenerator : Generator<SignedTransaction>(SignedTransactio
|
||||
val wireTransaction = WiredTransactionGenerator().generate(random, status)
|
||||
return SignedTransaction(
|
||||
ctx = wireTransaction,
|
||||
sigs = listOf(NullSignature)
|
||||
sigs = listOf(NULL_SIGNATURE)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,9 @@
|
||||
package net.corda.node.services.keys
|
||||
|
||||
import net.corda.core.internal.ThreadBox
|
||||
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.crypto.*
|
||||
import net.corda.core.identity.AnonymousPartyAndPath
|
||||
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.KeyManagementService
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
@ -75,7 +72,13 @@ class E2ETestKeyManagementService(val identityService: IdentityService,
|
||||
|
||||
override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
|
||||
val keyPair = getSigningKeyPair(publicKey)
|
||||
val signature = keyPair.sign(bytes)
|
||||
return signature
|
||||
return keyPair.sign(bytes)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,9 @@
|
||||
package net.corda.node.services.keys
|
||||
|
||||
import net.corda.core.internal.ThreadBox
|
||||
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.crypto.*
|
||||
import net.corda.core.identity.AnonymousPartyAndPath
|
||||
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.KeyManagementService
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
@ -86,8 +83,13 @@ class PersistentKeyManagementService(val identityService: IdentityService,
|
||||
|
||||
override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
|
||||
val keyPair = getSigningKeyPair(publicKey)
|
||||
val signature = keyPair.sign(bytes)
|
||||
return signature
|
||||
return keyPair.sign(bytes)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,10 @@ package net.corda.node.services.transactions
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import com.google.common.util.concurrent.SettableFuture
|
||||
import net.corda.core.crypto.Crypto
|
||||
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.NotaryException
|
||||
import net.corda.core.identity.Party
|
||||
@ -109,7 +112,8 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, c
|
||||
commitInputStates(inputs, id, callerIdentity)
|
||||
|
||||
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)
|
||||
} catch (e: NotaryException) {
|
||||
log.debug { "Error processing transaction: ${e.error}" }
|
||||
|
@ -14,21 +14,18 @@ import bftsmart.tom.server.defaultservices.DefaultReplier
|
||||
import bftsmart.tom.util.Extractor
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TimeWindow
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.flows.NotaryError
|
||||
import net.corda.core.flows.NotaryException
|
||||
import net.corda.core.identity.Party
|
||||
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.UniquenessProvider
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.internal.toTypedArray
|
||||
import net.corda.core.transactions.FilteredTransaction
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.debug
|
||||
@ -176,7 +173,7 @@ object BFTSMaRt {
|
||||
abstract class Replica(config: BFTSMaRtConfig,
|
||||
replicaId: Int,
|
||||
tableName: String,
|
||||
private val services: ServiceHubInternal,
|
||||
protected val services: ServiceHubInternal,
|
||||
private val timeWindowChecker: TimeWindowChecker) : DefaultRecoverable() {
|
||||
companion object {
|
||||
private val log = loggerFor<Replica>()
|
||||
@ -253,6 +250,10 @@ object BFTSMaRt {
|
||||
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:
|
||||
// - Test snapshot functionality with different bft-smart cluster configurations.
|
||||
// - Add streaming to support large data sets.
|
||||
|
@ -8,8 +8,7 @@ import net.corda.contracts.asset.Cash
|
||||
import net.corda.contracts.asset.`issued by`
|
||||
import net.corda.contracts.asset.`owned by`
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.InitiatedBy
|
||||
import net.corda.core.flows.InitiatingFlow
|
||||
@ -604,11 +603,11 @@ class TwoPartyTradeFlowTests {
|
||||
|
||||
val signed = wtxToSign.map {
|
||||
val id = it.id
|
||||
val sigs = mutableListOf<DigitalSignature.WithKey>()
|
||||
sigs.add(node.services.keyManagementService.sign(id.bytes, node.services.legalIdentityKey))
|
||||
sigs.add(notaryNode.services.keyManagementService.sign(id.bytes, notaryNode.services.notaryIdentityKey))
|
||||
val sigs = mutableListOf<TransactionSignature>()
|
||||
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(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(notaryNode.services.notaryIdentityKey).schemeNumberID)), notaryNode.services.notaryIdentityKey))
|
||||
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)
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.services.*
|
||||
import net.corda.core.serialization.SerializeAsToken
|
||||
import net.corda.core.utilities.NonEmptySet
|
||||
import net.corda.node.internal.InitiatedFlowFactory
|
||||
import net.corda.node.serialization.NodeClock
|
||||
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.transactions.InMemoryTransactionVerifierService
|
||||
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.node.MockAttachmentStorage
|
||||
import net.corda.testing.node.MockNetworkMapCache
|
||||
@ -60,7 +63,7 @@ open class MockServiceHubInternal(
|
||||
override val clock: Clock
|
||||
get() = overrideClock ?: throw UnsupportedOperationException()
|
||||
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 rpcFlows: List<Class<out FlowLogic<*>>>
|
||||
get() = throw UnsupportedOperationException()
|
||||
|
@ -4,10 +4,7 @@ import io.requery.Persistable
|
||||
import io.requery.kotlin.eq
|
||||
import io.requery.sql.KotlinEntityDataStore
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.testing.NullPublicKey
|
||||
import net.corda.core.crypto.toBase58String
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.node.services.Vault
|
||||
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.utilities.CordaPersistence
|
||||
import net.corda.node.utilities.configureDatabase
|
||||
import net.corda.testing.ALICE_PUBKEY
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.DUMMY_PUBKEY_1
|
||||
import net.corda.testing.TestDependencyInjectionBase
|
||||
@ -212,6 +210,6 @@ class RequeryConfigurationTest : TestDependencyInjectionBase() {
|
||||
notary = DUMMY_NOTARY,
|
||||
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))))
|
||||
}
|
||||
}
|
@ -19,7 +19,11 @@ import net.corda.node.services.persistence.DBCheckpointStorage
|
||||
import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl
|
||||
import net.corda.node.services.statemachine.StateMachineManager
|
||||
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.testNodeConfiguration
|
||||
import net.corda.testing.initialiseTestSerialization
|
||||
@ -35,7 +39,6 @@ import java.nio.file.Paths
|
||||
import java.security.PublicKey
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
@ -1,15 +1,17 @@
|
||||
package net.corda.node.services.persistence
|
||||
|
||||
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.testing.NullPublicKey
|
||||
import net.corda.core.crypto.SignatureMetadata
|
||||
import net.corda.core.crypto.TransactionSignature
|
||||
import net.corda.core.toFuture
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||
import net.corda.node.utilities.CordaPersistence
|
||||
import net.corda.node.utilities.configureDatabase
|
||||
import net.corda.testing.ALICE_PUBKEY
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.LogHelper
|
||||
import net.corda.testing.TestDependencyInjectionBase
|
||||
@ -150,6 +152,6 @@ class DBTransactionStorageTests : TestDependencyInjectionBase() {
|
||||
notary = DUMMY_NOTARY,
|
||||
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))))
|
||||
}
|
||||
}
|
||||
|
@ -3,15 +3,15 @@ package net.corda.node.services.transactions
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
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.NotaryException
|
||||
import net.corda.core.flows.NotaryFlow
|
||||
import net.corda.core.node.services.ServiceInfo
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
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.services.network.NetworkMapService
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
@ -132,7 +132,7 @@ class NotaryServiceTests {
|
||||
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 future = clientNode.services.startFlow(flow).resultFuture
|
||||
mockNet.runNetwork()
|
||||
|
@ -4,7 +4,7 @@ import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.contracts.Command
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
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.NotaryException
|
||||
import net.corda.core.flows.NotaryFlow
|
||||
@ -85,7 +85,7 @@ class ValidatingNotaryServiceTests {
|
||||
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 future = clientNode.services.startFlow(flow).resultFuture
|
||||
mockNet.runNetwork()
|
||||
|
@ -5,7 +5,6 @@ import net.corda.contracts.asset.DUMMY_CASH_ISSUER
|
||||
import net.corda.contracts.getCashBalance
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.node.services.StatesNotAvailableException
|
||||
import net.corda.core.node.services.Vault
|
||||
@ -509,14 +508,14 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
val amount = Amount(1000, Issued(BOC.ref(1), GBP))
|
||||
|
||||
// Issue some cash
|
||||
val issueTx = TransactionBuilder(notary).apply {
|
||||
val issueTxBuilder = TransactionBuilder(notary).apply {
|
||||
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
|
||||
val signedIssueTx = SignedTransaction(issueTx, listOf(BOC_KEY.sign(issueTx.id)))
|
||||
services.validatedTransactions.addTransaction(signedIssueTx)
|
||||
services.validatedTransactions.addTransaction(issueTxBuilder.toSignedTransaction())
|
||||
|
||||
val issueTx = issueTxBuilder.toWireTransaction()
|
||||
val initialCashState = StateAndRef(issueTx.outputs.single(), StateRef(issueTx.id, 0))
|
||||
|
||||
// Change notary
|
||||
|
@ -9,9 +9,7 @@ import net.corda.contracts.math.CubicSplineInterpolator
|
||||
import net.corda.contracts.math.Interpolator
|
||||
import net.corda.contracts.math.InterpolatorFactory
|
||||
import net.corda.core.contracts.Command
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.crypto.MerkleTreeException
|
||||
import net.corda.core.crypto.keys
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.flows.FlowException
|
||||
import net.corda.core.flows.FlowLogic
|
||||
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.
|
||||
// It will be fixed by adding partial signatures later.
|
||||
// DOCSTART 1
|
||||
fun sign(ftx: FilteredTransaction): DigitalSignature.WithKey {
|
||||
fun sign(ftx: FilteredTransaction): TransactionSignature {
|
||||
if (!ftx.verify()) {
|
||||
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
|
||||
// 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.
|
||||
val signature = services.keyManagementService.sign(ftx.rootHash.bytes, signingKey)
|
||||
return DigitalSignature.WithKey(signingKey, signature.bytes)
|
||||
val signableData = SignableData(ftx.rootHash, SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(signingKey).schemeNumberID))
|
||||
val signature = services.keyManagementService.sign(signableData, signingKey)
|
||||
return TransactionSignature(signature.bytes, signingKey, signableData.signatureMetadata)
|
||||
}
|
||||
// DOCEND 1
|
||||
|
||||
|
@ -3,7 +3,7 @@ package net.corda.irs.flows
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.contracts.Fix
|
||||
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.flows.FlowLogic
|
||||
import net.corda.core.flows.InitiatingFlow
|
||||
@ -112,10 +112,10 @@ open class RatesFixFlow(protected val tx: TransactionBuilder,
|
||||
|
||||
@InitiatingFlow
|
||||
class FixSignFlow(val tx: TransactionBuilder, val oracle: Party,
|
||||
val partialMerkleTx: FilteredTransaction) : FlowLogic<DigitalSignature.WithKey>() {
|
||||
val partialMerkleTx: FilteredTransaction) : FlowLogic<TransactionSignature>() {
|
||||
@Suspendable
|
||||
override fun call(): DigitalSignature.WithKey {
|
||||
val resp = sendAndReceive<DigitalSignature.WithKey>(oracle, SignRequest(partialMerkleTx))
|
||||
override fun call(): TransactionSignature {
|
||||
val resp = sendAndReceive<TransactionSignature>(oracle, SignRequest(partialMerkleTx))
|
||||
return resp.unwrap { sig ->
|
||||
check(oracle.owningKey.isFulfilledBy(listOf(sig.by)))
|
||||
tx.toWireTransaction().checkSignature(sig)
|
||||
|
@ -77,6 +77,9 @@ val DUMMY_CA: CertificateAndKeyPair by lazy {
|
||||
|
||||
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.
|
||||
//
|
||||
|
@ -1,12 +1,9 @@
|
||||
package net.corda.testing
|
||||
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.crypto.composite.expandedCompositeKeys
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.crypto.testing.NullSignature
|
||||
import net.corda.core.crypto.toStringShort
|
||||
import net.corda.core.crypto.testing.NULL_SIGNATURE
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
@ -289,7 +286,7 @@ data class TestLedgerDSLInterpreter private constructor(
|
||||
override fun verifies(): EnforceVerifyOrFail {
|
||||
try {
|
||||
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) {
|
||||
val wtx = value.transaction
|
||||
val ltx = wtx.toLedgerTransaction(services)
|
||||
@ -301,7 +298,7 @@ data class TestLedgerDSLInterpreter private constructor(
|
||||
throw DoubleSpentInputs(txIds)
|
||||
}
|
||||
usedInputs.addAll(wtx.inputs)
|
||||
services.recordTransactions(SignedTransaction(wtx, listOf(NullSignature)))
|
||||
services.recordTransactions(SignedTransaction(wtx, listOf(NULL_SIGNATURE)))
|
||||
}
|
||||
return EnforceVerifyOrFail.Token
|
||||
} catch (exception: TransactionVerificationException) {
|
||||
@ -335,7 +332,7 @@ data class TestLedgerDSLInterpreter private constructor(
|
||||
*/
|
||||
fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: List<KeyPair>) = transactionsToSign.map { wtx ->
|
||||
check(wtx.requiredSigningKeys.isNotEmpty())
|
||||
val signatures = ArrayList<DigitalSignature.WithKey>()
|
||||
val signatures = ArrayList<TransactionSignature>()
|
||||
val keyLookup = HashMap<PublicKey, KeyPair>()
|
||||
|
||||
(ALL_TEST_KEYS + extraKeys).forEach {
|
||||
@ -343,7 +340,7 @@ fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: List<KeyPair>)
|
||||
}
|
||||
wtx.requiredSigningKeys.expandedCompositeKeys.forEach {
|
||||
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)
|
||||
}
|
||||
|
@ -124,8 +124,12 @@ class MockKeyManagementService(val identityService: IdentityService,
|
||||
|
||||
override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
|
||||
val keyPair = getSigningKeyPair(publicKey)
|
||||
val signature = keyPair.sign(bytes)
|
||||
return signature
|
||||
return keyPair.sign(bytes)
|
||||
}
|
||||
|
||||
override fun sign(signableData: SignableData, publicKey: PublicKey): TransactionSignature {
|
||||
val keyPair = getSigningKeyPair(publicKey)
|
||||
return keyPair.sign(signableData)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user