Skip to content

Commit

Permalink
CORDA-540: Add verification to ensure that private keys can only be s…
Browse files Browse the repository at this point in the history
…erialized with specific contexts (corda#1800)
  • Loading branch information
vkolomeyko authored Oct 5, 2017
1 parent e8d21f2 commit 33ba145
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ import kotlin.collections.LinkedHashSet
* we can often end up pulling in a lot of objects that do not make sense to put in a checkpoint.
* Thus, by blacklisting classes/interfaces we don't expect to be serialised, we can better handle/monitor the aforementioned behaviour.
* Inheritance works for blacklisted items, but one can specifically exclude classes from blacklisting as well.
* Note: Custom serializer registration trumps white/black lists. So if a given type has a custom serializer and has its name
* in the blacklist - it will still be serialized as specified by custom serializer.
* For more details, see [net.corda.nodeapi.internal.serialization.CordaClassResolver.getRegistration]
*/
object AllButBlacklisted : ClassWhitelist {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,23 @@ import com.esotericsoftware.kryo.util.MapReferenceResolver
import net.corda.core.concurrent.CordaFuture
import net.corda.core.contracts.PrivacySalt
import net.corda.core.contracts.StateRef
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.TransactionSignature
import net.corda.core.identity.Party
import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationContext.UseCase.*
import net.corda.core.serialization.SerializeAsTokenContext
import net.corda.core.serialization.SerializedBytes
import net.corda.core.toFuture
import net.corda.core.toObservable
import net.corda.core.transactions.*
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
import org.bouncycastle.asn1.ASN1InputStream
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.cert.X509CertificateHolder
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import rx.Observable
import sun.security.ec.ECPublicKeyImpl
import sun.security.util.DerValue
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.lang.reflect.InvocationTargetException
Expand Down Expand Up @@ -285,9 +278,16 @@ object SignedTransactionSerializer : Serializer<SignedTransaction>() {
}
}

sealed class UseCaseSerializer<T>(private val allowedUseCases: EnumSet<SerializationContext.UseCase>) : Serializer<T>() {
protected fun checkUseCase() {
checkUseCase(allowedUseCases)
}
}

@ThreadSafe
object PrivateKeySerializer : Serializer<PrivateKey>() {
object PrivateKeySerializer : UseCaseSerializer<PrivateKey>(EnumSet.of(Storage, Checkpoint)) {
override fun write(kryo: Kryo, output: Output, obj: PrivateKey) {
checkUseCase()
output.writeBytesWithLength(obj.encoded)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package net.corda.nodeapi.internal.serialization

import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationFactory
import java.util.*

internal fun checkUseCase(allowedUseCases: EnumSet<SerializationContext.UseCase>) {
val currentContext: SerializationContext = SerializationFactory.currentFactory?.currentContext ?: throw IllegalStateException("Current context is not set")
if(!allowedUseCases.contains(currentContext.useCase)) {
throw IllegalStateException("UseCase '${currentContext.useCase}' is not within '$allowedUseCases'")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class KryoTests : TestDependencyInjectionBase() {
AllWhitelist,
emptyMap(),
true,
SerializationContext.UseCase.P2P)
SerializationContext.UseCase.Storage)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package net.corda.nodeapi.internal.serialization

import net.corda.core.crypto.Crypto
import net.corda.core.serialization.SerializationContext.UseCase.*
import net.corda.core.serialization.SerializationDefaults
import net.corda.core.serialization.serialize
import net.corda.testing.TestDependencyInjectionBase
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import java.security.PrivateKey
import kotlin.test.assertTrue

@RunWith(Parameterized::class)
class PrivateKeySerializationTest(private val privateKey: PrivateKey, private val testName: String) : TestDependencyInjectionBase() {

companion object {
@JvmStatic
@Parameterized.Parameters(name = "{1}")
fun data(): Collection<Array<Any>> {
val privateKeys: List<PrivateKey> = Crypto.supportedSignatureSchemes().filterNot { Crypto.COMPOSITE_KEY === it }
.map { Crypto.generateKeyPair(it).private }

return privateKeys.map { arrayOf<Any>(it, PrivateKeySerializationTest::class.java.simpleName + "-" + it.javaClass.simpleName) }
}
}

@Test
fun `passed with expected UseCases`() {
assertTrue { privateKey.serialize(context = SerializationDefaults.STORAGE_CONTEXT).bytes.isNotEmpty() }
assertTrue { privateKey.serialize(context = SerializationDefaults.CHECKPOINT_CONTEXT).bytes.isNotEmpty() }
}

@Test
fun `failed with wrong UseCase`() {
assertThatThrownBy { privateKey.serialize(context = SerializationDefaults.P2P_CONTEXT) }
.isInstanceOf(IllegalStateException::class.java)
.hasMessageContaining("UseCase '${P2P}' is not within")

}
}

0 comments on commit 33ba145

Please sign in to comment.