mirror of
https://github.com/corda/corda.git
synced 2024-12-24 07:06:44 +00:00
Merge pull request #3 from corda/feature/ENT-2506/change_signers_type
ENT-2506 Changes signers field type
This commit is contained in:
commit
7902d758bd
@ -429,7 +429,7 @@ public static final class net.corda.core.contracts.AmountTransfer$Companion exte
|
|||||||
public interface net.corda.core.contracts.Attachment extends net.corda.core.contracts.NamedByHash
|
public interface net.corda.core.contracts.Attachment extends net.corda.core.contracts.NamedByHash
|
||||||
public void extractFile(String, java.io.OutputStream)
|
public void extractFile(String, java.io.OutputStream)
|
||||||
@NotNull
|
@NotNull
|
||||||
public abstract java.util.List<net.corda.core.identity.Party> getSigners()
|
public abstract java.util.List<java.security.PublicKey> getSigners()
|
||||||
public abstract int getSize()
|
public abstract int getSize()
|
||||||
@NotNull
|
@NotNull
|
||||||
public abstract java.io.InputStream open()
|
public abstract java.io.InputStream open()
|
||||||
@ -540,7 +540,7 @@ public final class net.corda.core.contracts.ContractAttachment extends java.lang
|
|||||||
@NotNull
|
@NotNull
|
||||||
public net.corda.core.crypto.SecureHash getId()
|
public net.corda.core.crypto.SecureHash getId()
|
||||||
@NotNull
|
@NotNull
|
||||||
public java.util.List<net.corda.core.identity.Party> getSigners()
|
public java.util.List<java.security.PublicKey> getSigners()
|
||||||
public int getSize()
|
public int getSize()
|
||||||
@Nullable
|
@Nullable
|
||||||
public final String getUploader()
|
public final String getUploader()
|
||||||
|
@ -3,13 +3,13 @@ package net.corda.deterministic.common
|
|||||||
import net.corda.core.contracts.Attachment
|
import net.corda.core.contracts.Attachment
|
||||||
import net.corda.core.contracts.ContractClassName
|
import net.corda.core.contracts.ContractClassName
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.Party
|
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
import java.security.PublicKey
|
||||||
|
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
class MockContractAttachment(override val id: SecureHash = SecureHash.zeroHash, val contract: ContractClassName, override val signers: List<Party> = ArrayList()) : Attachment {
|
class MockContractAttachment(override val id: SecureHash = SecureHash.zeroHash, val contract: ContractClassName, override val signers: List<PublicKey> = emptyList()) : Attachment {
|
||||||
override fun open(): InputStream = ByteArrayInputStream(id.bytes)
|
override fun open(): InputStream = ByteArrayInputStream(id.bytes)
|
||||||
override val size = id.size
|
override val size = id.size
|
||||||
}
|
}
|
||||||
|
@ -42,8 +42,8 @@ class AttachmentTest {
|
|||||||
attachment = object : Attachment {
|
attachment = object : Attachment {
|
||||||
override val id: SecureHash
|
override val id: SecureHash
|
||||||
get() = SecureHash.allOnesHash
|
get() = SecureHash.allOnesHash
|
||||||
override val signers: List<Party>
|
override val signers: List<PublicKey>
|
||||||
get() = listOf(ALICE)
|
get() = listOf(ALICE_KEY)
|
||||||
override val size: Int
|
override val size: Int
|
||||||
get() = jarData.size
|
get() = jarData.size
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import net.corda.core.serialization.CordaSerializable
|
|||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
import java.security.PublicKey
|
||||||
import java.util.jar.JarInputStream
|
import java.util.jar.JarInputStream
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,10 +52,10 @@ interface Attachment : NamedByHash {
|
|||||||
fun extractFile(path: String, outputTo: OutputStream) = openAsJAR().use { it.extractFile(path, outputTo) }
|
fun extractFile(path: String, outputTo: OutputStream) = openAsJAR().use { it.extractFile(path, outputTo) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The parties that have correctly signed the whole attachment.
|
* The keys that have correctly signed the whole attachment.
|
||||||
* Can be empty, for example non-contract attachments won't be necessarily be signed.
|
* Can be empty, for example non-contract attachments won't be necessarily be signed.
|
||||||
*/
|
*/
|
||||||
val signers: List<Party>
|
val signers: List<PublicKey>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attachment size in bytes.
|
* Attachment size in bytes.
|
||||||
|
@ -82,5 +82,5 @@ data class SignatureAttachmentConstraint(
|
|||||||
val key: PublicKey
|
val key: PublicKey
|
||||||
) : AttachmentConstraint {
|
) : AttachmentConstraint {
|
||||||
override fun isSatisfiedBy(attachment: Attachment): Boolean =
|
override fun isSatisfiedBy(attachment: Attachment): Boolean =
|
||||||
key.isFulfilledBy(attachment.signers.map { it.owningKey })
|
key.isFulfilledBy(attachment.signers.map { it })
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
@file:KeepForDJVM
|
@file:KeepForDJVM
|
||||||
|
|
||||||
package net.corda.core.internal
|
package net.corda.core.internal
|
||||||
|
|
||||||
import net.corda.core.DeleteForDJVM
|
import net.corda.core.DeleteForDJVM
|
||||||
@ -11,6 +12,7 @@ import java.io.FileNotFoundException
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
import java.security.PublicKey
|
||||||
import java.util.jar.JarInputStream
|
import java.util.jar.JarInputStream
|
||||||
|
|
||||||
const val DEPLOYED_CORDAPP_UPLOADER = "app"
|
const val DEPLOYED_CORDAPP_UPLOADER = "app"
|
||||||
@ -40,8 +42,8 @@ abstract class AbstractAttachment(dataLoader: () -> ByteArray) : Attachment {
|
|||||||
override val size: Int get() = attachmentData.size
|
override val size: Int get() = attachmentData.size
|
||||||
|
|
||||||
override fun open(): InputStream = attachmentData.inputStream()
|
override fun open(): InputStream = attachmentData.inputStream()
|
||||||
override val signers by lazy {
|
override val signers: List<PublicKey> by lazy {
|
||||||
openAsJAR().use(JarSignatureCollector::collectSigningParties)
|
openAsJAR().use(JarSignatureCollector::collectSigners)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?) = other === this || other is Attachment && other.id == this.id
|
override fun equals(other: Any?) = other === this || other is Attachment && other.id == this.id
|
||||||
|
@ -2,6 +2,7 @@ package net.corda.core.internal
|
|||||||
|
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import java.security.CodeSigner
|
import java.security.CodeSigner
|
||||||
|
import java.security.PublicKey
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.util.jar.JarEntry
|
import java.util.jar.JarEntry
|
||||||
import java.util.jar.JarInputStream
|
import java.util.jar.JarInputStream
|
||||||
@ -20,7 +21,7 @@ object JarSignatureCollector {
|
|||||||
* @param jar The open [JarInputStream] to collect signing parties from.
|
* @param jar The open [JarInputStream] to collect signing parties from.
|
||||||
* @throws InvalidJarSignersException If the signer sets for any two signable items are different from each other.
|
* @throws InvalidJarSignersException If the signer sets for any two signable items are different from each other.
|
||||||
*/
|
*/
|
||||||
fun collectSigningParties(jar: JarInputStream): List<Party> {
|
fun collectSigners(jar: JarInputStream): List<PublicKey> {
|
||||||
val signerSets = jar.fileSignerSets
|
val signerSets = jar.fileSignerSets
|
||||||
if (signerSets.isEmpty()) return emptyList()
|
if (signerSets.isEmpty()) return emptyList()
|
||||||
|
|
||||||
@ -28,14 +29,14 @@ object JarSignatureCollector {
|
|||||||
for ((otherFile, otherSignerSet) in signerSets.subList(1, signerSets.size)) {
|
for ((otherFile, otherSignerSet) in signerSets.subList(1, signerSets.size)) {
|
||||||
if (otherSignerSet != firstSignerSet) throw InvalidJarSignersException(
|
if (otherSignerSet != firstSignerSet) throw InvalidJarSignersException(
|
||||||
"""
|
"""
|
||||||
Mismatch between signers ${firstSignerSet.toPartiesOrderedByName()} for file $firstFile
|
Mismatch between signers ${firstSignerSet.toOrderedPublicKeys()} for file $firstFile
|
||||||
and signers ${otherSignerSet.toPartiesOrderedByName()} for file ${otherFile}.
|
and signers ${otherSignerSet.toOrderedPublicKeys()} for file ${otherFile}.
|
||||||
See https://docs.corda.net/design/data-model-upgrades/signature-constraints.html for details of the
|
See https://docs.corda.net/design/data-model-upgrades/signature-constraints.html for details of the
|
||||||
constraints applied to attachment signatures.
|
constraints applied to attachment signatures.
|
||||||
""".trimIndent().replace('\n', ' '))
|
""".trimIndent().replace('\n', ' '))
|
||||||
}
|
}
|
||||||
|
|
||||||
return firstSignerSet.toPartiesOrderedByName()
|
return firstSignerSet.toOrderedPublicKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val JarInputStream.fileSignerSets: List<Pair<String, Set<CodeSigner>>> get() =
|
private val JarInputStream.fileSignerSets: List<Pair<String, Set<CodeSigner>>> get() =
|
||||||
@ -56,9 +57,9 @@ object JarSignatureCollector {
|
|||||||
private fun Sequence<JarEntry>.toFileSignerSet(): Sequence<Pair<String, Set<CodeSigner>>> =
|
private fun Sequence<JarEntry>.toFileSignerSet(): Sequence<Pair<String, Set<CodeSigner>>> =
|
||||||
map { entry -> entry.name to (entry.codeSigners?.toSet() ?: emptySet()) }
|
map { entry -> entry.name to (entry.codeSigners?.toSet() ?: emptySet()) }
|
||||||
|
|
||||||
private fun Set<CodeSigner>.toPartiesOrderedByName(): List<Party> = map {
|
private fun Set<CodeSigner>.toOrderedPublicKeys(): List<PublicKey> = map {
|
||||||
Party(it.signerCertPath.certificates[0] as X509Certificate)
|
(it.signerCertPath.certificates[0] as X509Certificate).publicKey
|
||||||
}.sortedBy { it.name.toString() } // Sorted for determinism.
|
}.sortedBy { it.hash} // Sorted for determinism.
|
||||||
|
|
||||||
private val JarInputStream.entries get(): Sequence<JarEntry> = generateSequence(nextJarEntry) { nextJarEntry }
|
private val JarInputStream.entries get(): Sequence<JarEntry> = generateSequence(nextJarEntry) { nextJarEntry }
|
||||||
}
|
}
|
||||||
|
@ -163,8 +163,8 @@ open class TransactionBuilder @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeSignatureAttachmentConstraint(attachmentSigners: List<Party>) =
|
private fun makeSignatureAttachmentConstraint(attachmentSigners: List<PublicKey>) =
|
||||||
SignatureAttachmentConstraint(CompositeKey.Builder().addKeys(attachmentSigners.map { it.owningKey }).build())
|
SignatureAttachmentConstraint(CompositeKey.Builder().addKeys(attachmentSigners.map { it }).build())
|
||||||
|
|
||||||
private fun useWhitelistedByZoneAttachmentConstraint(contractClassName: ContractClassName, networkParameters: NetworkParameters) =
|
private fun useWhitelistedByZoneAttachmentConstraint(contractClassName: ContractClassName, networkParameters: NetworkParameters) =
|
||||||
contractClassName in networkParameters.whitelistedContractImplementations.keys
|
contractClassName in networkParameters.whitelistedContractImplementations.keys
|
||||||
|
@ -2,6 +2,7 @@ package net.corda.core.internal
|
|||||||
|
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
||||||
import net.corda.testing.core.ALICE_NAME
|
import net.corda.testing.core.ALICE_NAME
|
||||||
import net.corda.testing.core.BOB_NAME
|
import net.corda.testing.core.BOB_NAME
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
@ -13,6 +14,7 @@ import java.io.FileInputStream
|
|||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
|
import java.security.PublicKey
|
||||||
import java.util.jar.JarInputStream
|
import java.util.jar.JarInputStream
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
@ -60,7 +62,7 @@ class JarSignatureCollectorTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val List<Party>.names get() = map { it.name }
|
private val List<Party>.keys get() = map { it.owningKey }
|
||||||
|
|
||||||
@After
|
@After
|
||||||
fun tearDown() {
|
fun tearDown() {
|
||||||
@ -92,33 +94,33 @@ class JarSignatureCollectorTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `one signer`() {
|
fun `one signer`() {
|
||||||
createJar("_signable1", "_signable2")
|
createJar("_signable1", "_signable2")
|
||||||
signAsAlice()
|
val key = signAsAlice()
|
||||||
assertEquals(listOf(ALICE_NAME), getJarSigners().names) // We only reused ALICE's distinguished name, so the keys will be different.
|
assertEquals(listOf(key), getJarSigners())
|
||||||
|
|
||||||
(dir / "my-dir").createDirectory()
|
(dir / "my-dir").createDirectory()
|
||||||
updateJar("my-dir")
|
updateJar("my-dir")
|
||||||
assertEquals(listOf(ALICE_NAME), getJarSigners().names) // Unsigned directory is irrelevant.
|
assertEquals(listOf(key), getJarSigners()) // Unsigned directory is irrelevant.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `two signers`() {
|
fun `two signers`() {
|
||||||
createJar("_signable1", "_signable2")
|
createJar("_signable1", "_signable2")
|
||||||
signAsAlice()
|
val key1 = signAsAlice()
|
||||||
signAsBob()
|
val key2 = signAsBob()
|
||||||
|
|
||||||
assertEquals(listOf(ALICE_NAME, BOB_NAME), getJarSigners().names)
|
assertEquals(setOf(key1, key2), getJarSigners().toSet())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `all files must be signed by the same set of signers`() {
|
fun `all files must be signed by the same set of signers`() {
|
||||||
createJar("_signable1")
|
createJar("_signable1")
|
||||||
signAsAlice()
|
val key1 = signAsAlice()
|
||||||
assertEquals(listOf(ALICE_NAME), getJarSigners().names)
|
assertEquals(listOf(key1), getJarSigners())
|
||||||
|
|
||||||
updateJar("_signable2")
|
updateJar("_signable2")
|
||||||
signAsBob()
|
signAsBob()
|
||||||
assertFailsWith<InvalidJarSignersException>(
|
assertFailsWith<InvalidJarSignersException>(
|
||||||
"""
|
"""
|
||||||
Mismatch between signers [O=Alice Corp, L=Madrid, C=ES, O=Bob Plc, L=Rome, C=IT] for file _signable1
|
Mismatch between signers [O=Alice Corp, L=Madrid, C=ES, O=Bob Plc, L=Rome, C=IT] for file _signable1
|
||||||
and signers [O=Bob Plc, L=Rome, C=IT] for file _signable2.
|
and signers [O=Bob Plc, L=Rome, C=IT] for file _signable2.
|
||||||
See https://docs.corda.net/design/data-model-upgrades/signature-constraints.html for details of the
|
See https://docs.corda.net/design/data-model-upgrades/signature-constraints.html for details of the
|
||||||
@ -131,8 +133,8 @@ class JarSignatureCollectorTest {
|
|||||||
fun `bad signature is caught even if the party would not qualify as a signer`() {
|
fun `bad signature is caught even if the party would not qualify as a signer`() {
|
||||||
(dir / "volatile").writeLines(listOf("volatile"))
|
(dir / "volatile").writeLines(listOf("volatile"))
|
||||||
createJar("volatile")
|
createJar("volatile")
|
||||||
signAsAlice()
|
val key1 = signAsAlice()
|
||||||
assertEquals(listOf(ALICE_NAME), getJarSigners().names)
|
assertEquals(listOf(key1), getJarSigners())
|
||||||
|
|
||||||
(dir / "volatile").writeLines(listOf("garbage"))
|
(dir / "volatile").writeLines(listOf("garbage"))
|
||||||
updateJar("volatile", "_signable1") // ALICE's signature on volatile is now bad.
|
updateJar("volatile", "_signable1") // ALICE's signature on volatile is now bad.
|
||||||
@ -148,14 +150,17 @@ class JarSignatureCollectorTest {
|
|||||||
private fun updateJar(vararg contents: String) =
|
private fun updateJar(vararg contents: String) =
|
||||||
execute(*(arrayOf("jar", "uvf", FILENAME) + contents))
|
execute(*(arrayOf("jar", "uvf", FILENAME) + contents))
|
||||||
|
|
||||||
private fun signJar(alias: String, password: String) =
|
private fun signJar(alias: String, password: String): PublicKey {
|
||||||
execute("jarsigner", "-keystore", "_teststore", "-storepass", "storepass", "-keypass", password, FILENAME, alias)
|
execute("jarsigner", "-keystore", "_teststore", "-storepass", "storepass", "-keypass", password, FILENAME, alias)
|
||||||
|
val ks = loadKeyStore(dir.resolve("_teststore"), "storepass")
|
||||||
|
return ks.getCertificate(alias).publicKey
|
||||||
|
}
|
||||||
|
|
||||||
private fun signAsAlice() = signJar(ALICE, ALICE_PASS)
|
private fun signAsAlice() = signJar(ALICE, ALICE_PASS)
|
||||||
private fun signAsBob() = signJar(BOB, BOB_PASS)
|
private fun signAsBob() = signJar(BOB, BOB_PASS)
|
||||||
|
|
||||||
private fun getJarSigners() =
|
private fun getJarSigners() =
|
||||||
JarInputStream(FileInputStream((dir / FILENAME).toFile())).use(JarSignatureCollector::collectSigningParties)
|
JarInputStream(FileInputStream((dir / FILENAME).toFile())).use(JarSignatureCollector::collectSigners)
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import org.junit.Assert.assertTrue
|
|||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.security.PublicKey
|
||||||
|
|
||||||
class TransactionBuilderTest {
|
class TransactionBuilderTest {
|
||||||
@Rule
|
@Rule
|
||||||
@ -119,12 +120,12 @@ class TransactionBuilderTest {
|
|||||||
private val unsignedAttachment = object : AbstractAttachment({ byteArrayOf() }) {
|
private val unsignedAttachment = object : AbstractAttachment({ byteArrayOf() }) {
|
||||||
override val id: SecureHash get() = throw UnsupportedOperationException()
|
override val id: SecureHash get() = throw UnsupportedOperationException()
|
||||||
|
|
||||||
override val signers: List<Party> get() = emptyList()
|
override val signers: List<PublicKey> get() = emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun signedAttachment(vararg parties: Party) = object : AbstractAttachment({ byteArrayOf() }) {
|
private fun signedAttachment(vararg parties: Party) = object : AbstractAttachment({ byteArrayOf() }) {
|
||||||
override val id: SecureHash get() = throw UnsupportedOperationException()
|
override val id: SecureHash get() = throw UnsupportedOperationException()
|
||||||
|
|
||||||
override val signers: List<Party> get() = parties.toList()
|
override val signers: List<PublicKey> get() = parties.map { it.owningKey }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user