Merge pull request #3 from corda/feature/ENT-2506/change_signers_type

ENT-2506 Changes signers field type
This commit is contained in:
Tudor Malene 2018-09-19 11:07:29 +01:00 committed by GitHub
commit 7902d758bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 47 additions and 37 deletions

View File

@ -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()

View File

@ -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
} }

View File

@ -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

View File

@ -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.

View File

@ -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 })
} }

View File

@ -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

View File

@ -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 }
} }

View File

@ -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

View File

@ -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
} }

View File

@ -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 }
} }
} }