mirror of
https://github.com/corda/corda.git
synced 2025-05-02 16:53:22 +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
|
* A privacy salt is required to compute nonces per transaction component in order to ensure that an adversary cannot
|
||||||
* use brute force techniques and reveal the content of a merkle-leaf hashed value.
|
* use brute force techniques and reveal the content of a Merkle-leaf hashed value.
|
||||||
* Because this salt serves the role of the seed to compute nonces, its size and entropy should be equal to the
|
* Because this salt serves the role of the seed to compute nonces, its size and entropy should be equal to the
|
||||||
* underlying hash function used for Merkle tree generation, currently [SHA256], which has an output of 32 bytes.
|
* underlying hash function used for Merkle tree generation, currently [SHA256], which has an output of 32 bytes.
|
||||||
* There are two constructors, one that generates a new 32-bytes random salt, and another that takes a [ByteArray] input.
|
* There are two constructors, one that generates a new 32-bytes random salt, and another that takes a [ByteArray] input.
|
||||||
|
@ -4,6 +4,7 @@ import net.corda.core.crypto.composite.CompositeKey
|
|||||||
import net.corda.core.crypto.composite.CompositeSignature
|
import net.corda.core.crypto.composite.CompositeSignature
|
||||||
import net.corda.core.crypto.provider.CordaObjectIdentifier
|
import net.corda.core.crypto.provider.CordaObjectIdentifier
|
||||||
import net.corda.core.crypto.provider.CordaSecurityProvider
|
import net.corda.core.crypto.provider.CordaSecurityProvider
|
||||||
|
import net.corda.core.serialization.serialize
|
||||||
import net.i2p.crypto.eddsa.EdDSAEngine
|
import net.i2p.crypto.eddsa.EdDSAEngine
|
||||||
import net.i2p.crypto.eddsa.EdDSAPrivateKey
|
import net.i2p.crypto.eddsa.EdDSAPrivateKey
|
||||||
import net.i2p.crypto.eddsa.EdDSAPublicKey
|
import net.i2p.crypto.eddsa.EdDSAPublicKey
|
||||||
@ -401,23 +402,23 @@ object Crypto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic way to sign [MetaData] objects with a [PrivateKey].
|
* Generic way to sign [SignableData] objects with a [PrivateKey].
|
||||||
* [MetaData] is a wrapper over the transaction's Merkle root in order to attach extra information, such as a timestamp or partial and blind signature indicators.
|
* [SignableData] is a wrapper over the transaction's id (Merkle root) in order to attach extra information, such as a timestamp or partial and blind signature indicators.
|
||||||
* @param privateKey the signer's [PrivateKey].
|
* @param privateKey the signer's [PrivateKey].
|
||||||
* @param metaData a [MetaData] object that adds extra information to a transaction.
|
* @param signableData a [SignableData] object that adds extra information to a transaction.
|
||||||
* @return a [TransactionSignature] object than contains the output of a successful signing and the metaData.
|
* @return a [TransactionSignature] object than contains the output of a successful signing, signer's public key and the signature metadata.
|
||||||
* @throws IllegalArgumentException if the signature scheme is not supported for this private key or
|
* @throws IllegalArgumentException if the signature scheme is not supported for this private key.
|
||||||
* if metaData.schemeCodeName is not aligned with key type.
|
|
||||||
* @throws InvalidKeyException if the private key is invalid.
|
* @throws InvalidKeyException if the private key is invalid.
|
||||||
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
||||||
*/
|
*/
|
||||||
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
|
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
|
||||||
fun doSign(privateKey: PrivateKey, metaData: MetaData): TransactionSignature {
|
fun doSign(keyPair: KeyPair, signableData: SignableData): TransactionSignature {
|
||||||
val sigKey: SignatureScheme = findSignatureScheme(privateKey)
|
val sigKey: SignatureScheme = findSignatureScheme(keyPair.private)
|
||||||
val sigMetaData: SignatureScheme = findSignatureScheme(metaData.schemeCodeName)
|
val sigMetaData: SignatureScheme = findSignatureScheme(keyPair.public)
|
||||||
if (sigKey != sigMetaData) throw IllegalArgumentException("Metadata schemeCodeName: ${metaData.schemeCodeName} is not aligned with the key type.")
|
if (sigKey != sigMetaData) throw IllegalArgumentException("Metadata schemeCodeName: ${sigMetaData.schemeCodeName}" +
|
||||||
val signatureData = doSign(sigKey.schemeCodeName, privateKey, metaData.bytes())
|
" is not aligned with the key type: ${sigKey.schemeCodeName}.")
|
||||||
return TransactionSignature(signatureData, metaData)
|
val signatureBytes = doSign(sigKey.schemeCodeName, keyPair.private, signableData.serialize().bytes)
|
||||||
|
return TransactionSignature(signatureBytes, keyPair.public, signableData.signatureMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -434,7 +435,7 @@ object Crypto {
|
|||||||
* if this signatureData scheme is unable to process the input data provided, if the verification is not possible.
|
* if this signatureData scheme is unable to process the input data provided, if the verification is not possible.
|
||||||
* @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty.
|
* @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty.
|
||||||
*/
|
*/
|
||||||
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
|
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||||
fun doVerify(schemeCodeName: String, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray) = doVerify(findSignatureScheme(schemeCodeName), publicKey, signatureData, clearData)
|
fun doVerify(schemeCodeName: String, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray) = doVerify(findSignatureScheme(schemeCodeName), publicKey, signatureData, clearData)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -485,9 +486,9 @@ object Crypto {
|
|||||||
/**
|
/**
|
||||||
* Utility to simplify the act of verifying a [TransactionSignature].
|
* Utility to simplify the act of verifying a [TransactionSignature].
|
||||||
* It returns true if it succeeds, but it always throws an exception if verification fails.
|
* It returns true if it succeeds, but it always throws an exception if verification fails.
|
||||||
* @param publicKey the signer's [PublicKey].
|
* @param txId transaction's id (Merkle root).
|
||||||
* @param transactionSignature the signatureData on a message.
|
* @param transactionSignature the signature on the transaction.
|
||||||
* @return true if verification passes or throws an exception if verification fails.
|
* @return true if verification passes or throw exception if verification fails.
|
||||||
* @throws InvalidKeyException if the key is invalid.
|
* @throws InvalidKeyException if the key is invalid.
|
||||||
* @throws SignatureException if this signatureData object is not initialized properly,
|
* @throws SignatureException if this signatureData object is not initialized properly,
|
||||||
* the passed-in signatureData is improperly encoded or of the wrong type,
|
* the passed-in signatureData is improperly encoded or of the wrong type,
|
||||||
@ -495,9 +496,26 @@ object Crypto {
|
|||||||
* @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty.
|
* @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty.
|
||||||
*/
|
*/
|
||||||
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
|
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
|
||||||
fun doVerify(publicKey: PublicKey, transactionSignature: TransactionSignature): Boolean {
|
fun doVerify(txId: SecureHash, transactionSignature: TransactionSignature): Boolean {
|
||||||
if (publicKey != transactionSignature.metaData.publicKey) IllegalArgumentException("MetaData's publicKey: ${transactionSignature.metaData.publicKey.toStringShort()} does not match")
|
val signableData = SignableData(txId, transactionSignature.signatureMetadata)
|
||||||
return Crypto.doVerify(publicKey, transactionSignature.signatureData, transactionSignature.metaData.bytes())
|
return Crypto.doVerify(transactionSignature.by, transactionSignature.bytes, signableData.serialize().bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility to simplify the act of verifying a digital signature by identifying the signature scheme used from the input public key's type.
|
||||||
|
* It returns true if it succeeds and false if not. In comparison to [doVerify] if the key and signature
|
||||||
|
* do not match it returns false rather than throwing an exception. Normally you should use the function which throws,
|
||||||
|
* as it avoids the risk of failing to test the result.
|
||||||
|
* @param txId transaction's id (Merkle root).
|
||||||
|
* @param transactionSignature the signature on the transaction.
|
||||||
|
* @throws SignatureException if this signatureData object is not initialized properly,
|
||||||
|
* the passed-in signatureData is improperly encoded or of the wrong type,
|
||||||
|
* if this signatureData scheme is unable to process the input data provided, if the verification is not possible.
|
||||||
|
*/
|
||||||
|
@Throws(SignatureException::class)
|
||||||
|
fun isValid(txId: SecureHash, transactionSignature: TransactionSignature): Boolean {
|
||||||
|
val signableData = SignableData(txId, transactionSignature.signatureMetadata)
|
||||||
|
return isValid(findSignatureScheme(transactionSignature.by), transactionSignature.by, transactionSignature.bytes, signableData.serialize().bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,7 +34,17 @@ fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignat
|
|||||||
*/
|
*/
|
||||||
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
|
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
|
||||||
fun KeyPair.sign(bytesToSign: ByteArray) = private.sign(bytesToSign, public)
|
fun KeyPair.sign(bytesToSign: ByteArray) = private.sign(bytesToSign, public)
|
||||||
fun KeyPair.sign(bytesToSign: OpaqueBytes) = private.sign(bytesToSign.bytes, public)
|
fun KeyPair.sign(bytesToSign: OpaqueBytes) = sign(bytesToSign.bytes)
|
||||||
|
/**
|
||||||
|
* Helper function for signing a [SignableData] object.
|
||||||
|
* @param signableData the object to be signed.
|
||||||
|
* @return a [TransactionSignature] object.
|
||||||
|
* @throws IllegalArgumentException if the signature scheme is not supported for this private key.
|
||||||
|
* @throws InvalidKeyException if the private key is invalid.
|
||||||
|
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
||||||
|
*/
|
||||||
|
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||||
|
fun KeyPair.sign(signableData: SignableData): TransactionSignature = Crypto.doSign(this, signableData)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility to simplify the act of verifying a signature.
|
* Utility to simplify the act of verifying a signature.
|
||||||
@ -88,7 +98,7 @@ fun PublicKey.containsAny(otherKeys: Iterable<PublicKey>): Boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the set of all [PublicKey]s of the signatures */
|
/** Returns the set of all [PublicKey]s of the signatures */
|
||||||
fun Iterable<DigitalSignature.WithKey>.byKeys() = map { it.by }.toSet()
|
fun Iterable<TransactionSignature>.byKeys() = map { it.by }.toSet()
|
||||||
|
|
||||||
// Allow Kotlin destructuring: val (private, public) = keyPair
|
// Allow Kotlin destructuring: val (private, public) = keyPair
|
||||||
operator fun KeyPair.component1(): PrivateKey = this.private
|
operator fun KeyPair.component1(): PrivateKey = this.private
|
||||||
@ -105,17 +115,6 @@ fun generateKeyPair(): KeyPair = Crypto.generateKeyPair()
|
|||||||
*/
|
*/
|
||||||
fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.deriveKeyPairFromEntropy(entropy)
|
fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.deriveKeyPairFromEntropy(entropy)
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function for signing.
|
|
||||||
* @param metaData tha attached MetaData object.
|
|
||||||
* @return a [TransactionSignature ] object.
|
|
||||||
* @throws IllegalArgumentException if the signature scheme is not supported for this private key.
|
|
||||||
* @throws InvalidKeyException if the private key is invalid.
|
|
||||||
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
|
||||||
*/
|
|
||||||
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
|
|
||||||
fun PrivateKey.sign(metaData: MetaData): TransactionSignature = Crypto.doSign(this, metaData)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to verify a signature.
|
* Helper function to verify a signature.
|
||||||
* @param signatureData the signature on a message.
|
* @param signatureData the signature on a message.
|
||||||
@ -129,21 +128,6 @@ fun PrivateKey.sign(metaData: MetaData): TransactionSignature = Crypto.doSign(th
|
|||||||
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
|
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
|
||||||
fun PublicKey.verify(signatureData: ByteArray, clearData: ByteArray): Boolean = Crypto.doVerify(this, signatureData, clearData)
|
fun PublicKey.verify(signatureData: ByteArray, clearData: ByteArray): Boolean = Crypto.doVerify(this, signatureData, clearData)
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to verify a metadata attached signature. It is noted that the transactionSignature contains
|
|
||||||
* signatureData and a [MetaData] object that contains the signer's public key and the transaction's Merkle root.
|
|
||||||
* @param transactionSignature a [TransactionSignature] object that .
|
|
||||||
* @throws InvalidKeyException if the key is invalid.
|
|
||||||
* @throws SignatureException if this signatureData object is not initialized properly,
|
|
||||||
* the passed-in signatureData is improperly encoded or of the wrong type,
|
|
||||||
* if this signatureData algorithm is unable to process the input data provided, etc.
|
|
||||||
* @throws IllegalArgumentException if the signature scheme is not supported for this private key or if any of the clear or signature data is empty.
|
|
||||||
*/
|
|
||||||
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
|
|
||||||
fun PublicKey.verify(transactionSignature: TransactionSignature): Boolean {
|
|
||||||
return Crypto.doVerify(this, transactionSignature)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function for the signers to verify their own signature.
|
* Helper function for the signers to verify their own signature.
|
||||||
* @param signatureData the signature on a message.
|
* @param signatureData the signature on a message.
|
||||||
|
@ -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.
|
* This class is used to define a digital signature scheme.
|
||||||
* @param schemeNumberID we assign a number ID for more efficient on-wire serialisation. Please ensure uniqueness between schemes.
|
* @param schemeNumberID we assign a number ID for better efficiency on-wire serialisation. Please ensure uniqueness between schemes.
|
||||||
* @param schemeCodeName code name for this signature scheme (e.g. RSA_SHA256, ECDSA_SECP256K1_SHA256, ECDSA_SECP256R1_SHA256, EDDSA_ED25519_SHA512, SPHINCS-256_SHA512).
|
* @param schemeCodeName code name for this signature scheme (e.g. RSA_SHA256, ECDSA_SECP256K1_SHA256, ECDSA_SECP256R1_SHA256, EDDSA_ED25519_SHA512, SPHINCS-256_SHA512).
|
||||||
* @param signatureOID ASN.1 algorithm identifier of the signature algorithm (e.g 1.3.101.112 for EdDSA)
|
* @param signatureOID ASN.1 algorithm identifier of the signature algorithm (e.g 1.3.101.112 for EdDSA)
|
||||||
* @param alternativeOIDs ASN.1 algorithm identifiers for keys of the signature, where we want to map multiple keys to
|
* @param alternativeOIDs ASN.1 algorithm identifiers for keys of the signature, where we want to map multiple keys to
|
||||||
|
@ -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
|
package net.corda.core.crypto
|
||||||
|
|
||||||
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import java.security.InvalidKeyException
|
import java.security.InvalidKeyException
|
||||||
|
import java.security.PublicKey
|
||||||
import java.security.SignatureException
|
import java.security.SignatureException
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A wrapper around a digital signature accompanied with metadata, see [MetaData.Full] and [DigitalSignature].
|
* A wrapper over the signature output accompanied by signer's public key and signature metadata.
|
||||||
* The signature protocol works as follows: s = sign(MetaData.hashBytes).
|
* This is similar to [DigitalSignature.WithKey], but targeted to DLT transaction signatures.
|
||||||
*/
|
*/
|
||||||
open class TransactionSignature(val signatureData: ByteArray, val metaData: MetaData) : DigitalSignature(signatureData) {
|
@CordaSerializable
|
||||||
|
class TransactionSignature(bytes: ByteArray, val by: PublicKey, val signatureMetadata: SignatureMetadata): DigitalSignature(bytes) {
|
||||||
/**
|
/**
|
||||||
* Function to auto-verify a [MetaData] object's signature.
|
* Function to verify a [SignableData] object's signature.
|
||||||
* Note that [MetaData] contains both public key and merkle root of the transaction.
|
* Note that [SignableData] contains the id of the transaction and extra metadata, such as DLT's platform version.
|
||||||
|
*
|
||||||
|
* @param txId transaction's id (Merkle root), which along with [signatureMetadata] will be used to construct the [SignableData] object to be signed.
|
||||||
* @throws InvalidKeyException if the key is invalid.
|
* @throws InvalidKeyException if the key is invalid.
|
||||||
* @throws SignatureException if this signatureData object is not initialized properly,
|
* @throws SignatureException if this signatureData object is not initialized properly,
|
||||||
* the passed-in signatureData is improperly encoded or of the wrong type,
|
* the passed-in signatureData is improperly encoded or of the wrong type,
|
||||||
* if this signatureData algorithm is unable to process the input data provided, etc.
|
* if this signatureData algorithm is unable to process the input data provided, etc.
|
||||||
* @throws IllegalArgumentException if the signature scheme is not supported for this private key or if any of the clear or signature data is empty.
|
* @throws IllegalArgumentException if the signature scheme is not supported for this private key or if any of the clear or signature data is empty.
|
||||||
*/
|
*/
|
||||||
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
|
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||||
fun verify(): Boolean = Crypto.doVerify(metaData.publicKey, signatureData, metaData.bytes())
|
fun verify(txId: SecureHash) = Crypto.doVerify(txId, this)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility to simplify the act of verifying a signature. In comparison to [verify] doesn't throw an
|
||||||
|
* exception, making it more suitable where a boolean is required, but normally you should use the function
|
||||||
|
* which throws, as it avoids the risk of failing to test the result.
|
||||||
|
*
|
||||||
|
* @throws InvalidKeyException if the key to verify the signature with is not valid (i.e. wrong key type for the
|
||||||
|
* signature).
|
||||||
|
* @throws SignatureException if the signature is invalid (i.e. damaged).
|
||||||
|
* @return whether the signature is correct for this key.
|
||||||
|
*/
|
||||||
|
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||||
|
fun isValid(txId: SecureHash) = Crypto.isValid(txId, this)
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other !is TransactionSignature) return false
|
||||||
|
|
||||||
|
return (Arrays.equals(bytes, other.bytes)
|
||||||
|
&& by == other.by
|
||||||
|
&& signatureMetadata == other.signatureMetadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = super.hashCode()
|
||||||
|
result = 31 * result + by.hashCode()
|
||||||
|
result = 31 * result + signatureMetadata.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package net.corda.core.crypto.composite
|
package net.corda.core.crypto.composite
|
||||||
|
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier
|
|
||||||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier
|
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.security.*
|
import java.security.*
|
||||||
import java.security.spec.AlgorithmParameterSpec
|
import java.security.spec.AlgorithmParameterSpec
|
||||||
@ -77,7 +76,7 @@ class CompositeSignature : Signature(SIGNATURE_ALGORITHM) {
|
|||||||
fun engineVerify(sigBytes: ByteArray): Boolean {
|
fun engineVerify(sigBytes: ByteArray): Boolean {
|
||||||
val sig = sigBytes.deserialize<CompositeSignaturesWithKeys>()
|
val sig = sigBytes.deserialize<CompositeSignaturesWithKeys>()
|
||||||
return if (verifyKey.isFulfilledBy(sig.sigs.map { it.by })) {
|
return if (verifyKey.isFulfilledBy(sig.sigs.map { it.by })) {
|
||||||
val clearData = buffer.toByteArray()
|
val clearData = SecureHash.SHA256(buffer.toByteArray())
|
||||||
sig.sigs.all { it.isValid(clearData) }
|
sig.sigs.all { it.isValid(clearData) }
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.corda.core.crypto.composite
|
package net.corda.core.crypto.composite
|
||||||
|
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -8,7 +8,7 @@ import net.corda.core.serialization.CordaSerializable
|
|||||||
* serialization format (i.e. not Kryo).
|
* serialization format (i.e. not Kryo).
|
||||||
*/
|
*/
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
data class CompositeSignaturesWithKeys(val sigs: List<DigitalSignature.WithKey>) {
|
data class CompositeSignaturesWithKeys(val sigs: List<TransactionSignature>) {
|
||||||
companion object {
|
companion object {
|
||||||
val EMPTY = CompositeSignaturesWithKeys(emptyList())
|
val EMPTY = CompositeSignaturesWithKeys(emptyList())
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.corda.core.crypto.testing
|
package net.corda.core.crypto.testing
|
||||||
|
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.SignatureMetadata
|
||||||
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
@ -31,5 +32,4 @@ class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** A signature with a key and value of zero. Useful when you want a signature object that you know won't ever be used. */
|
/** A signature with a key and value of zero. Useful when you want a signature object that you know won't ever be used. */
|
||||||
@CordaSerializable
|
val NULL_SIGNATURE = TransactionSignature(ByteArray(32), NullPublicKey, SignatureMetadata(1, -1))
|
||||||
object NullSignature : DigitalSignature.WithKey(NullPublicKey, ByteArray(32))
|
|
@ -4,7 +4,7 @@ import co.paralleluniverse.fibers.Suspendable
|
|||||||
import net.corda.core.contracts.ContractState
|
import net.corda.core.contracts.ContractState
|
||||||
import net.corda.core.contracts.StateAndRef
|
import net.corda.core.contracts.StateAndRef
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.crypto.isFulfilledBy
|
import net.corda.core.crypto.isFulfilledBy
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
@ -95,7 +95,7 @@ abstract class AbstractStateReplacementFlow {
|
|||||||
abstract protected fun assembleTx(): UpgradeTx
|
abstract protected fun assembleTx(): UpgradeTx
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
private fun collectSignatures(participants: Iterable<PublicKey>, stx: SignedTransaction): List<DigitalSignature.WithKey> {
|
private fun collectSignatures(participants: Iterable<PublicKey>, stx: SignedTransaction): List<TransactionSignature> {
|
||||||
val parties = participants.map {
|
val parties = participants.map {
|
||||||
val participantNode = serviceHub.networkMapCache.getNodeByLegalIdentityKey(it) ?:
|
val participantNode = serviceHub.networkMapCache.getNodeByLegalIdentityKey(it) ?:
|
||||||
throw IllegalStateException("Participant $it to state $originalState not found on the network")
|
throw IllegalStateException("Participant $it to state $originalState not found on the network")
|
||||||
@ -113,10 +113,10 @@ abstract class AbstractStateReplacementFlow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
private fun getParticipantSignature(party: Party, stx: SignedTransaction): DigitalSignature.WithKey {
|
private fun getParticipantSignature(party: Party, stx: SignedTransaction): TransactionSignature {
|
||||||
val proposal = Proposal(originalState.ref, modification)
|
val proposal = Proposal(originalState.ref, modification)
|
||||||
subFlow(SendTransactionFlow(party, stx))
|
subFlow(SendTransactionFlow(party, stx))
|
||||||
return sendAndReceive<DigitalSignature.WithKey>(party, proposal).unwrap {
|
return sendAndReceive<TransactionSignature>(party, proposal).unwrap {
|
||||||
check(party.owningKey.isFulfilledBy(it.by)) { "Not signed by the required participant" }
|
check(party.owningKey.isFulfilledBy(it.by)) { "Not signed by the required participant" }
|
||||||
it.verify(stx.id)
|
it.verify(stx.id)
|
||||||
it
|
it
|
||||||
@ -124,7 +124,7 @@ abstract class AbstractStateReplacementFlow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
private fun getNotarySignatures(stx: SignedTransaction): List<DigitalSignature.WithKey> {
|
private fun getNotarySignatures(stx: SignedTransaction): List<TransactionSignature> {
|
||||||
progressTracker.currentStep = NOTARY
|
progressTracker.currentStep = NOTARY
|
||||||
try {
|
try {
|
||||||
return subFlow(NotaryFlow.Client(stx))
|
return subFlow(NotaryFlow.Client(stx))
|
||||||
@ -165,7 +165,7 @@ abstract class AbstractStateReplacementFlow {
|
|||||||
progressTracker.currentStep = APPROVING
|
progressTracker.currentStep = APPROVING
|
||||||
|
|
||||||
val mySignature = sign(stx)
|
val mySignature = sign(stx)
|
||||||
val swapSignatures = sendAndReceive<List<DigitalSignature.WithKey>>(otherSide, mySignature)
|
val swapSignatures = sendAndReceive<List<TransactionSignature>>(otherSide, mySignature)
|
||||||
|
|
||||||
// TODO: This step should not be necessary, as signatures are re-checked in verifyRequiredSignatures.
|
// TODO: This step should not be necessary, as signatures are re-checked in verifyRequiredSignatures.
|
||||||
val allSignatures = swapSignatures.unwrap { signatures ->
|
val allSignatures = swapSignatures.unwrap { signatures ->
|
||||||
@ -203,7 +203,7 @@ abstract class AbstractStateReplacementFlow {
|
|||||||
require(myKey in requiredKeys) { "Party is not a participant for any of the input states of transaction ${stx.id}" }
|
require(myKey in requiredKeys) { "Party is not a participant for any of the input states of transaction ${stx.id}" }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sign(stx: SignedTransaction): DigitalSignature.WithKey {
|
private fun sign(stx: SignedTransaction): TransactionSignature {
|
||||||
return serviceHub.createSignature(stx)
|
return serviceHub.createSignature(stx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.corda.core.flows
|
package net.corda.core.flows
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.crypto.isFulfilledBy
|
import net.corda.core.crypto.isFulfilledBy
|
||||||
import net.corda.core.crypto.toBase58String
|
import net.corda.core.crypto.toBase58String
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
@ -124,10 +124,10 @@ class CollectSignaturesFlow(val partiallySignedTx: SignedTransaction,
|
|||||||
/**
|
/**
|
||||||
* Get and check the required signature.
|
* Get and check the required signature.
|
||||||
*/
|
*/
|
||||||
@Suspendable private fun collectSignature(counterparty: Party): DigitalSignature.WithKey {
|
@Suspendable private fun collectSignature(counterparty: Party): TransactionSignature {
|
||||||
// SendTransactionFlow allows otherParty to access our data to resolve the transaction.
|
// SendTransactionFlow allows otherParty to access our data to resolve the transaction.
|
||||||
subFlow(SendTransactionFlow(counterparty, partiallySignedTx))
|
subFlow(SendTransactionFlow(counterparty, partiallySignedTx))
|
||||||
return receive<DigitalSignature.WithKey>(counterparty).unwrap {
|
return receive<TransactionSignature>(counterparty).unwrap {
|
||||||
require(counterparty.owningKey.isFulfilledBy(it.by)) { "Not signed by the required Party." }
|
require(counterparty.owningKey.isFulfilledBy(it.by)) { "Not signed by the required Party." }
|
||||||
it
|
it
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,9 @@ package net.corda.core.flows
|
|||||||
import net.corda.core.contracts.ContractState
|
import net.corda.core.contracts.ContractState
|
||||||
import net.corda.core.contracts.StateAndRef
|
import net.corda.core.contracts.StateAndRef
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
|
import net.corda.core.crypto.Crypto
|
||||||
|
import net.corda.core.crypto.SignableData
|
||||||
|
import net.corda.core.crypto.SignatureMetadata
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.transactions.NotaryChangeWireTransaction
|
import net.corda.core.transactions.NotaryChangeWireTransaction
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
@ -36,7 +39,8 @@ class NotaryChangeFlow<out T : ContractState>(
|
|||||||
val participantKeys = inputs.flatMap { it.state.data.participants }.map { it.owningKey }.toSet()
|
val participantKeys = inputs.flatMap { it.state.data.participants }.map { it.owningKey }.toSet()
|
||||||
// TODO: We need a much faster way of finding our key in the transaction
|
// TODO: We need a much faster way of finding our key in the transaction
|
||||||
val myKey = serviceHub.keyManagementService.filterMyKeys(participantKeys).single()
|
val myKey = serviceHub.keyManagementService.filterMyKeys(participantKeys).single()
|
||||||
val mySignature = serviceHub.keyManagementService.sign(tx.id.bytes, myKey)
|
val signableData = SignableData(tx.id, SignatureMetadata(serviceHub.myInfo.platformVersion, Crypto.findSignatureScheme(myKey).schemeNumberID))
|
||||||
|
val mySignature = serviceHub.keyManagementService.sign(signableData, myKey)
|
||||||
val stx = SignedTransaction(tx, listOf(mySignature))
|
val stx = SignedTransaction(tx, listOf(mySignature))
|
||||||
|
|
||||||
return AbstractStateReplacementFlow.UpgradeTx(stx, participantKeys, myKey)
|
return AbstractStateReplacementFlow.UpgradeTx(stx, participantKeys, myKey)
|
||||||
|
@ -3,9 +3,9 @@ package net.corda.core.flows
|
|||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.contracts.TimeWindow
|
import net.corda.core.contracts.TimeWindow
|
||||||
import net.corda.core.crypto.DigitalSignature
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.SignedData
|
import net.corda.core.crypto.SignedData
|
||||||
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.crypto.keys
|
import net.corda.core.crypto.keys
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.FetchDataFlow
|
import net.corda.core.internal.FetchDataFlow
|
||||||
@ -32,7 +32,7 @@ object NotaryFlow {
|
|||||||
*/
|
*/
|
||||||
@InitiatingFlow
|
@InitiatingFlow
|
||||||
open class Client(private val stx: SignedTransaction,
|
open class Client(private val stx: SignedTransaction,
|
||||||
override val progressTracker: ProgressTracker) : FlowLogic<List<DigitalSignature.WithKey>>() {
|
override val progressTracker: ProgressTracker) : FlowLogic<List<TransactionSignature>>() {
|
||||||
constructor(stx: SignedTransaction) : this(stx, tracker())
|
constructor(stx: SignedTransaction) : this(stx, tracker())
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -46,7 +46,7 @@ object NotaryFlow {
|
|||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
@Throws(NotaryException::class)
|
@Throws(NotaryException::class)
|
||||||
override fun call(): List<DigitalSignature.WithKey> {
|
override fun call(): List<TransactionSignature> {
|
||||||
progressTracker.currentStep = REQUESTING
|
progressTracker.currentStep = REQUESTING
|
||||||
|
|
||||||
notaryParty = stx.notary ?: throw IllegalStateException("Transaction does not specify a Notary")
|
notaryParty = stx.notary ?: throw IllegalStateException("Transaction does not specify a Notary")
|
||||||
@ -67,7 +67,7 @@ object NotaryFlow {
|
|||||||
val response = try {
|
val response = try {
|
||||||
if (serviceHub.networkMapCache.isValidatingNotary(notaryParty)) {
|
if (serviceHub.networkMapCache.isValidatingNotary(notaryParty)) {
|
||||||
subFlow(SendTransactionWithRetry(notaryParty, stx))
|
subFlow(SendTransactionWithRetry(notaryParty, stx))
|
||||||
receive<List<DigitalSignature.WithKey>>(notaryParty)
|
receive<List<TransactionSignature>>(notaryParty)
|
||||||
} else {
|
} else {
|
||||||
val tx: Any = if (stx.isNotaryChangeTransaction()) {
|
val tx: Any = if (stx.isNotaryChangeTransaction()) {
|
||||||
stx.notaryChangeTx
|
stx.notaryChangeTx
|
||||||
@ -84,14 +84,14 @@ object NotaryFlow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return response.unwrap { signatures ->
|
return response.unwrap { signatures ->
|
||||||
signatures.forEach { validateSignature(it, stx.id.bytes) }
|
signatures.forEach { validateSignature(it, stx.id) }
|
||||||
signatures
|
signatures
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validateSignature(sig: DigitalSignature.WithKey, data: ByteArray) {
|
private fun validateSignature(sig: TransactionSignature, txId: SecureHash) {
|
||||||
check(sig.by in notaryParty.owningKey.keys) { "Invalid signer for the notary result" }
|
check(sig.by in notaryParty.owningKey.keys) { "Invalid signer for the notary result" }
|
||||||
sig.verify(data)
|
sig.verify(txId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ object NotaryFlow {
|
|||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
private fun signAndSendResponse(txId: SecureHash) {
|
private fun signAndSendResponse(txId: SecureHash) {
|
||||||
val signature = service.sign(txId.bytes)
|
val signature = service.sign(txId)
|
||||||
send(otherSide, listOf(signature))
|
send(otherSide, listOf(signature))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package net.corda.core.node
|
package net.corda.core.node
|
||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.Crypto
|
||||||
|
import net.corda.core.crypto.SignableData
|
||||||
|
import net.corda.core.crypto.SignatureMetadata
|
||||||
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.node.services.*
|
import net.corda.core.node.services.*
|
||||||
import net.corda.core.serialization.SerializeAsToken
|
import net.corda.core.serialization.SerializeAsToken
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
@ -123,7 +126,7 @@ interface ServiceHub : ServicesForResolution {
|
|||||||
/**
|
/**
|
||||||
* Helper property to shorten code for fetching the the [PublicKey] portion of the
|
* Helper property to shorten code for fetching the the [PublicKey] portion of the
|
||||||
* Node's Notary signing identity. It is required that the Node hosts a notary service,
|
* Node's Notary signing identity. It is required that the Node hosts a notary service,
|
||||||
* otherwise an IllegalArgumentException will be thrown.
|
* otherwise an [IllegalArgumentException] will be thrown.
|
||||||
* Typical use is during signing in flows and for unit test signing.
|
* Typical use is during signing in flows and for unit test signing.
|
||||||
* When this [PublicKey] is passed into the signing methods below, or on the KeyManagementService
|
* When this [PublicKey] is passed into the signing methods below, or on the KeyManagementService
|
||||||
* the matching [java.security.PrivateKey] will be looked up internally and used to sign.
|
* the matching [java.security.PrivateKey] will be looked up internally and used to sign.
|
||||||
@ -132,9 +135,17 @@ interface ServiceHub : ServicesForResolution {
|
|||||||
*/
|
*/
|
||||||
val notaryIdentityKey: PublicKey get() = this.myInfo.notaryIdentity.owningKey
|
val notaryIdentityKey: PublicKey get() = this.myInfo.notaryIdentity.owningKey
|
||||||
|
|
||||||
|
// Helper method to construct an initial partially signed transaction from a [TransactionBuilder].
|
||||||
|
private fun signInitialTransaction(builder: TransactionBuilder, publicKey: PublicKey, signatureMetadata: SignatureMetadata): SignedTransaction {
|
||||||
|
val signableData = SignableData(builder.toWireTransaction().id, signatureMetadata)
|
||||||
|
val sig = keyManagementService.sign(signableData, publicKey)
|
||||||
|
builder.addSignatureUnchecked(sig)
|
||||||
|
return builder.toSignedTransaction(false)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to construct an initial partially signed transaction from a [TransactionBuilder]
|
* Helper method to construct an initial partially signed transaction from a [TransactionBuilder]
|
||||||
* using keys stored inside the node.
|
* using keys stored inside the node. Signature metadata is added automatically.
|
||||||
* @param builder The [TransactionBuilder] to seal with the node's signature.
|
* @param builder The [TransactionBuilder] to seal with the node's signature.
|
||||||
* Any existing signatures on the builder will be preserved.
|
* Any existing signatures on the builder will be preserved.
|
||||||
* @param publicKey The [PublicKey] matched to the internal [java.security.PrivateKey] to use in signing this transaction.
|
* @param publicKey The [PublicKey] matched to the internal [java.security.PrivateKey] to use in signing this transaction.
|
||||||
@ -142,15 +153,12 @@ interface ServiceHub : ServicesForResolution {
|
|||||||
* to sign with.
|
* to sign with.
|
||||||
* @return Returns a SignedTransaction with the new node signature attached.
|
* @return Returns a SignedTransaction with the new node signature attached.
|
||||||
*/
|
*/
|
||||||
fun signInitialTransaction(builder: TransactionBuilder, publicKey: PublicKey): SignedTransaction {
|
fun signInitialTransaction(builder: TransactionBuilder, publicKey: PublicKey) =
|
||||||
val sig = keyManagementService.sign(builder.toWireTransaction().id.bytes, publicKey)
|
signInitialTransaction(builder, publicKey, SignatureMetadata(myInfo.platformVersion, Crypto.findSignatureScheme(publicKey).schemeNumberID))
|
||||||
builder.addSignatureUnchecked(sig)
|
|
||||||
return builder.toSignedTransaction(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to construct an initial partially signed transaction from a TransactionBuilder
|
* Helper method to construct an initial partially signed transaction from a TransactionBuilder
|
||||||
* using the default identity key contained in the node.
|
* using the default identity key contained in the node. The legal Indentity key is used to sign.
|
||||||
* @param builder The TransactionBuilder to seal with the node's signature.
|
* @param builder The TransactionBuilder to seal with the node's signature.
|
||||||
* Any existing signatures on the builder will be preserved.
|
* Any existing signatures on the builder will be preserved.
|
||||||
* @return Returns a SignedTransaction with the new node signature attached.
|
* @return Returns a SignedTransaction with the new node signature attached.
|
||||||
@ -175,25 +183,30 @@ interface ServiceHub : ServicesForResolution {
|
|||||||
return stx
|
return stx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper method to create an additional signature for an existing (partially) [SignedTransaction].
|
||||||
|
private fun createSignature(signedTransaction: SignedTransaction, publicKey: PublicKey, signatureMetadata: SignatureMetadata): TransactionSignature {
|
||||||
|
val signableData = SignableData(signedTransaction.id, signatureMetadata)
|
||||||
|
return keyManagementService.sign(signableData, publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to create an additional signature for an existing (partially) [SignedTransaction].
|
* Helper method to create an additional signature for an existing (partially) [SignedTransaction].
|
||||||
* @param signedTransaction The [SignedTransaction] to which the signature will apply.
|
* @param signedTransaction The [SignedTransaction] to which the signature will apply.
|
||||||
* @param publicKey The [PublicKey] matching to a signing [java.security.PrivateKey] hosted in the node.
|
* @param publicKey The [PublicKey] matching to a signing [java.security.PrivateKey] hosted in the node.
|
||||||
* If the [PublicKey] is actually a [net.corda.core.crypto.CompositeKey] the first leaf key found locally will be used
|
* If the [PublicKey] is actually a [net.corda.core.crypto.CompositeKey] the first leaf key found locally will be used
|
||||||
* for signing.
|
* for signing.
|
||||||
* @return The [DigitalSignature.WithKey] generated by signing with the internally held [java.security.PrivateKey].
|
* @return The [TransactionSignature] generated by signing with the internally held [java.security.PrivateKey].
|
||||||
*/
|
*/
|
||||||
fun createSignature(signedTransaction: SignedTransaction, publicKey: PublicKey): DigitalSignature.WithKey {
|
fun createSignature(signedTransaction: SignedTransaction, publicKey: PublicKey) =
|
||||||
return keyManagementService.sign(signedTransaction.id.bytes, publicKey)
|
createSignature(signedTransaction, publicKey, SignatureMetadata(myInfo.platformVersion, Crypto.findSignatureScheme(publicKey).schemeNumberID))
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to create an additional signature for an existing (partially) SignedTransaction
|
* Helper method to create an additional signature for an existing (partially) [SignedTransaction]
|
||||||
* using the default identity signing key of the node.
|
* using the default identity signing key of the node. The legal identity key is used to sign.
|
||||||
* @param signedTransaction The SignedTransaction to which the signature will apply.
|
* @param signedTransaction The SignedTransaction to which the signature will apply.
|
||||||
* @return The DigitalSignature.WithKey generated by signing with the internally held identity PrivateKey.
|
* @return The DigitalSignature.WithKey generated by signing with the internally held identity PrivateKey.
|
||||||
*/
|
*/
|
||||||
fun createSignature(signedTransaction: SignedTransaction): DigitalSignature.WithKey {
|
fun createSignature(signedTransaction: SignedTransaction): TransactionSignature {
|
||||||
return createSignature(signedTransaction, legalIdentityKey)
|
return createSignature(signedTransaction, legalIdentityKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,7 +223,7 @@ interface ServiceHub : ServicesForResolution {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to ap-pend an additional signature for an existing (partially) [SignedTransaction]
|
* Helper method to append an additional signature for an existing (partially) [SignedTransaction]
|
||||||
* using the default identity signing key of the node.
|
* using the default identity signing key of the node.
|
||||||
* @param signedTransaction The [SignedTransaction] to which the signature will be added.
|
* @param signedTransaction The [SignedTransaction] to which the signature will be added.
|
||||||
* @return A new [SignedTransaction] with the addition of the new signature.
|
* @return A new [SignedTransaction] with the addition of the new signature.
|
||||||
|
@ -2,6 +2,8 @@ package net.corda.core.node.services
|
|||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.DigitalSignature
|
||||||
|
import net.corda.core.crypto.SignableData
|
||||||
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.identity.AnonymousPartyAndPath
|
import net.corda.core.identity.AnonymousPartyAndPath
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -48,9 +50,18 @@ interface KeyManagementService {
|
|||||||
* or previously generated via the [freshKey] method.
|
* or previously generated via the [freshKey] method.
|
||||||
* If the [PublicKey] is actually a [CompositeKey] the first leaf signing key hosted by the node is used.
|
* If the [PublicKey] is actually a [CompositeKey] the first leaf signing key hosted by the node is used.
|
||||||
* @throws IllegalArgumentException if the input key is not a member of [keys].
|
* @throws IllegalArgumentException if the input key is not a member of [keys].
|
||||||
* TODO A full [KeyManagementService] implementation needs to record activity to the [AuditService] and to limit signing to
|
|
||||||
* appropriately authorised contexts and initiating users.
|
|
||||||
*/
|
*/
|
||||||
@Suspendable
|
@Suspendable
|
||||||
fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey
|
fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Using the provided signing [PublicKey] internally looks up the matching [PrivateKey] and signs the [SignableData].
|
||||||
|
* @param signableData a wrapper over transaction id (Merkle root) and signature metadata.
|
||||||
|
* @param publicKey The [PublicKey] partner to an internally held [PrivateKey], either derived from the node's primary identity,
|
||||||
|
* or previously generated via the [freshKey] method.
|
||||||
|
* If the [PublicKey] is actually a [CompositeKey] the first leaf signing key hosted by the node is used.
|
||||||
|
* @throws IllegalArgumentException if the input key is not a member of [keys].
|
||||||
|
*/
|
||||||
|
@Suspendable
|
||||||
|
fun sign(signableData: SignableData, publicKey: PublicKey): TransactionSignature
|
||||||
|
}
|
||||||
|
@ -2,9 +2,7 @@ package net.corda.core.node.services
|
|||||||
|
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.contracts.TimeWindow
|
import net.corda.core.contracts.TimeWindow
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.SecureHash
|
|
||||||
import net.corda.core.crypto.SignedData
|
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.NotaryError
|
import net.corda.core.flows.NotaryError
|
||||||
import net.corda.core.flows.NotaryException
|
import net.corda.core.flows.NotaryException
|
||||||
@ -75,4 +73,9 @@ abstract class TrustedAuthorityNotaryService : NotaryService() {
|
|||||||
fun sign(bits: ByteArray): DigitalSignature.WithKey {
|
fun sign(bits: ByteArray): DigitalSignature.WithKey {
|
||||||
return services.keyManagementService.sign(bits, services.notaryIdentityKey)
|
return services.keyManagementService.sign(bits, services.notaryIdentityKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sign(txId: SecureHash): TransactionSignature {
|
||||||
|
val signableData = SignableData(txId, SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(services.notaryIdentityKey).schemeNumberID))
|
||||||
|
return services.keyManagementService.sign(signableData, services.notaryIdentityKey)
|
||||||
|
}
|
||||||
}
|
}
|
@ -11,7 +11,6 @@ import de.javakaffee.kryoserializers.ArraysAsListSerializer
|
|||||||
import de.javakaffee.kryoserializers.BitSetSerializer
|
import de.javakaffee.kryoserializers.BitSetSerializer
|
||||||
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer
|
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer
|
||||||
import de.javakaffee.kryoserializers.guava.*
|
import de.javakaffee.kryoserializers.guava.*
|
||||||
import net.corda.core.crypto.MetaData
|
|
||||||
import net.corda.core.crypto.composite.CompositeKey
|
import net.corda.core.crypto.composite.CompositeKey
|
||||||
import net.corda.core.node.CordaPluginRegistry
|
import net.corda.core.node.CordaPluginRegistry
|
||||||
import net.corda.core.transactions.NotaryChangeWireTransaction
|
import net.corda.core.transactions.NotaryChangeWireTransaction
|
||||||
@ -98,7 +97,6 @@ object DefaultKryoCustomizer {
|
|||||||
|
|
||||||
addDefaultSerializer(SerializeAsToken::class.java, SerializeAsTokenSerializer<SerializeAsToken>())
|
addDefaultSerializer(SerializeAsToken::class.java, SerializeAsTokenSerializer<SerializeAsToken>())
|
||||||
|
|
||||||
register(MetaData::class.java, MetaDataSerializer)
|
|
||||||
register(BitSet::class.java, BitSetSerializer())
|
register(BitSet::class.java, BitSetSerializer())
|
||||||
register(Class::class.java, ClassSerializer)
|
register(Class::class.java, ClassSerializer)
|
||||||
|
|
||||||
|
@ -5,7 +5,9 @@ import com.esotericsoftware.kryo.io.Input
|
|||||||
import com.esotericsoftware.kryo.io.Output
|
import com.esotericsoftware.kryo.io.Output
|
||||||
import com.esotericsoftware.kryo.util.MapReferenceResolver
|
import com.esotericsoftware.kryo.util.MapReferenceResolver
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.Crypto
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.crypto.composite.CompositeKey
|
import net.corda.core.crypto.composite.CompositeKey
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.VisibleForTesting
|
import net.corda.core.internal.VisibleForTesting
|
||||||
@ -30,8 +32,6 @@ import java.security.PrivateKey
|
|||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
import java.security.cert.CertificateFactory
|
import java.security.cert.CertificateFactory
|
||||||
import java.security.spec.InvalidKeySpecException
|
|
||||||
import java.time.Instant
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.annotation.concurrent.ThreadSafe
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
@ -306,7 +306,7 @@ object SignedTransactionSerializer : Serializer<SignedTransaction>() {
|
|||||||
override fun read(kryo: Kryo, input: Input, type: Class<SignedTransaction>): SignedTransaction {
|
override fun read(kryo: Kryo, input: Input, type: Class<SignedTransaction>): SignedTransaction {
|
||||||
return SignedTransaction(
|
return SignedTransaction(
|
||||||
kryo.readClassAndObject(input) as SerializedBytes<CoreTransaction>,
|
kryo.readClassAndObject(input) as SerializedBytes<CoreTransaction>,
|
||||||
kryo.readClassAndObject(input) as List<DigitalSignature.WithKey>
|
kryo.readClassAndObject(input) as List<TransactionSignature>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -495,35 +495,6 @@ fun <T> Kryo.withoutReferences(block: () -> T): T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** For serialising a MetaData object. */
|
|
||||||
@ThreadSafe
|
|
||||||
object MetaDataSerializer : Serializer<MetaData>() {
|
|
||||||
override fun write(kryo: Kryo, output: Output, obj: MetaData) {
|
|
||||||
output.writeString(obj.schemeCodeName)
|
|
||||||
output.writeString(obj.versionID)
|
|
||||||
kryo.writeClassAndObject(output, obj.signatureType)
|
|
||||||
kryo.writeClassAndObject(output, obj.timestamp)
|
|
||||||
kryo.writeClassAndObject(output, obj.visibleInputs)
|
|
||||||
kryo.writeClassAndObject(output, obj.signedInputs)
|
|
||||||
output.writeBytesWithLength(obj.merkleRoot)
|
|
||||||
output.writeBytesWithLength(obj.publicKey.encoded)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
@Throws(IllegalArgumentException::class, InvalidKeySpecException::class)
|
|
||||||
override fun read(kryo: Kryo, input: Input, type: Class<MetaData>): MetaData {
|
|
||||||
val schemeCodeName = input.readString()
|
|
||||||
val versionID = input.readString()
|
|
||||||
val signatureType = kryo.readClassAndObject(input) as SignatureType
|
|
||||||
val timestamp = kryo.readClassAndObject(input) as Instant?
|
|
||||||
val visibleInputs = kryo.readClassAndObject(input) as BitSet?
|
|
||||||
val signedInputs = kryo.readClassAndObject(input) as BitSet?
|
|
||||||
val merkleRoot = input.readBytesWithLength()
|
|
||||||
val publicKey = Crypto.decodePublicKey(schemeCodeName, input.readBytesWithLength())
|
|
||||||
return MetaData(schemeCodeName, versionID, signatureType, timestamp, visibleInputs, signedInputs, merkleRoot, publicKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** For serialising a Logger. */
|
/** For serialising a Logger. */
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
object LoggerSerializer : Serializer<Logger>() {
|
object LoggerSerializer : Serializer<Logger>() {
|
||||||
|
@ -10,7 +10,7 @@ import java.nio.ByteBuffer
|
|||||||
import java.util.function.Predicate
|
import java.util.function.Predicate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a privacy salt is provided, the resulted output (merkle-leaf) is computed as
|
* If a privacy salt is provided, the resulted output (Merkle-leaf) is computed as
|
||||||
* Hash(serializedObject || Hash(privacy_salt || obj_index_in_merkle_tree)).
|
* Hash(serializedObject || Hash(privacy_salt || obj_index_in_merkle_tree)).
|
||||||
*/
|
*/
|
||||||
fun <T : Any> serializedHash(x: T, privacySalt: PrivacySalt?, index: Int): SecureHash {
|
fun <T : Any> serializedHash(x: T, privacySalt: PrivacySalt?, index: Int): SecureHash {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package net.corda.core.transactions
|
package net.corda.core.transactions
|
||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.DigitalSignature
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.crypto.toBase58String
|
import net.corda.core.crypto.toBase58String
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
@ -36,7 +36,7 @@ data class NotaryChangeWireTransaction(
|
|||||||
*/
|
*/
|
||||||
override val id: SecureHash by lazy { serializedHash(inputs + notary + newNotary) }
|
override val id: SecureHash by lazy { serializedHash(inputs + notary + newNotary) }
|
||||||
|
|
||||||
fun resolve(services: ServiceHub, sigs: List<DigitalSignature.WithKey>): NotaryChangeLedgerTransaction {
|
fun resolve(services: ServiceHub, sigs: List<TransactionSignature>): NotaryChangeLedgerTransaction {
|
||||||
val resolvedInputs = inputs.map { ref ->
|
val resolvedInputs = inputs.map { ref ->
|
||||||
services.loadState(ref).let { StateAndRef(it, ref) }
|
services.loadState(ref).let { StateAndRef(it, ref) }
|
||||||
}
|
}
|
||||||
@ -54,7 +54,7 @@ data class NotaryChangeLedgerTransaction(
|
|||||||
override val notary: Party,
|
override val notary: Party,
|
||||||
val newNotary: Party,
|
val newNotary: Party,
|
||||||
override val id: SecureHash,
|
override val id: SecureHash,
|
||||||
override val sigs: List<DigitalSignature.WithKey>
|
override val sigs: List<TransactionSignature>
|
||||||
) : FullTransaction(), TransactionWithSignatures {
|
) : FullTransaction(), TransactionWithSignatures {
|
||||||
init {
|
init {
|
||||||
checkEncumbrances()
|
checkEncumbrances()
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package net.corda.core.transactions
|
package net.corda.core.transactions
|
||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.DigitalSignature
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
@ -29,10 +29,10 @@ import java.util.*
|
|||||||
*/
|
*/
|
||||||
// DOCSTART 1
|
// DOCSTART 1
|
||||||
data class SignedTransaction(val txBits: SerializedBytes<CoreTransaction>,
|
data class SignedTransaction(val txBits: SerializedBytes<CoreTransaction>,
|
||||||
override val sigs: List<DigitalSignature.WithKey>
|
override val sigs: List<TransactionSignature>
|
||||||
) : TransactionWithSignatures {
|
) : TransactionWithSignatures {
|
||||||
// DOCEND 1
|
// DOCEND 1
|
||||||
constructor(ctx: CoreTransaction, sigs: List<DigitalSignature.WithKey>) : this(ctx.serialize(), sigs) {
|
constructor(ctx: CoreTransaction, sigs: List<TransactionSignature>) : this(ctx.serialize(), sigs) {
|
||||||
cachedTransaction = ctx
|
cachedTransaction = ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,16 +75,16 @@ data class SignedTransaction(val txBits: SerializedBytes<CoreTransaction>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the same transaction but with an additional (unchecked) signature. */
|
/** Returns the same transaction but with an additional (unchecked) signature. */
|
||||||
fun withAdditionalSignature(sig: DigitalSignature.WithKey) = copyWithCache(listOf(sig))
|
fun withAdditionalSignature(sig: TransactionSignature) = copyWithCache(listOf(sig))
|
||||||
|
|
||||||
/** Returns the same transaction but with an additional (unchecked) signatures. */
|
/** Returns the same transaction but with an additional (unchecked) signatures. */
|
||||||
fun withAdditionalSignatures(sigList: Iterable<DigitalSignature.WithKey>) = copyWithCache(sigList)
|
fun withAdditionalSignatures(sigList: Iterable<TransactionSignature>) = copyWithCache(sigList)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a copy of the SignedTransaction that includes the provided [sigList]. Also propagates the [cachedTransaction]
|
* Creates a copy of the SignedTransaction that includes the provided [sigList]. Also propagates the [cachedTransaction]
|
||||||
* so the contained transaction does not need to be deserialized again.
|
* so the contained transaction does not need to be deserialized again.
|
||||||
*/
|
*/
|
||||||
private fun copyWithCache(sigList: Iterable<DigitalSignature.WithKey>): SignedTransaction {
|
private fun copyWithCache(sigList: Iterable<TransactionSignature>): SignedTransaction {
|
||||||
val cached = cachedTransaction
|
val cached = cachedTransaction
|
||||||
return copy(sigs = sigs + sigList).apply {
|
return copy(sigs = sigs + sigList).apply {
|
||||||
cachedTransaction = cached
|
cachedTransaction = cached
|
||||||
@ -92,10 +92,10 @@ data class SignedTransaction(val txBits: SerializedBytes<CoreTransaction>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Alias for [withAdditionalSignature] to let you use Kotlin operator overloading. */
|
/** Alias for [withAdditionalSignature] to let you use Kotlin operator overloading. */
|
||||||
operator fun plus(sig: DigitalSignature.WithKey) = withAdditionalSignature(sig)
|
operator fun plus(sig: TransactionSignature) = withAdditionalSignature(sig)
|
||||||
|
|
||||||
/** Alias for [withAdditionalSignatures] to let you use Kotlin operator overloading. */
|
/** Alias for [withAdditionalSignatures] to let you use Kotlin operator overloading. */
|
||||||
operator fun plus(sigList: Collection<DigitalSignature.WithKey>) = withAdditionalSignatures(sigList)
|
operator fun plus(sigList: Collection<TransactionSignature>) = withAdditionalSignatures(sigList)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the transaction's signatures are valid, optionally calls [verifyRequiredSignatures] to
|
* Checks the transaction's signatures are valid, optionally calls [verifyRequiredSignatures] to
|
||||||
|
@ -151,18 +151,18 @@ open class TransactionBuilder(
|
|||||||
|
|
||||||
/** The signatures that have been collected so far - might be incomplete! */
|
/** The signatures that have been collected so far - might be incomplete! */
|
||||||
@Deprecated("Signatures should be gathered on a SignedTransaction instead.")
|
@Deprecated("Signatures should be gathered on a SignedTransaction instead.")
|
||||||
protected val currentSigs = arrayListOf<DigitalSignature.WithKey>()
|
protected val currentSigs = arrayListOf<TransactionSignature>()
|
||||||
|
|
||||||
@Deprecated("Use ServiceHub.signInitialTransaction() instead.")
|
@Deprecated("Use ServiceHub.signInitialTransaction() instead.")
|
||||||
fun signWith(key: KeyPair): TransactionBuilder {
|
fun signWith(key: KeyPair): TransactionBuilder {
|
||||||
val data = toWireTransaction().id
|
val signableData = SignableData(toWireTransaction().id, SignatureMetadata(1, Crypto.findSignatureScheme(key.public).schemeNumberID)) // A dummy platformVersion.
|
||||||
addSignatureUnchecked(key.sign(data.bytes))
|
addSignatureUnchecked(key.sign(signableData))
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Adds the signature directly to the transaction, without checking it for validity. */
|
/** Adds the signature directly to the transaction, without checking it for validity. */
|
||||||
@Deprecated("Use ServiceHub.signInitialTransaction() instead.")
|
@Deprecated("Use ServiceHub.signInitialTransaction() instead.")
|
||||||
fun addSignatureUnchecked(sig: DigitalSignature.WithKey): TransactionBuilder {
|
fun addSignatureUnchecked(sig: TransactionSignature): TransactionBuilder {
|
||||||
currentSigs.add(sig)
|
currentSigs.add(sig)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
@ -188,7 +188,7 @@ open class TransactionBuilder(
|
|||||||
* @throws IllegalArgumentException if the signature key doesn't appear in any command.
|
* @throws IllegalArgumentException if the signature key doesn't appear in any command.
|
||||||
*/
|
*/
|
||||||
@Deprecated("Use WireTransaction.checkSignature() instead.")
|
@Deprecated("Use WireTransaction.checkSignature() instead.")
|
||||||
fun checkAndAddSignature(sig: DigitalSignature.WithKey) {
|
fun checkAndAddSignature(sig: TransactionSignature) {
|
||||||
checkSignature(sig)
|
checkSignature(sig)
|
||||||
addSignatureUnchecked(sig)
|
addSignatureUnchecked(sig)
|
||||||
}
|
}
|
||||||
@ -200,7 +200,7 @@ open class TransactionBuilder(
|
|||||||
* @throws IllegalArgumentException if the signature key doesn't appear in any command.
|
* @throws IllegalArgumentException if the signature key doesn't appear in any command.
|
||||||
*/
|
*/
|
||||||
@Deprecated("Use WireTransaction.checkSignature() instead.")
|
@Deprecated("Use WireTransaction.checkSignature() instead.")
|
||||||
fun checkSignature(sig: DigitalSignature.WithKey) {
|
fun checkSignature(sig: TransactionSignature) {
|
||||||
require(commands.any { it.signers.any { sig.by in it.keys } }) { "Signature key doesn't match any command" }
|
require(commands.any { it.signers.any { sig.by in it.keys } }) { "Signature key doesn't match any command" }
|
||||||
sig.verify(toWireTransaction().id)
|
sig.verify(toWireTransaction().id)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.corda.core.transactions
|
package net.corda.core.transactions
|
||||||
|
|
||||||
import net.corda.core.contracts.NamedByHash
|
import net.corda.core.contracts.NamedByHash
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.crypto.isFulfilledBy
|
import net.corda.core.crypto.isFulfilledBy
|
||||||
import net.corda.core.transactions.SignedTransaction.SignaturesMissingException
|
import net.corda.core.transactions.SignedTransaction.SignaturesMissingException
|
||||||
import net.corda.core.utilities.toNonEmptySet
|
import net.corda.core.utilities.toNonEmptySet
|
||||||
@ -10,7 +10,7 @@ import java.security.SignatureException
|
|||||||
|
|
||||||
/** An interface for transactions containing signatures, with logic for signature verification */
|
/** An interface for transactions containing signatures, with logic for signature verification */
|
||||||
interface TransactionWithSignatures : NamedByHash {
|
interface TransactionWithSignatures : NamedByHash {
|
||||||
val sigs: List<DigitalSignature.WithKey>
|
val sigs: List<TransactionSignature>
|
||||||
|
|
||||||
/** Specifies all the public keys that require signatures for the transaction to be valid */
|
/** Specifies all the public keys that require signatures for the transaction to be valid */
|
||||||
val requiredSigningKeys: Set<PublicKey>
|
val requiredSigningKeys: Set<PublicKey>
|
||||||
@ -57,7 +57,7 @@ interface TransactionWithSignatures : NamedByHash {
|
|||||||
@Throws(SignatureException::class)
|
@Throws(SignatureException::class)
|
||||||
fun checkSignaturesAreValid() {
|
fun checkSignaturesAreValid() {
|
||||||
for (sig in sigs) {
|
for (sig in sigs) {
|
||||||
sig.verify(id.bytes)
|
sig.verify(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package net.corda.core.transactions
|
package net.corda.core.transactions
|
||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.DigitalSignature
|
|
||||||
import net.corda.core.crypto.MerkleTree
|
import net.corda.core.crypto.MerkleTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.crypto.keys
|
import net.corda.core.crypto.keys
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.Emoji
|
import net.corda.core.internal.Emoji
|
||||||
@ -172,7 +172,7 @@ data class WireTransaction(
|
|||||||
* @throws SignatureException if the signature didn't match the transaction contents.
|
* @throws SignatureException if the signature didn't match the transaction contents.
|
||||||
* @throws IllegalArgumentException if the signature key doesn't appear in any command.
|
* @throws IllegalArgumentException if the signature key doesn't appear in any command.
|
||||||
*/
|
*/
|
||||||
fun checkSignature(sig: DigitalSignature.WithKey) {
|
fun checkSignature(sig: TransactionSignature) {
|
||||||
require(commands.any { it.signers.any { sig.by in it.keys } }) { "Signature key doesn't match any command" }
|
require(commands.any { it.signers.any { sig.by in it.keys } }) { "Signature key doesn't match any command" }
|
||||||
sig.verify(id)
|
sig.verify(id)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.contracts
|
||||||
|
|
||||||
import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY
|
import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.composite.CompositeKey
|
import net.corda.core.crypto.composite.CompositeKey
|
||||||
import net.corda.core.crypto.generateKeyPair
|
|
||||||
import net.corda.core.crypto.sign
|
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
@ -19,8 +17,12 @@ import kotlin.test.assertNotEquals
|
|||||||
|
|
||||||
class TransactionTests : TestDependencyInjectionBase() {
|
class TransactionTests : TestDependencyInjectionBase() {
|
||||||
private fun makeSigned(wtx: WireTransaction, vararg keys: KeyPair, notarySig: Boolean = true): SignedTransaction {
|
private fun makeSigned(wtx: WireTransaction, vararg keys: KeyPair, notarySig: Boolean = true): SignedTransaction {
|
||||||
val keySigs = keys.map { it.sign(wtx.id.bytes) }
|
val keySigs = keys.map { it.sign(SignableData(wtx.id, SignatureMetadata(1, Crypto.findSignatureScheme(it.public).schemeNumberID))) }
|
||||||
val sigs = if (notarySig) keySigs + DUMMY_NOTARY_KEY.sign(wtx.id.bytes) else keySigs
|
val sigs = if (notarySig) {
|
||||||
|
keySigs + DUMMY_NOTARY_KEY.sign(SignableData(wtx.id, SignatureMetadata(1, Crypto.findSignatureScheme(DUMMY_NOTARY_KEY.public).schemeNumberID)))
|
||||||
|
} else {
|
||||||
|
keySigs
|
||||||
|
}
|
||||||
return SignedTransaction(wtx, sigs)
|
return SignedTransaction(wtx, sigs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,13 +36,16 @@ class CompositeKeyTests : TestDependencyInjectionBase() {
|
|||||||
val charliePublicKey: PublicKey = charlieKey.public
|
val charliePublicKey: PublicKey = charlieKey.public
|
||||||
|
|
||||||
val message = OpaqueBytes("Transaction".toByteArray())
|
val message = OpaqueBytes("Transaction".toByteArray())
|
||||||
|
val secureHash = message.sha256()
|
||||||
|
|
||||||
val aliceSignature = aliceKey.sign(message)
|
// By lazy is required so that the serialisers are configured before vals initialisation takes place (they internally invoke serialise).
|
||||||
val bobSignature = bobKey.sign(message)
|
val aliceSignature by lazy { aliceKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(alicePublicKey).schemeNumberID))) }
|
||||||
val charlieSignature = charlieKey.sign(message)
|
val bobSignature by lazy { bobKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(bobPublicKey).schemeNumberID))) }
|
||||||
|
val charlieSignature by lazy { charlieKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(charliePublicKey).schemeNumberID))) }
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `(Alice) fulfilled by Alice signature`() {
|
fun `(Alice) fulfilled by Alice signature`() {
|
||||||
|
println(aliceKey.serialize().hash)
|
||||||
assertTrue { alicePublicKey.isFulfilledBy(aliceSignature.by) }
|
assertTrue { alicePublicKey.isFulfilledBy(aliceSignature.by) }
|
||||||
assertFalse { alicePublicKey.isFulfilledBy(charlieSignature.by) }
|
assertFalse { alicePublicKey.isFulfilledBy(charlieSignature.by) }
|
||||||
}
|
}
|
||||||
@ -153,11 +156,12 @@ class CompositeKeyTests : TestDependencyInjectionBase() {
|
|||||||
* Check that verifying a composite signature using the [CompositeSignature] engine works.
|
* Check that verifying a composite signature using the [CompositeSignature] engine works.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun `composite signature verification`() {
|
fun `composite TransactionSignature verification `() {
|
||||||
val twoOfThree = CompositeKey.Builder().addKeys(alicePublicKey, bobPublicKey, charliePublicKey).build(threshold = 2)
|
val twoOfThree = CompositeKey.Builder().addKeys(alicePublicKey, bobPublicKey, charliePublicKey).build(threshold = 2)
|
||||||
|
|
||||||
val engine = CompositeSignature()
|
val engine = CompositeSignature()
|
||||||
engine.initVerify(twoOfThree)
|
engine.initVerify(twoOfThree)
|
||||||
engine.update(message.bytes)
|
engine.update(secureHash.bytes)
|
||||||
|
|
||||||
assertFalse { engine.verify(CompositeSignaturesWithKeys(listOf(aliceSignature)).serialize().bytes) }
|
assertFalse { engine.verify(CompositeSignaturesWithKeys(listOf(aliceSignature)).serialize().bytes) }
|
||||||
assertFalse { engine.verify(CompositeSignaturesWithKeys(listOf(bobSignature)).serialize().bytes) }
|
assertFalse { engine.verify(CompositeSignaturesWithKeys(listOf(bobSignature)).serialize().bytes) }
|
||||||
@ -168,7 +172,7 @@ class CompositeKeyTests : TestDependencyInjectionBase() {
|
|||||||
assertTrue { engine.verify(CompositeSignaturesWithKeys(listOf(aliceSignature, bobSignature, charlieSignature)).serialize().bytes) }
|
assertTrue { engine.verify(CompositeSignaturesWithKeys(listOf(aliceSignature, bobSignature, charlieSignature)).serialize().bytes) }
|
||||||
|
|
||||||
// Check the underlying signature is validated
|
// Check the underlying signature is validated
|
||||||
val brokenBobSignature = DigitalSignature.WithKey(bobSignature.by, aliceSignature.bytes)
|
val brokenBobSignature = TransactionSignature(aliceSignature.bytes, bobSignature.by, SignatureMetadata(1, Crypto.findSignatureScheme(bobSignature.by).schemeNumberID))
|
||||||
assertFalse { engine.verify(CompositeSignaturesWithKeys(listOf(aliceSignature, brokenBobSignature)).serialize().bytes) }
|
assertFalse { engine.verify(CompositeSignaturesWithKeys(listOf(aliceSignature, brokenBobSignature)).serialize().bytes) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,19 +286,19 @@ class CompositeKeyTests : TestDependencyInjectionBase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `CompositeKey from multiple signature schemes and signature verification`() {
|
fun `CompositeKey from multiple signature schemes and signature verification`() {
|
||||||
val (privRSA, pubRSA) = Crypto.generateKeyPair(Crypto.RSA_SHA256)
|
val keyPairRSA = Crypto.generateKeyPair(Crypto.RSA_SHA256)
|
||||||
val (privK1, pubK1) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
||||||
val (privR1, pubR1) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||||
val (privEd, pubEd) = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
|
val keyPairEd = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
|
||||||
val (privSP, pubSP) = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
|
val keyPairSP = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
|
||||||
|
|
||||||
val RSASignature = privRSA.sign(message.bytes, pubRSA)
|
val RSASignature = keyPairRSA.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairRSA.public).schemeNumberID)))
|
||||||
val K1Signature = privK1.sign(message.bytes, pubK1)
|
val K1Signature = keyPairK1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairK1.public).schemeNumberID)))
|
||||||
val R1Signature = privR1.sign(message.bytes, pubR1)
|
val R1Signature = keyPairR1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairR1.public).schemeNumberID)))
|
||||||
val EdSignature = privEd.sign(message.bytes, pubEd)
|
val EdSignature = keyPairEd.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairEd.public).schemeNumberID)))
|
||||||
val SPSignature = privSP.sign(message.bytes, pubSP)
|
val SPSignature = keyPairSP.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairSP.public).schemeNumberID)))
|
||||||
|
|
||||||
val compositeKey = CompositeKey.Builder().addKeys(pubRSA, pubK1, pubR1, pubEd, pubSP).build() as CompositeKey
|
val compositeKey = CompositeKey.Builder().addKeys(keyPairRSA.public, keyPairK1.public, keyPairR1.public, keyPairEd.public, keyPairSP.public).build() as CompositeKey
|
||||||
|
|
||||||
val signatures = listOf(RSASignature, K1Signature, R1Signature, EdSignature, SPSignature)
|
val signatures = listOf(RSASignature, K1Signature, R1Signature, EdSignature, SPSignature)
|
||||||
assertTrue { compositeKey.isFulfilledBy(signatures.byKeys()) }
|
assertTrue { compositeKey.isFulfilledBy(signatures.byKeys()) }
|
||||||
@ -307,19 +311,19 @@ class CompositeKeyTests : TestDependencyInjectionBase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `Test save to keystore`() {
|
fun `Test save to keystore`() {
|
||||||
// From test case [CompositeKey from multiple signature schemes and signature verification]
|
// From test case [CompositeKey from multiple signature schemes and signature verification]
|
||||||
val (privRSA, pubRSA) = Crypto.generateKeyPair(Crypto.RSA_SHA256)
|
val keyPairRSA = Crypto.generateKeyPair(Crypto.RSA_SHA256)
|
||||||
val (privK1, pubK1) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
||||||
val (privR1, pubR1) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||||
val (privEd, pubEd) = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
|
val keyPairEd = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
|
||||||
val (privSP, pubSP) = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
|
val keyPairSP = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
|
||||||
|
|
||||||
val RSASignature = privRSA.sign(message.bytes, pubRSA)
|
val RSASignature = keyPairRSA.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairRSA.public).schemeNumberID)))
|
||||||
val K1Signature = privK1.sign(message.bytes, pubK1)
|
val K1Signature = keyPairK1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairK1.public).schemeNumberID)))
|
||||||
val R1Signature = privR1.sign(message.bytes, pubR1)
|
val R1Signature = keyPairR1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairR1.public).schemeNumberID)))
|
||||||
val EdSignature = privEd.sign(message.bytes, pubEd)
|
val EdSignature = keyPairEd.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairEd.public).schemeNumberID)))
|
||||||
val SPSignature = privSP.sign(message.bytes, pubSP)
|
val SPSignature = keyPairSP.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairSP.public).schemeNumberID)))
|
||||||
|
|
||||||
val compositeKey = CompositeKey.Builder().addKeys(pubRSA, pubK1, pubR1, pubEd, pubSP).build() as CompositeKey
|
val compositeKey = CompositeKey.Builder().addKeys(keyPairRSA.public, keyPairK1.public, keyPairR1.public, keyPairEd.public, keyPairSP.public).build() as CompositeKey
|
||||||
|
|
||||||
val signatures = listOf(RSASignature, K1Signature, R1Signature, EdSignature, SPSignature)
|
val signatures = listOf(RSASignature, K1Signature, R1Signature, EdSignature, SPSignature)
|
||||||
assertTrue { compositeKey.isFulfilledBy(signatures.byKeys()) }
|
assertTrue { compositeKey.isFulfilledBy(signatures.byKeys()) }
|
||||||
|
@ -3,73 +3,39 @@ package net.corda.core.crypto
|
|||||||
import net.corda.testing.TestDependencyInjectionBase
|
import net.corda.testing.TestDependencyInjectionBase
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.SignatureException
|
import java.security.SignatureException
|
||||||
import java.time.Instant
|
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Digital signature MetaData tests
|
* Digital signature MetaData tests.
|
||||||
*/
|
*/
|
||||||
class TransactionSignatureTest : TestDependencyInjectionBase() {
|
class TransactionSignatureTest : TestDependencyInjectionBase() {
|
||||||
|
|
||||||
val testBytes = "12345678901234567890123456789012".toByteArray()
|
val testBytes = "12345678901234567890123456789012".toByteArray()
|
||||||
|
|
||||||
/** valid sign and verify. */
|
/** Valid sign and verify. */
|
||||||
@Test
|
@Test
|
||||||
fun `MetaData Full sign and verify`() {
|
fun `Signature metadata full sign and verify`() {
|
||||||
val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
|
val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
|
||||||
|
|
||||||
// create a MetaData.Full object
|
// Create a SignableData object.
|
||||||
val meta = MetaData("ECDSA_SECP256K1_SHA256", "M9", SignatureType.FULL, Instant.now(), null, null, testBytes, keyPair.public)
|
val signableData = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(keyPair.public).schemeNumberID))
|
||||||
|
|
||||||
// sign the message
|
// Sign the meta object.
|
||||||
val transactionSignature: TransactionSignature = keyPair.private.sign(meta)
|
val transactionSignature: TransactionSignature = keyPair.sign(signableData)
|
||||||
|
|
||||||
// check auto-verification
|
// Check auto-verification.
|
||||||
assertTrue(transactionSignature.verify())
|
assertTrue(transactionSignature.verify(testBytes.sha256()))
|
||||||
|
|
||||||
// check manual verification
|
// Check manual verification.
|
||||||
assertTrue(keyPair.public.verify(transactionSignature))
|
assertTrue(Crypto.doVerify(testBytes.sha256(), transactionSignature))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Signing should fail, as I sign with a secpK1 key, but set schemeCodeName is set to secpR1. */
|
/** Verification should fail; corrupted metadata - clearData (Merkle root) has changed. */
|
||||||
@Test(expected = IllegalArgumentException::class)
|
@Test(expected = SignatureException::class)
|
||||||
fun `MetaData Full failure wrong scheme`() {
|
fun `Signature metadata full failure clearData has changed`() {
|
||||||
val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
|
val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
|
||||||
val meta = MetaData("ECDSA_SECP256R1_SHA256", "M9", SignatureType.FULL, Instant.now(), null, null, testBytes, keyPair.public)
|
val signableData = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(keyPair.public).schemeNumberID))
|
||||||
keyPair.private.sign(meta)
|
val transactionSignature = keyPair.sign(signableData)
|
||||||
}
|
Crypto.doVerify((testBytes + testBytes).sha256(), transactionSignature)
|
||||||
|
|
||||||
/** Verification should fail; corrupted metadata - public key has changed. */
|
|
||||||
@Test(expected = SignatureException::class)
|
|
||||||
fun `MetaData Full failure public key has changed`() {
|
|
||||||
val keyPair1 = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
|
|
||||||
val keyPair2 = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
|
|
||||||
val meta = MetaData("ECDSA_SECP256K1_SHA256", "M9", SignatureType.FULL, Instant.now(), null, null, testBytes, keyPair2.public)
|
|
||||||
val transactionSignature = keyPair1.private.sign(meta)
|
|
||||||
transactionSignature.verify()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Verification should fail; corrupted metadata - clearData has changed. */
|
|
||||||
@Test(expected = SignatureException::class)
|
|
||||||
fun `MetaData Full failure clearData has changed`() {
|
|
||||||
val keyPair1 = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
|
|
||||||
val meta = MetaData("ECDSA_SECP256K1_SHA256", "M9", SignatureType.FULL, Instant.now(), null, null, testBytes, keyPair1.public)
|
|
||||||
val transactionSignature = keyPair1.private.sign(meta)
|
|
||||||
|
|
||||||
val meta2 = MetaData("ECDSA_SECP256K1_SHA256", "M9", SignatureType.FULL, Instant.now(), null, null, testBytes.plus(testBytes), keyPair1.public)
|
|
||||||
val transactionSignature2 = TransactionSignature(transactionSignature.signatureData, meta2)
|
|
||||||
keyPair1.public.verify(transactionSignature2)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Verification should fail; corrupted metadata - schemeCodeName has changed from K1 to R1. */
|
|
||||||
@Test(expected = SignatureException::class)
|
|
||||||
fun `MetaData Wrong schemeCodeName has changed`() {
|
|
||||||
val keyPair1 = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
|
|
||||||
val meta = MetaData("ECDSA_SECP256K1_SHA256", "M9", SignatureType.FULL, Instant.now(), null, null, testBytes, keyPair1.public)
|
|
||||||
val transactionSignature = keyPair1.private.sign(meta)
|
|
||||||
|
|
||||||
val meta2 = MetaData("ECDSA_SECP256R1_SHA256", "M9", SignatureType.FULL, Instant.now(), null, null, testBytes.plus(testBytes), keyPair1.public)
|
|
||||||
val transactionSignature2 = TransactionSignature(transactionSignature.signatureData, meta2)
|
|
||||||
keyPair1.public.verify(transactionSignature2)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import net.corda.nodeapi.serialization.KryoHeaderV0_1
|
|||||||
import net.corda.nodeapi.serialization.SerializationContextImpl
|
import net.corda.nodeapi.serialization.SerializationContextImpl
|
||||||
import net.corda.nodeapi.serialization.SerializationFactoryImpl
|
import net.corda.nodeapi.serialization.SerializationFactoryImpl
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
|
import net.corda.testing.ALICE_PUBKEY
|
||||||
import net.corda.testing.BOB
|
import net.corda.testing.BOB
|
||||||
import net.corda.testing.BOB_PUBKEY
|
import net.corda.testing.BOB_PUBKEY
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
@ -117,16 +118,13 @@ class KryoTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `serialize - deserialize MetaData`() {
|
fun `serialize - deserialize SignableData`() {
|
||||||
val testString = "Hello World"
|
val testString = "Hello World"
|
||||||
val testBytes = testString.toByteArray()
|
val testBytes = testString.toByteArray()
|
||||||
val keyPair1 = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
|
|
||||||
val bitSet = java.util.BitSet(10)
|
|
||||||
bitSet.set(3)
|
|
||||||
|
|
||||||
val meta = MetaData("ECDSA_SECP256K1_SHA256", "M9", SignatureType.FULL, Instant.now(), bitSet, bitSet, testBytes, keyPair1.public)
|
val meta = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(ALICE_PUBKEY).schemeNumberID))
|
||||||
val serializedMetaData = meta.serialize(factory, context).bytes
|
val serializedMetaData = meta.serialize(factory, context).bytes
|
||||||
val meta2 = serializedMetaData.deserialize<MetaData>(factory, context)
|
val meta2 = serializedMetaData.deserialize<SignableData>(factory, context)
|
||||||
assertEquals(meta2, meta)
|
assertEquals(meta2, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ Where:
|
|||||||
* ``outputs`` is a list of the transaction's outputs'
|
* ``outputs`` is a list of the transaction's outputs'
|
||||||
* ``attachments`` is a list of the transaction's attachments'
|
* ``attachments`` is a list of the transaction's attachments'
|
||||||
* ``commands`` is a list of the transaction's commands, and their associated signatures'
|
* ``commands`` is a list of the transaction's commands, and their associated signatures'
|
||||||
* ``id`` is the transaction's merkle root hash'
|
* ``id`` is the transaction's Merkle root hash'
|
||||||
* ``notary`` is the transaction's notary. If there are inputs these must have the same notary on their source transactions.
|
* ``notary`` is the transaction's notary. If there are inputs these must have the same notary on their source transactions.
|
||||||
* ``timeWindow`` is the transaction's timestamp and defines the acceptable delay for notarisation.
|
* ``timeWindow`` is the transaction's timestamp and defines the acceptable delay for notarisation.
|
||||||
|
|
||||||
|
@ -5,8 +5,8 @@ import com.google.common.collect.ImmutableList;
|
|||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import net.corda.contracts.asset.Cash;
|
import net.corda.contracts.asset.Cash;
|
||||||
import net.corda.core.contracts.*;
|
import net.corda.core.contracts.*;
|
||||||
import net.corda.core.crypto.DigitalSignature;
|
|
||||||
import net.corda.core.crypto.SecureHash;
|
import net.corda.core.crypto.SecureHash;
|
||||||
|
import net.corda.core.crypto.TransactionSignature;
|
||||||
import net.corda.core.flows.*;
|
import net.corda.core.flows.*;
|
||||||
import net.corda.core.identity.Party;
|
import net.corda.core.identity.Party;
|
||||||
import net.corda.core.internal.FetchDataFlow;
|
import net.corda.core.internal.FetchDataFlow;
|
||||||
@ -30,10 +30,8 @@ import java.security.PublicKey;
|
|||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static net.corda.core.contracts.ContractsDSL.requireThat;
|
import static net.corda.core.contracts.ContractsDSL.requireThat;
|
||||||
import static net.corda.testing.TestConstants.getDUMMY_PUBKEY_1;
|
import static net.corda.testing.TestConstants.getDUMMY_PUBKEY_1;
|
||||||
|
|
||||||
@ -383,11 +381,11 @@ public class FlowCookbookJava {
|
|||||||
// node does not need to check we haven't changed anything in the
|
// node does not need to check we haven't changed anything in the
|
||||||
// transaction.
|
// transaction.
|
||||||
// DOCSTART 40
|
// DOCSTART 40
|
||||||
DigitalSignature.WithKey sig = getServiceHub().createSignature(onceSignedTx);
|
TransactionSignature sig = getServiceHub().createSignature(onceSignedTx);
|
||||||
// DOCEND 40
|
// DOCEND 40
|
||||||
// And again, if we wanted to use a different public key:
|
// And again, if we wanted to use a different public key:
|
||||||
// DOCSTART 41
|
// DOCSTART 41
|
||||||
DigitalSignature.WithKey sig2 = getServiceHub().createSignature(onceSignedTx, otherKey2);
|
TransactionSignature sig2 = getServiceHub().createSignature(onceSignedTx, otherKey2);
|
||||||
// DOCEND 41
|
// DOCEND 41
|
||||||
|
|
||||||
/*----------------------------
|
/*----------------------------
|
||||||
|
@ -5,8 +5,8 @@ package net.corda.docs
|
|||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.contracts.asset.Cash
|
import net.corda.contracts.asset.Cash
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.DigitalSignature
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.flows.*
|
import net.corda.core.flows.*
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.FetchDataFlow
|
import net.corda.core.internal.FetchDataFlow
|
||||||
@ -363,11 +363,11 @@ object FlowCookbook {
|
|||||||
// node does not need to check we haven't changed anything in the
|
// node does not need to check we haven't changed anything in the
|
||||||
// transaction.
|
// transaction.
|
||||||
// DOCSTART 40
|
// DOCSTART 40
|
||||||
val sig: DigitalSignature.WithKey = serviceHub.createSignature(onceSignedTx)
|
val sig: TransactionSignature = serviceHub.createSignature(onceSignedTx)
|
||||||
// DOCEND 40
|
// DOCEND 40
|
||||||
// And again, if we wanted to use a different public key:
|
// And again, if we wanted to use a different public key:
|
||||||
// DOCSTART 41
|
// DOCSTART 41
|
||||||
val sig2: DigitalSignature.WithKey = serviceHub.createSignature(onceSignedTx, otherKey2)
|
val sig2: TransactionSignature = serviceHub.createSignature(onceSignedTx, otherKey2)
|
||||||
// DOCEND 41
|
// DOCEND 41
|
||||||
|
|
||||||
// In practice, however, the process of gathering every signature
|
// In practice, however, the process of gathering every signature
|
||||||
|
@ -5,8 +5,11 @@ import net.corda.contracts.asset.Cash
|
|||||||
import net.corda.core.contracts.Amount
|
import net.corda.core.contracts.Amount
|
||||||
import net.corda.core.contracts.Issued
|
import net.corda.core.contracts.Issued
|
||||||
import net.corda.core.contracts.StateAndRef
|
import net.corda.core.contracts.StateAndRef
|
||||||
import net.corda.core.crypto.DigitalSignature
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.TransactionSignature
|
||||||
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.flows.InitiatedBy
|
||||||
|
import net.corda.core.flows.InitiatingFlow
|
||||||
import net.corda.core.flows.*
|
import net.corda.core.flows.*
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
@ -154,7 +157,7 @@ class ForeignExchangeFlow(val tradeId: String,
|
|||||||
// pass transaction details to the counterparty to revalidate and confirm with a signature
|
// pass transaction details to the counterparty to revalidate and confirm with a signature
|
||||||
// Allow otherParty to access our data to resolve the transaction.
|
// Allow otherParty to access our data to resolve the transaction.
|
||||||
subFlow(SendTransactionFlow(remoteRequestWithNotary.owner, signedTransaction))
|
subFlow(SendTransactionFlow(remoteRequestWithNotary.owner, signedTransaction))
|
||||||
val allPartySignedTx = receive<DigitalSignature.WithKey>(remoteRequestWithNotary.owner).unwrap {
|
val allPartySignedTx = receive<TransactionSignature>(remoteRequestWithNotary.owner).unwrap {
|
||||||
val withNewSignature = signedTransaction + it
|
val withNewSignature = signedTransaction + it
|
||||||
// check all signatures are present except the notary
|
// check all signatures are present except the notary
|
||||||
withNewSignature.verifySignaturesExcept(withNewSignature.tx.notary!!.owningKey)
|
withNewSignature.verifySignaturesExcept(withNewSignature.tx.notary!!.owningKey)
|
||||||
|
@ -2,8 +2,8 @@ package net.corda.docs
|
|||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.DigitalSignature
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.crypto.containsAny
|
import net.corda.core.crypto.containsAny
|
||||||
import net.corda.core.flows.FinalityFlow
|
import net.corda.core.flows.FinalityFlow
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
@ -188,7 +188,7 @@ class SubmitCompletionFlow(val ref: StateRef, val verdict: WorkflowState) : Flow
|
|||||||
val selfSignedTx = serviceHub.signInitialTransaction(tx)
|
val selfSignedTx = serviceHub.signInitialTransaction(tx)
|
||||||
//DOCEND 2
|
//DOCEND 2
|
||||||
// Send the signed transaction to the originator and await their signature to confirm
|
// Send the signed transaction to the originator and await their signature to confirm
|
||||||
val allPartySignedTx = sendAndReceive<DigitalSignature.WithKey>(newState.source, selfSignedTx).unwrap {
|
val allPartySignedTx = sendAndReceive<TransactionSignature>(newState.source, selfSignedTx).unwrap {
|
||||||
// Add their signature to our unmodified transaction. To check they signed the same tx.
|
// Add their signature to our unmodified transaction. To check they signed the same tx.
|
||||||
val agreedTx = selfSignedTx + it
|
val agreedTx = selfSignedTx + it
|
||||||
// Receive back their signature and confirm that it is for an unmodified transaction
|
// Receive back their signature and confirm that it is for an unmodified transaction
|
||||||
|
@ -112,7 +112,7 @@ Here is an extract from the ``NodeInterestRates.Oracle`` class and supporting ty
|
|||||||
class Oracle {
|
class Oracle {
|
||||||
fun query(queries: List<FixOf>, deadline: Instant): List<Fix>
|
fun query(queries: List<FixOf>, deadline: Instant): List<Fix>
|
||||||
|
|
||||||
fun sign(ftx: FilteredTransaction, merkleRoot: SecureHash): DigitalSignature.WithKey
|
fun sign(ftx: FilteredTransaction, txId: SecureHash): DigitalSignature.WithKey
|
||||||
}
|
}
|
||||||
|
|
||||||
Because the fix contains a timestamp (the ``forDay`` field), that identifies the version of the data being requested,
|
Because the fix contains a timestamp (the ``forDay`` field), that identifies the version of the data being requested,
|
||||||
|
@ -8,7 +8,7 @@ import net.corda.contracts.asset.Cash
|
|||||||
import net.corda.core.contracts.Command
|
import net.corda.core.contracts.Command
|
||||||
import net.corda.core.contracts.CommandData
|
import net.corda.core.contracts.CommandData
|
||||||
import net.corda.core.contracts.ContractState
|
import net.corda.core.contracts.ContractState
|
||||||
import net.corda.core.crypto.testing.NullSignature
|
import net.corda.core.crypto.testing.NULL_SIGNATURE
|
||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.testing.*
|
import net.corda.core.testing.*
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
@ -82,7 +82,7 @@ class SignedTransactionGenerator : Generator<SignedTransaction>(SignedTransactio
|
|||||||
val wireTransaction = WiredTransactionGenerator().generate(random, status)
|
val wireTransaction = WiredTransactionGenerator().generate(random, status)
|
||||||
return SignedTransaction(
|
return SignedTransaction(
|
||||||
ctx = wireTransaction,
|
ctx = wireTransaction,
|
||||||
sigs = listOf(NullSignature)
|
sigs = listOf(NULL_SIGNATURE)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
package net.corda.node.services.keys
|
package net.corda.node.services.keys
|
||||||
|
|
||||||
import net.corda.core.internal.ThreadBox
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.DigitalSignature
|
|
||||||
import net.corda.core.crypto.generateKeyPair
|
|
||||||
import net.corda.core.crypto.keys
|
|
||||||
import net.corda.core.crypto.sign
|
|
||||||
import net.corda.core.identity.AnonymousPartyAndPath
|
import net.corda.core.identity.AnonymousPartyAndPath
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
|
import net.corda.core.internal.ThreadBox
|
||||||
import net.corda.core.node.services.IdentityService
|
import net.corda.core.node.services.IdentityService
|
||||||
import net.corda.core.node.services.KeyManagementService
|
import net.corda.core.node.services.KeyManagementService
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
@ -75,7 +72,13 @@ class E2ETestKeyManagementService(val identityService: IdentityService,
|
|||||||
|
|
||||||
override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
|
override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
|
||||||
val keyPair = getSigningKeyPair(publicKey)
|
val keyPair = getSigningKeyPair(publicKey)
|
||||||
val signature = keyPair.sign(bytes)
|
return keyPair.sign(bytes)
|
||||||
return signature
|
}
|
||||||
|
|
||||||
|
// TODO: A full KeyManagementService implementation needs to record activity to the Audit Service and to limit
|
||||||
|
// signing to appropriately authorised contexts and initiating users.
|
||||||
|
override fun sign(signableData: SignableData, publicKey: PublicKey): TransactionSignature {
|
||||||
|
val keyPair = getSigningKeyPair(publicKey)
|
||||||
|
return keyPair.sign(signableData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
package net.corda.node.services.keys
|
package net.corda.node.services.keys
|
||||||
|
|
||||||
import net.corda.core.internal.ThreadBox
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.DigitalSignature
|
|
||||||
import net.corda.core.crypto.generateKeyPair
|
|
||||||
import net.corda.core.crypto.keys
|
|
||||||
import net.corda.core.crypto.sign
|
|
||||||
import net.corda.core.identity.AnonymousPartyAndPath
|
import net.corda.core.identity.AnonymousPartyAndPath
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
|
import net.corda.core.internal.ThreadBox
|
||||||
import net.corda.core.node.services.IdentityService
|
import net.corda.core.node.services.IdentityService
|
||||||
import net.corda.core.node.services.KeyManagementService
|
import net.corda.core.node.services.KeyManagementService
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
@ -86,8 +83,13 @@ class PersistentKeyManagementService(val identityService: IdentityService,
|
|||||||
|
|
||||||
override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
|
override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
|
||||||
val keyPair = getSigningKeyPair(publicKey)
|
val keyPair = getSigningKeyPair(publicKey)
|
||||||
val signature = keyPair.sign(bytes)
|
return keyPair.sign(bytes)
|
||||||
return signature
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: A full KeyManagementService implementation needs to record activity to the Audit Service and to limit
|
||||||
|
// signing to appropriately authorised contexts and initiating users.
|
||||||
|
override fun sign(signableData: SignableData, publicKey: PublicKey): TransactionSignature {
|
||||||
|
val keyPair = getSigningKeyPair(publicKey)
|
||||||
|
return keyPair.sign(signableData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,10 @@ package net.corda.node.services.transactions
|
|||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import com.google.common.util.concurrent.SettableFuture
|
import com.google.common.util.concurrent.SettableFuture
|
||||||
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.DigitalSignature
|
||||||
|
import net.corda.core.crypto.SignableData
|
||||||
|
import net.corda.core.crypto.SignatureMetadata
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.NotaryException
|
import net.corda.core.flows.NotaryException
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
@ -109,7 +112,8 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, c
|
|||||||
commitInputStates(inputs, id, callerIdentity)
|
commitInputStates(inputs, id, callerIdentity)
|
||||||
|
|
||||||
log.debug { "Inputs committed successfully, signing $id" }
|
log.debug { "Inputs committed successfully, signing $id" }
|
||||||
val sig = sign(id.bytes)
|
val signableData = SignableData(id, SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(services.notaryIdentityKey).schemeNumberID))
|
||||||
|
val sig = sign(signableData)
|
||||||
BFTSMaRt.ReplicaResponse.Signature(sig)
|
BFTSMaRt.ReplicaResponse.Signature(sig)
|
||||||
} catch (e: NotaryException) {
|
} catch (e: NotaryException) {
|
||||||
log.debug { "Error processing transaction: ${e.error}" }
|
log.debug { "Error processing transaction: ${e.error}" }
|
||||||
|
@ -14,21 +14,18 @@ import bftsmart.tom.server.defaultservices.DefaultReplier
|
|||||||
import bftsmart.tom.util.Extractor
|
import bftsmart.tom.util.Extractor
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.contracts.TimeWindow
|
import net.corda.core.contracts.TimeWindow
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.SecureHash
|
|
||||||
import net.corda.core.crypto.SignedData
|
|
||||||
import net.corda.core.crypto.sign
|
|
||||||
import net.corda.core.flows.NotaryError
|
import net.corda.core.flows.NotaryError
|
||||||
import net.corda.core.flows.NotaryException
|
import net.corda.core.flows.NotaryException
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.declaredField
|
import net.corda.core.internal.declaredField
|
||||||
|
import net.corda.core.internal.toTypedArray
|
||||||
import net.corda.core.node.services.TimeWindowChecker
|
import net.corda.core.node.services.TimeWindowChecker
|
||||||
import net.corda.core.node.services.UniquenessProvider
|
import net.corda.core.node.services.UniquenessProvider
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.internal.toTypedArray
|
|
||||||
import net.corda.core.transactions.FilteredTransaction
|
import net.corda.core.transactions.FilteredTransaction
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.utilities.debug
|
import net.corda.core.utilities.debug
|
||||||
@ -176,7 +173,7 @@ object BFTSMaRt {
|
|||||||
abstract class Replica(config: BFTSMaRtConfig,
|
abstract class Replica(config: BFTSMaRtConfig,
|
||||||
replicaId: Int,
|
replicaId: Int,
|
||||||
tableName: String,
|
tableName: String,
|
||||||
private val services: ServiceHubInternal,
|
protected val services: ServiceHubInternal,
|
||||||
private val timeWindowChecker: TimeWindowChecker) : DefaultRecoverable() {
|
private val timeWindowChecker: TimeWindowChecker) : DefaultRecoverable() {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = loggerFor<Replica>()
|
private val log = loggerFor<Replica>()
|
||||||
@ -253,6 +250,10 @@ object BFTSMaRt {
|
|||||||
return services.database.transaction { services.keyManagementService.sign(bytes, services.notaryIdentityKey) }
|
return services.database.transaction { services.keyManagementService.sign(bytes, services.notaryIdentityKey) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun sign(signableData: SignableData): TransactionSignature {
|
||||||
|
return services.database.transaction { services.keyManagementService.sign(signableData, services.notaryIdentityKey) }
|
||||||
|
}
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// - Test snapshot functionality with different bft-smart cluster configurations.
|
// - Test snapshot functionality with different bft-smart cluster configurations.
|
||||||
// - Add streaming to support large data sets.
|
// - Add streaming to support large data sets.
|
||||||
|
@ -8,8 +8,7 @@ import net.corda.contracts.asset.Cash
|
|||||||
import net.corda.contracts.asset.`issued by`
|
import net.corda.contracts.asset.`issued by`
|
||||||
import net.corda.contracts.asset.`owned by`
|
import net.corda.contracts.asset.`owned by`
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.SecureHash
|
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.InitiatedBy
|
import net.corda.core.flows.InitiatedBy
|
||||||
import net.corda.core.flows.InitiatingFlow
|
import net.corda.core.flows.InitiatingFlow
|
||||||
@ -604,11 +603,11 @@ class TwoPartyTradeFlowTests {
|
|||||||
|
|
||||||
val signed = wtxToSign.map {
|
val signed = wtxToSign.map {
|
||||||
val id = it.id
|
val id = it.id
|
||||||
val sigs = mutableListOf<DigitalSignature.WithKey>()
|
val sigs = mutableListOf<TransactionSignature>()
|
||||||
sigs.add(node.services.keyManagementService.sign(id.bytes, node.services.legalIdentityKey))
|
sigs.add(node.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(node.services.legalIdentityKey).schemeNumberID)), node.services.legalIdentityKey))
|
||||||
sigs.add(notaryNode.services.keyManagementService.sign(id.bytes, notaryNode.services.notaryIdentityKey))
|
sigs.add(notaryNode.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(notaryNode.services.notaryIdentityKey).schemeNumberID)), notaryNode.services.notaryIdentityKey))
|
||||||
extraSigningNodes.forEach { currentNode ->
|
extraSigningNodes.forEach { currentNode ->
|
||||||
sigs.add(currentNode.services.keyManagementService.sign(id.bytes, currentNode.info.legalIdentity.owningKey))
|
sigs.add(currentNode.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(currentNode.info.legalIdentity.owningKey).schemeNumberID)), currentNode.info.legalIdentity.owningKey))
|
||||||
}
|
}
|
||||||
SignedTransaction(it, sigs)
|
SignedTransaction(it, sigs)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import net.corda.core.flows.FlowLogic
|
|||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.services.*
|
import net.corda.core.node.services.*
|
||||||
import net.corda.core.serialization.SerializeAsToken
|
import net.corda.core.serialization.SerializeAsToken
|
||||||
|
import net.corda.core.utilities.NonEmptySet
|
||||||
import net.corda.node.internal.InitiatedFlowFactory
|
import net.corda.node.internal.InitiatedFlowFactory
|
||||||
import net.corda.node.serialization.NodeClock
|
import net.corda.node.serialization.NodeClock
|
||||||
import net.corda.node.services.api.*
|
import net.corda.node.services.api.*
|
||||||
@ -16,6 +17,8 @@ import net.corda.node.services.statemachine.FlowStateMachineImpl
|
|||||||
import net.corda.node.services.statemachine.StateMachineManager
|
import net.corda.node.services.statemachine.StateMachineManager
|
||||||
import net.corda.node.services.transactions.InMemoryTransactionVerifierService
|
import net.corda.node.services.transactions.InMemoryTransactionVerifierService
|
||||||
import net.corda.node.utilities.CordaPersistence
|
import net.corda.node.utilities.CordaPersistence
|
||||||
|
import net.corda.testing.DUMMY_IDENTITY_1
|
||||||
|
import net.corda.testing.MOCK_HOST_AND_PORT
|
||||||
import net.corda.testing.MOCK_IDENTITY_SERVICE
|
import net.corda.testing.MOCK_IDENTITY_SERVICE
|
||||||
import net.corda.testing.node.MockAttachmentStorage
|
import net.corda.testing.node.MockAttachmentStorage
|
||||||
import net.corda.testing.node.MockNetworkMapCache
|
import net.corda.testing.node.MockNetworkMapCache
|
||||||
@ -60,7 +63,7 @@ open class MockServiceHubInternal(
|
|||||||
override val clock: Clock
|
override val clock: Clock
|
||||||
get() = overrideClock ?: throw UnsupportedOperationException()
|
get() = overrideClock ?: throw UnsupportedOperationException()
|
||||||
override val myInfo: NodeInfo
|
override val myInfo: NodeInfo
|
||||||
get() = throw UnsupportedOperationException()
|
get() = NodeInfo(listOf(MOCK_HOST_AND_PORT), DUMMY_IDENTITY_1, NonEmptySet.of(DUMMY_IDENTITY_1), 1) // Required to get a dummy platformVersion when required for tests.
|
||||||
override val monitoringService: MonitoringService = MonitoringService(MetricRegistry())
|
override val monitoringService: MonitoringService = MonitoringService(MetricRegistry())
|
||||||
override val rpcFlows: List<Class<out FlowLogic<*>>>
|
override val rpcFlows: List<Class<out FlowLogic<*>>>
|
||||||
get() = throw UnsupportedOperationException()
|
get() = throw UnsupportedOperationException()
|
||||||
|
@ -4,10 +4,7 @@ import io.requery.Persistable
|
|||||||
import io.requery.kotlin.eq
|
import io.requery.kotlin.eq
|
||||||
import io.requery.sql.KotlinEntityDataStore
|
import io.requery.sql.KotlinEntityDataStore
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.SecureHash
|
|
||||||
import net.corda.core.crypto.testing.NullPublicKey
|
|
||||||
import net.corda.core.crypto.toBase58String
|
|
||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
@ -20,6 +17,7 @@ import net.corda.node.services.vault.schemas.requery.VaultSchema
|
|||||||
import net.corda.node.services.vault.schemas.requery.VaultStatesEntity
|
import net.corda.node.services.vault.schemas.requery.VaultStatesEntity
|
||||||
import net.corda.node.utilities.CordaPersistence
|
import net.corda.node.utilities.CordaPersistence
|
||||||
import net.corda.node.utilities.configureDatabase
|
import net.corda.node.utilities.configureDatabase
|
||||||
|
import net.corda.testing.ALICE_PUBKEY
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
import net.corda.testing.DUMMY_PUBKEY_1
|
import net.corda.testing.DUMMY_PUBKEY_1
|
||||||
import net.corda.testing.TestDependencyInjectionBase
|
import net.corda.testing.TestDependencyInjectionBase
|
||||||
@ -212,6 +210,6 @@ class RequeryConfigurationTest : TestDependencyInjectionBase() {
|
|||||||
notary = DUMMY_NOTARY,
|
notary = DUMMY_NOTARY,
|
||||||
timeWindow = null
|
timeWindow = null
|
||||||
)
|
)
|
||||||
return SignedTransaction(wtx, listOf(DigitalSignature.WithKey(NullPublicKey, ByteArray(1))))
|
return SignedTransaction(wtx, listOf(TransactionSignature(ByteArray(1), ALICE_PUBKEY, SignatureMetadata(1, Crypto.findSignatureScheme(ALICE_PUBKEY).schemeNumberID))))
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,7 +19,11 @@ import net.corda.node.services.persistence.DBCheckpointStorage
|
|||||||
import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl
|
import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl
|
||||||
import net.corda.node.services.statemachine.StateMachineManager
|
import net.corda.node.services.statemachine.StateMachineManager
|
||||||
import net.corda.node.services.vault.NodeVaultService
|
import net.corda.node.services.vault.NodeVaultService
|
||||||
import net.corda.node.utilities.*
|
import net.corda.node.utilities.AffinityExecutor
|
||||||
|
import net.corda.node.utilities.CordaPersistence
|
||||||
|
import net.corda.node.utilities.configureDatabase
|
||||||
|
import net.corda.testing.node.InMemoryMessagingNetwork
|
||||||
|
import net.corda.testing.node.MockKeyManagementService
|
||||||
import net.corda.testing.getTestX509Name
|
import net.corda.testing.getTestX509Name
|
||||||
import net.corda.testing.testNodeConfiguration
|
import net.corda.testing.testNodeConfiguration
|
||||||
import net.corda.testing.initialiseTestSerialization
|
import net.corda.testing.initialiseTestSerialization
|
||||||
@ -35,7 +39,6 @@ import java.nio.file.Paths
|
|||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
package net.corda.node.services.persistence
|
package net.corda.node.services.persistence
|
||||||
|
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.testing.NullPublicKey
|
import net.corda.core.crypto.SignatureMetadata
|
||||||
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.toFuture
|
import net.corda.core.toFuture
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||||
import net.corda.node.utilities.CordaPersistence
|
import net.corda.node.utilities.CordaPersistence
|
||||||
import net.corda.node.utilities.configureDatabase
|
import net.corda.node.utilities.configureDatabase
|
||||||
|
import net.corda.testing.ALICE_PUBKEY
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
import net.corda.testing.LogHelper
|
import net.corda.testing.LogHelper
|
||||||
import net.corda.testing.TestDependencyInjectionBase
|
import net.corda.testing.TestDependencyInjectionBase
|
||||||
@ -150,6 +152,6 @@ class DBTransactionStorageTests : TestDependencyInjectionBase() {
|
|||||||
notary = DUMMY_NOTARY,
|
notary = DUMMY_NOTARY,
|
||||||
timeWindow = null
|
timeWindow = null
|
||||||
)
|
)
|
||||||
return SignedTransaction(wtx, listOf(DigitalSignature.WithKey(NullPublicKey, ByteArray(1))))
|
return SignedTransaction(wtx, listOf(TransactionSignature(ByteArray(1), ALICE_PUBKEY, SignatureMetadata(1, Crypto.findSignatureScheme(ALICE_PUBKEY).schemeNumberID))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,15 @@ package net.corda.node.services.transactions
|
|||||||
import net.corda.core.concurrent.CordaFuture
|
import net.corda.core.concurrent.CordaFuture
|
||||||
import net.corda.core.contracts.StateAndRef
|
import net.corda.core.contracts.StateAndRef
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.flows.NotaryError
|
import net.corda.core.flows.NotaryError
|
||||||
import net.corda.core.flows.NotaryException
|
import net.corda.core.flows.NotaryException
|
||||||
import net.corda.core.flows.NotaryFlow
|
import net.corda.core.flows.NotaryFlow
|
||||||
import net.corda.core.node.services.ServiceInfo
|
import net.corda.core.node.services.ServiceInfo
|
||||||
import net.corda.core.utilities.seconds
|
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.utilities.getOrThrow
|
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
|
import net.corda.core.utilities.getOrThrow
|
||||||
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.node.internal.AbstractNode
|
import net.corda.node.internal.AbstractNode
|
||||||
import net.corda.node.services.network.NetworkMapService
|
import net.corda.node.services.network.NetworkMapService
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
@ -132,7 +132,7 @@ class NotaryServiceTests {
|
|||||||
notaryError.conflict.verified()
|
notaryError.conflict.verified()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun runNotaryClient(stx: SignedTransaction): CordaFuture<List<DigitalSignature.WithKey>> {
|
private fun runNotaryClient(stx: SignedTransaction): CordaFuture<List<TransactionSignature>> {
|
||||||
val flow = NotaryFlow.Client(stx)
|
val flow = NotaryFlow.Client(stx)
|
||||||
val future = clientNode.services.startFlow(flow).resultFuture
|
val future = clientNode.services.startFlow(flow).resultFuture
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
|
@ -4,7 +4,7 @@ import net.corda.core.concurrent.CordaFuture
|
|||||||
import net.corda.core.contracts.Command
|
import net.corda.core.contracts.Command
|
||||||
import net.corda.core.contracts.StateAndRef
|
import net.corda.core.contracts.StateAndRef
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.flows.NotaryError
|
import net.corda.core.flows.NotaryError
|
||||||
import net.corda.core.flows.NotaryException
|
import net.corda.core.flows.NotaryException
|
||||||
import net.corda.core.flows.NotaryFlow
|
import net.corda.core.flows.NotaryFlow
|
||||||
@ -85,7 +85,7 @@ class ValidatingNotaryServiceTests {
|
|||||||
assertEquals(setOf(expectedMissingKey), missingKeys)
|
assertEquals(setOf(expectedMissingKey), missingKeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun runClient(stx: SignedTransaction): CordaFuture<List<DigitalSignature.WithKey>> {
|
private fun runClient(stx: SignedTransaction): CordaFuture<List<TransactionSignature>> {
|
||||||
val flow = NotaryFlow.Client(stx)
|
val flow = NotaryFlow.Client(stx)
|
||||||
val future = clientNode.services.startFlow(flow).resultFuture
|
val future = clientNode.services.startFlow(flow).resultFuture
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
|
@ -5,7 +5,6 @@ import net.corda.contracts.asset.DUMMY_CASH_ISSUER
|
|||||||
import net.corda.contracts.getCashBalance
|
import net.corda.contracts.getCashBalance
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.generateKeyPair
|
import net.corda.core.crypto.generateKeyPair
|
||||||
import net.corda.core.crypto.sign
|
|
||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.node.services.StatesNotAvailableException
|
import net.corda.core.node.services.StatesNotAvailableException
|
||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
@ -509,14 +508,14 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
|||||||
val amount = Amount(1000, Issued(BOC.ref(1), GBP))
|
val amount = Amount(1000, Issued(BOC.ref(1), GBP))
|
||||||
|
|
||||||
// Issue some cash
|
// Issue some cash
|
||||||
val issueTx = TransactionBuilder(notary).apply {
|
val issueTxBuilder = TransactionBuilder(notary).apply {
|
||||||
Cash().generateIssue(this, amount, anonymousIdentity.party, notary)
|
Cash().generateIssue(this, amount, anonymousIdentity.party, notary)
|
||||||
}.toWireTransaction()
|
signWith(BOC_KEY)
|
||||||
|
}
|
||||||
// We need to record the issue transaction so inputs can be resolved for the notary change transaction
|
// We need to record the issue transaction so inputs can be resolved for the notary change transaction
|
||||||
val signedIssueTx = SignedTransaction(issueTx, listOf(BOC_KEY.sign(issueTx.id)))
|
services.validatedTransactions.addTransaction(issueTxBuilder.toSignedTransaction())
|
||||||
services.validatedTransactions.addTransaction(signedIssueTx)
|
|
||||||
|
|
||||||
|
val issueTx = issueTxBuilder.toWireTransaction()
|
||||||
val initialCashState = StateAndRef(issueTx.outputs.single(), StateRef(issueTx.id, 0))
|
val initialCashState = StateAndRef(issueTx.outputs.single(), StateRef(issueTx.id, 0))
|
||||||
|
|
||||||
// Change notary
|
// Change notary
|
||||||
|
@ -9,9 +9,7 @@ import net.corda.contracts.math.CubicSplineInterpolator
|
|||||||
import net.corda.contracts.math.Interpolator
|
import net.corda.contracts.math.Interpolator
|
||||||
import net.corda.contracts.math.InterpolatorFactory
|
import net.corda.contracts.math.InterpolatorFactory
|
||||||
import net.corda.core.contracts.Command
|
import net.corda.core.contracts.Command
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.MerkleTreeException
|
|
||||||
import net.corda.core.crypto.keys
|
|
||||||
import net.corda.core.flows.FlowException
|
import net.corda.core.flows.FlowException
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.InitiatedBy
|
import net.corda.core.flows.InitiatedBy
|
||||||
@ -146,7 +144,7 @@ object NodeInterestRates {
|
|||||||
// Oracle gets signing request for only some of them with a valid partial tree? We sign over a whole transaction.
|
// Oracle gets signing request for only some of them with a valid partial tree? We sign over a whole transaction.
|
||||||
// It will be fixed by adding partial signatures later.
|
// It will be fixed by adding partial signatures later.
|
||||||
// DOCSTART 1
|
// DOCSTART 1
|
||||||
fun sign(ftx: FilteredTransaction): DigitalSignature.WithKey {
|
fun sign(ftx: FilteredTransaction): TransactionSignature {
|
||||||
if (!ftx.verify()) {
|
if (!ftx.verify()) {
|
||||||
throw MerkleTreeException("Rate Fix Oracle: Couldn't verify partial Merkle tree.")
|
throw MerkleTreeException("Rate Fix Oracle: Couldn't verify partial Merkle tree.")
|
||||||
}
|
}
|
||||||
@ -177,8 +175,9 @@ object NodeInterestRates {
|
|||||||
// Note that we will happily sign an invalid transaction, as we are only being presented with a filtered
|
// Note that we will happily sign an invalid transaction, as we are only being presented with a filtered
|
||||||
// version so we can't resolve or check it ourselves. However, that doesn't matter much, as if we sign
|
// version so we can't resolve or check it ourselves. However, that doesn't matter much, as if we sign
|
||||||
// an invalid transaction the signature is worthless.
|
// an invalid transaction the signature is worthless.
|
||||||
val signature = services.keyManagementService.sign(ftx.rootHash.bytes, signingKey)
|
val signableData = SignableData(ftx.rootHash, SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(signingKey).schemeNumberID))
|
||||||
return DigitalSignature.WithKey(signingKey, signature.bytes)
|
val signature = services.keyManagementService.sign(signableData, signingKey)
|
||||||
|
return TransactionSignature(signature.bytes, signingKey, signableData.signatureMetadata)
|
||||||
}
|
}
|
||||||
// DOCEND 1
|
// DOCEND 1
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ package net.corda.irs.flows
|
|||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.contracts.Fix
|
import net.corda.contracts.Fix
|
||||||
import net.corda.contracts.FixOf
|
import net.corda.contracts.FixOf
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.crypto.isFulfilledBy
|
import net.corda.core.crypto.isFulfilledBy
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.InitiatingFlow
|
import net.corda.core.flows.InitiatingFlow
|
||||||
@ -112,10 +112,10 @@ open class RatesFixFlow(protected val tx: TransactionBuilder,
|
|||||||
|
|
||||||
@InitiatingFlow
|
@InitiatingFlow
|
||||||
class FixSignFlow(val tx: TransactionBuilder, val oracle: Party,
|
class FixSignFlow(val tx: TransactionBuilder, val oracle: Party,
|
||||||
val partialMerkleTx: FilteredTransaction) : FlowLogic<DigitalSignature.WithKey>() {
|
val partialMerkleTx: FilteredTransaction) : FlowLogic<TransactionSignature>() {
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call(): DigitalSignature.WithKey {
|
override fun call(): TransactionSignature {
|
||||||
val resp = sendAndReceive<DigitalSignature.WithKey>(oracle, SignRequest(partialMerkleTx))
|
val resp = sendAndReceive<TransactionSignature>(oracle, SignRequest(partialMerkleTx))
|
||||||
return resp.unwrap { sig ->
|
return resp.unwrap { sig ->
|
||||||
check(oracle.owningKey.isFulfilledBy(listOf(sig.by)))
|
check(oracle.owningKey.isFulfilledBy(listOf(sig.by)))
|
||||||
tx.toWireTransaction().checkSignature(sig)
|
tx.toWireTransaction().checkSignature(sig)
|
||||||
|
@ -77,6 +77,9 @@ val DUMMY_CA: CertificateAndKeyPair by lazy {
|
|||||||
|
|
||||||
fun dummyCommand(vararg signers: PublicKey) = Command<TypeOnlyCommandData>(object : TypeOnlyCommandData() {}, signers.toList())
|
fun dummyCommand(vararg signers: PublicKey) = Command<TypeOnlyCommandData>(object : TypeOnlyCommandData() {}, signers.toList())
|
||||||
|
|
||||||
|
val DUMMY_IDENTITY_1: PartyAndCertificate get() = getTestPartyAndCertificate(DUMMY_PARTY)
|
||||||
|
val DUMMY_PARTY: Party get() = Party(X500Name("CN=Dummy,O=Dummy,L=Madrid,C=ES"), DUMMY_KEY_1.public)
|
||||||
|
|
||||||
//
|
//
|
||||||
// Extensions to the Driver DSL to auto-manufacture nodes by name.
|
// Extensions to the Driver DSL to auto-manufacture nodes by name.
|
||||||
//
|
//
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
package net.corda.testing
|
package net.corda.testing
|
||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.SecureHash
|
|
||||||
import net.corda.core.crypto.composite.expandedCompositeKeys
|
import net.corda.core.crypto.composite.expandedCompositeKeys
|
||||||
import net.corda.core.crypto.sign
|
import net.corda.core.crypto.testing.NULL_SIGNATURE
|
||||||
import net.corda.core.crypto.testing.NullSignature
|
|
||||||
import net.corda.core.crypto.toStringShort
|
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
@ -289,7 +286,7 @@ data class TestLedgerDSLInterpreter private constructor(
|
|||||||
override fun verifies(): EnforceVerifyOrFail {
|
override fun verifies(): EnforceVerifyOrFail {
|
||||||
try {
|
try {
|
||||||
val usedInputs = mutableSetOf<StateRef>()
|
val usedInputs = mutableSetOf<StateRef>()
|
||||||
services.recordTransactions(transactionsUnverified.map { SignedTransaction(it, listOf(NullSignature)) })
|
services.recordTransactions(transactionsUnverified.map { SignedTransaction(it, listOf(NULL_SIGNATURE)) })
|
||||||
for ((_, value) in transactionWithLocations) {
|
for ((_, value) in transactionWithLocations) {
|
||||||
val wtx = value.transaction
|
val wtx = value.transaction
|
||||||
val ltx = wtx.toLedgerTransaction(services)
|
val ltx = wtx.toLedgerTransaction(services)
|
||||||
@ -301,7 +298,7 @@ data class TestLedgerDSLInterpreter private constructor(
|
|||||||
throw DoubleSpentInputs(txIds)
|
throw DoubleSpentInputs(txIds)
|
||||||
}
|
}
|
||||||
usedInputs.addAll(wtx.inputs)
|
usedInputs.addAll(wtx.inputs)
|
||||||
services.recordTransactions(SignedTransaction(wtx, listOf(NullSignature)))
|
services.recordTransactions(SignedTransaction(wtx, listOf(NULL_SIGNATURE)))
|
||||||
}
|
}
|
||||||
return EnforceVerifyOrFail.Token
|
return EnforceVerifyOrFail.Token
|
||||||
} catch (exception: TransactionVerificationException) {
|
} catch (exception: TransactionVerificationException) {
|
||||||
@ -335,7 +332,7 @@ data class TestLedgerDSLInterpreter private constructor(
|
|||||||
*/
|
*/
|
||||||
fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: List<KeyPair>) = transactionsToSign.map { wtx ->
|
fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: List<KeyPair>) = transactionsToSign.map { wtx ->
|
||||||
check(wtx.requiredSigningKeys.isNotEmpty())
|
check(wtx.requiredSigningKeys.isNotEmpty())
|
||||||
val signatures = ArrayList<DigitalSignature.WithKey>()
|
val signatures = ArrayList<TransactionSignature>()
|
||||||
val keyLookup = HashMap<PublicKey, KeyPair>()
|
val keyLookup = HashMap<PublicKey, KeyPair>()
|
||||||
|
|
||||||
(ALL_TEST_KEYS + extraKeys).forEach {
|
(ALL_TEST_KEYS + extraKeys).forEach {
|
||||||
@ -343,7 +340,7 @@ fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: List<KeyPair>)
|
|||||||
}
|
}
|
||||||
wtx.requiredSigningKeys.expandedCompositeKeys.forEach {
|
wtx.requiredSigningKeys.expandedCompositeKeys.forEach {
|
||||||
val key = keyLookup[it] ?: throw IllegalArgumentException("Missing required key for ${it.toStringShort()}")
|
val key = keyLookup[it] ?: throw IllegalArgumentException("Missing required key for ${it.toStringShort()}")
|
||||||
signatures += key.sign(wtx.id)
|
signatures += key.sign(SignableData(wtx.id, SignatureMetadata(1, Crypto.findSignatureScheme(it).schemeNumberID)))
|
||||||
}
|
}
|
||||||
SignedTransaction(wtx, signatures)
|
SignedTransaction(wtx, signatures)
|
||||||
}
|
}
|
||||||
|
@ -124,8 +124,12 @@ class MockKeyManagementService(val identityService: IdentityService,
|
|||||||
|
|
||||||
override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
|
override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
|
||||||
val keyPair = getSigningKeyPair(publicKey)
|
val keyPair = getSigningKeyPair(publicKey)
|
||||||
val signature = keyPair.sign(bytes)
|
return keyPair.sign(bytes)
|
||||||
return signature
|
}
|
||||||
|
|
||||||
|
override fun sign(signableData: SignableData, publicKey: PublicKey): TransactionSignature {
|
||||||
|
val keyPair = getSigningKeyPair(publicKey)
|
||||||
|
return keyPair.sign(signableData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user