From 98b97dafca7114da2ce21bbcf730394d8a3ac345 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 14 Feb 2023 22:54:32 +0000 Subject: [PATCH 01/37] Add key type tests --- .../kotlin/io/libp2p/crypto/KeyTypesTest.kt | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 libp2p/src/test/kotlin/io/libp2p/crypto/KeyTypesTest.kt diff --git a/libp2p/src/test/kotlin/io/libp2p/crypto/KeyTypesTest.kt b/libp2p/src/test/kotlin/io/libp2p/crypto/KeyTypesTest.kt new file mode 100644 index 000000000..9a5b5805a --- /dev/null +++ b/libp2p/src/test/kotlin/io/libp2p/crypto/KeyTypesTest.kt @@ -0,0 +1,43 @@ +package io.libp2p.crypto + +import io.libp2p.crypto.keys.generateEcdsaKeyPair +import io.libp2p.crypto.keys.generateEd25519KeyPair +import io.libp2p.crypto.keys.generateRsaKeyPair +import io.libp2p.crypto.keys.generateSecp256k1KeyPair +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +class KeyTypesTest { + + @Test + fun ed25519() { + val pair = generateEd25519KeyPair() + val toSign = "G'day!".toByteArray() + val signed = pair.first.sign(toSign) + assertTrue(pair.second.verify(toSign, signed)) + } + + @Test + fun rsa() { + val pair = generateRsaKeyPair(2048) + val toSign = "G'day!".toByteArray() + val signed = pair.first.sign(toSign) + assertTrue(pair.second.verify(toSign, signed)) + } + + @Test + fun secp256k1() { + val pair = generateSecp256k1KeyPair() + val toSign = "G'day!".toByteArray() + val signed = pair.first.sign(toSign) + assertTrue(pair.second.verify(toSign, signed)) + } + + @Test + fun ecdsa() { + val pair = generateEcdsaKeyPair() // p-256 + val toSign = "G'day!".toByteArray() + val signed = pair.first.sign(toSign) + assertTrue(pair.second.verify(toSign, signed)) + } +} From b634975a542e21b66d949982f5016a142a3fa23d Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 22 Feb 2023 09:57:30 +0000 Subject: [PATCH 02/37] Initial attempt at libp2p-tls implementation Todo: * muxer negotiation via client hello info * verify remote certs --- .../io/libp2p/core/security/SecureChannel.kt | 2 +- .../libp2p/security/tls/TLSSecureChannel.kt | 218 ++++++++++++++++++ .../libp2p/security/tls/CertificatesTest.kt | 70 ++++++ 3 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt create mode 100644 libp2p/src/test/kotlin/io/libp2p/security/tls/CertificatesTest.kt diff --git a/libp2p/src/main/kotlin/io/libp2p/core/security/SecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/core/security/SecureChannel.kt index fb14f039c..ce12552b9 100644 --- a/libp2p/src/main/kotlin/io/libp2p/core/security/SecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/core/security/SecureChannel.kt @@ -20,7 +20,7 @@ interface SecureChannel : ProtocolBinding { val remoteId: PeerId, /** - * The public key of the + * The public key of the remote peer. */ val remotePubKey: PubKey ) diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt new file mode 100644 index 000000000..53eab2f6d --- /dev/null +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -0,0 +1,218 @@ +package io.libp2p.security.tls + +import crypto.pb.Crypto +import io.libp2p.core.P2PChannel +import io.libp2p.core.PeerId +import io.libp2p.core.crypto.PrivKey +import io.libp2p.core.crypto.PubKey +import io.libp2p.core.crypto.marshalPublicKey +import io.libp2p.core.crypto.unmarshalPublicKey +import io.libp2p.core.multistream.ProtocolDescriptor +import io.libp2p.core.security.SecureChannel +import io.libp2p.crypto.keys.Ed25519PublicKey +import io.libp2p.crypto.keys.generateEd25519KeyPair +import io.netty.buffer.PooledByteBufAllocator +import io.netty.channel.CombinedChannelDuplexHandler +import io.netty.handler.codec.LengthFieldBasedFrameDecoder +import io.netty.handler.codec.LengthFieldPrepender +import io.netty.handler.ssl.ClientAuth +import io.netty.handler.ssl.SslContextBuilder +import org.bouncycastle.asn1.* +import org.bouncycastle.asn1.edec.EdECObjectIdentifiers +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo +import org.bouncycastle.asn1.x500.X500Name +import org.bouncycastle.asn1.x509.AlgorithmIdentifier +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo +import org.bouncycastle.cert.X509CertificateHolder +import org.bouncycastle.cert.X509v3CertificateBuilder +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils +import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters +import org.bouncycastle.jcajce.interfaces.EdDSAPublicKey +import org.bouncycastle.jce.provider.BouncyCastleProvider +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder +import java.math.BigInteger +import java.security.KeyFactory +import java.security.PrivateKey +import java.security.PublicKey +import java.security.cert.Certificate +import java.security.cert.X509Certificate +import java.security.spec.* +import java.time.Instant +import java.util.* +import java.util.concurrent.CompletableFuture +import java.util.logging.Logger +import kotlin.experimental.and + + +private val log = Logger.getLogger(TlsSecureChannel::class.java.name) +const val MaxCipheredPacketLength = 65535 +val certificatePrefix = "libp2p-tls-handshake:".encodeToByteArray() + +class UShortLengthCodec : CombinedChannelDuplexHandler( + LengthFieldBasedFrameDecoder(MaxCipheredPacketLength + 2, 0, 2, 0, 2), + LengthFieldPrepender(2) +) + +class TlsSecureChannel(private val localKey: PrivKey) : + SecureChannel { + + companion object { + const val announce = "/tls/1.0.0" + } + + override val protocolDescriptor = ProtocolDescriptor(announce) + + fun initChannel(ch: P2PChannel): CompletableFuture { + return initChannel(ch, "") + } + + override fun initChannel( + ch: P2PChannel, + selectedProtocol: String + ): CompletableFuture { + val handshakeComplete = CompletableFuture() + + ch.pushHandler(UShortLengthCodec()) // Packet length codec should stay forever. + + ch.isInitiator + val connectionKeys = generateEd25519KeyPair() + val javaPrivateKey = getJavaKey(connectionKeys.first) + val sslContext = SslContextBuilder.forServer(javaPrivateKey, listOf(buildCert(localKey, connectionKeys.first))) + .protocols(listOf("TLSv1.3")) + .clientAuth(ClientAuth.REQUIRE) + .build() + val handler = sslContext.newHandler(PooledByteBufAllocator.DEFAULT) + ch.pushHandler(handler) + val handshake = handler.handshakeFuture() + val engine = handler.engine() + handshake.addListener { _ -> handshakeComplete.complete(SecureChannel.Session( + PeerId.fromPubKey(localKey.publicKey()), + verifyAndExtractPeerId(engine.getSession().getPeerCertificates()), + getPublicKeyFromCert(engine.getSession().getPeerCertificates()) + )) } + return handshakeComplete + } +} + +fun getJavaKey(priv: PrivKey): PrivateKey { + if (priv.keyType == Crypto.KeyType.Ed25519) { + val kf = KeyFactory.getInstance("Ed25519"); + val privKeyInfo = + PrivateKeyInfo(AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), DEROctetString(priv.raw())) + val pkcs8KeySpec = PKCS8EncodedKeySpec(privKeyInfo.encoded) + return kf.generatePrivate(pkcs8KeySpec) + } + if (priv.keyType == Crypto.KeyType.RSA) { + throw IllegalStateException("Unimplemented RSA key support for TLS") + } + throw IllegalArgumentException("Unsupported TLS key type:" + priv.keyType) +} + +fun getJavaPublicKey(pub: PubKey): PublicKey { + if (pub.keyType == Crypto.KeyType.Ed25519) { + val kf = KeyFactory.getInstance("Ed25519"); + + // determine if x was odd. + var pk = pub.raw() + val lastbyteInt = pk[pk.lastIndex].toInt(); + var xisodd = lastbyteInt.and(255).shr(7) == 1; + // make sure most significant bit will be 0 - after reversing. + pk[31] = pk[31].and(127); + val y = BigInteger(1, pk.reversedArray()); + + val paramSpec = NamedParameterSpec("Ed25519"); + val ep = EdECPoint(xisodd, y); + val pubSpec = EdECPublicKeySpec(paramSpec, ep); + return kf.generatePublic(pubSpec); + } + throw IllegalArgumentException("Unsupported TLS key type:" + pub.keyType) +} + +fun getPubKey(pub: PublicKey): PubKey { + if (pub.algorithm.equals("Ed25519")) + return Ed25519PublicKey(Ed25519PublicKeyParameters(pub.encoded)) + if (pub.algorithm.equals("RSA")) + throw IllegalStateException("Unimplemented RSA public key support for TLS") + throw IllegalStateException("Unsupported key type: " + pub.algorithm) +} + +fun verifyAndExtractPeerId(chain: Array): PeerId { + if (chain.size != 1) + throw java.lang.IllegalStateException("Cert chain must have exactly 1 element!") + val cert = chain.get(0) + // peerid is in the certificate extension + val bcCert = org.bouncycastle.asn1.x509.Certificate + .getInstance(ASN1Primitive.fromByteArray(cert.getEncoded())) + val bcX509Cert = X509CertificateHolder(bcCert) + val libp2pOid = ASN1ObjectIdentifier("1.3.6.1.4.1.53594.1.1") + val extension = bcX509Cert.extensions.getExtension(libp2pOid) + if (extension == null) + throw IllegalStateException("Certificate extension not present!") + val input = ASN1InputStream(extension.extnValue.encoded) + val wrapper = input.readObject() as DEROctetString + val seq = ASN1InputStream(wrapper.octets).readObject() as DLSequence + val pubKeyProto = (seq.getObjectAt(0) as DEROctetString).octets + val signature = (seq.getObjectAt(1) as DEROctetString).octets + val pubKey = unmarshalPublicKey(pubKeyProto) + if (! pubKey.verify(certificatePrefix.plus(cert.publicKey.encoded), signature)) + throw IllegalStateException("Invalid signature on TLS certificate extension!") + + cert.verify(cert.publicKey) + val now = Date() + if (bcCert.endDate.date.before(now)) + throw IllegalStateException("TLS certificate has expired!") + if (bcCert.startDate.date.after(now)) + throw IllegalStateException("TLS certificate is not valid yet!") + return PeerId.fromPubKey(pubKey) +} + +fun getPublicKeyFromCert(chain: Array): PubKey { + if (chain.size != 1) + throw java.lang.IllegalStateException("Cert chain must have exactly 1 element!") + val cert = chain.get(0) + return getPubKey(cert.publicKey) +} + +fun toAsn1(pub: PubKey): ByteArray { + // Shouldn't be using RSA here + if (pub.keyType == Crypto.KeyType.Ed25519) { + return Ed25519PublicKeyParameters(pub.raw()).encoded + } + if (pub.keyType == Crypto.KeyType.ECDSA) { + + } + throw IllegalStateException("Unsupported key type for TLS: " + pub.keyType) +} + +/** Build a self signed cert, with an extension containing the host key + sig(cert public key) + * + */ +fun buildCert(hostKey: PrivKey, subjectKey: PrivKey) : X509Certificate { + val publicKeyAsn1 = getJavaPublicKey(subjectKey.publicKey()).encoded + val subPubKeyInfo = SubjectPublicKeyInfo.getInstance(publicKeyAsn1) + + val now = Instant.now() + val validFrom = Date.from(now.minusSeconds(3600)) + val oneYear = 60L * 60 * 24 * 365 + val validTo = Date.from(now.plusSeconds(oneYear)) + val issuer = X500Name("CN=Nabu,O=Peergos,L=Oxford,C=UK") + val subject = issuer + + val signature = hostKey.sign(certificatePrefix.plus(publicKeyAsn1)) + val hostPublicProto = hostKey.publicKey().bytes() + val extension = DERSequence(arrayOf(DEROctetString(hostPublicProto), DEROctetString(signature))) + + var certBuilder = X509v3CertificateBuilder( + issuer, + BigInteger.valueOf(now.toEpochMilli()), + validFrom, + validTo, + subject, + subPubKeyInfo + ).addExtension(ASN1ObjectIdentifier("1.3.6.1.4.1.53594.1.1"), false, extension) + val signer = JcaContentSignerBuilder("Ed25519") + .setProvider(BouncyCastleProvider()) + .build(getJavaKey(subjectKey)) + return JcaX509CertificateConverter().getCertificate(certBuilder.build(signer)) +} diff --git a/libp2p/src/test/kotlin/io/libp2p/security/tls/CertificatesTest.kt b/libp2p/src/test/kotlin/io/libp2p/security/tls/CertificatesTest.kt new file mode 100644 index 000000000..69cb9e4e1 --- /dev/null +++ b/libp2p/src/test/kotlin/io/libp2p/security/tls/CertificatesTest.kt @@ -0,0 +1,70 @@ +package io.libp2p.security.tls + +import io.libp2p.core.PeerId +import io.libp2p.crypto.keys.generateEd25519KeyPair +import org.bouncycastle.cert.X509CertificateHolder +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter +import org.bouncycastle.jce.provider.BouncyCastleProvider +import org.bouncycastle.util.encoders.Hex +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class CertificatesTest { + + @Test + fun ed25519Peer() { + val hex = "308201773082011ea003020102020900f5bd0debaa597f52300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d030107034200046bf9871220d71dcb3483ecdfcbfcc7c103f8509d0974b3c18ab1f1be1302d643103a08f7a7722c1b247ba3876fe2c59e26526f479d7718a85202ddbe47562358a37f307d307b060a2b0601040183a25a01010101ff046a30680424080112207fda21856709c5ae12fd6e8450623f15f11955d384212b89f56e7e136d2e17280440aaa6bffabe91b6f30c35e3aa4f94b1188fed96b0ffdd393f4c58c1c047854120e674ce64c788406d1c2c4b116581fd7411b309881c3c7f20b46e54c7e6fe7f0f300a06082a8648ce3d040302034700304402207d1a1dbd2bda235ff2ec87daf006f9b04ba076a5a5530180cd9c2e8f6399e09d0220458527178c7e77024601dbb1b256593e9b96d961b96349d1f560114f61a87595" + val certBytes = Hex.decode(hex) + val certHolder = X509CertificateHolder(certBytes) + val cert = JcaX509CertificateConverter().setProvider(BouncyCastleProvider()).getCertificate(certHolder) + val peerIdFromCert = verifyAndExtractPeerId(arrayOf(cert)) + val expectedPeerId = PeerId.fromBase58("12D3KooWJRSrypvnpHgc6ZAgyCni4KcSmbV7uGRaMw5LgMKT18fq") + assertEquals(peerIdFromCert, expectedPeerId) + } + + @Test + fun ecdsaPeer() { + val hex = "308201c030820166a003020102020900eaf419a6e3edb4a6300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d030107034200048dbf1116c7c608d6d5292bd826c3feb53483a89fce434bf64538a359c8e07538ff71f6766239be6a146dcc1a5f3bb934bcd4ae2ae1d4da28ac68b4a20593f06ba381c63081c33081c0060a2b0601040183a25a01010101ff0481ae3081ab045f0803125b3059301306072a8648ce3d020106082a8648ce3d0301070342000484b93fa456a74bd0153919f036db7bc63c802f055bc7023395d0203de718ee0fc7b570b767cdd858aca6c7c4113ff002e78bd2138ac1a3b26dde3519e06979ad04483046022100bc84014cea5a41feabdf4c161096564b9ccf4b62fbef4fe1cd382c84e11101780221009204f086a84cb8ed8a9ddd7868dc90c792ee434adf62c66f99a08a5eba11615b300a06082a8648ce3d0403020348003045022054b437be9a2edf591312d68ff24bf91367ad4143f76cf80b5658f232ade820da022100e23b48de9df9c25d4c83ddddf75d2676f0b9318ee2a6c88a736d85eab94a912f" + val certBytes = Hex.decode(hex) + val certHolder = X509CertificateHolder(certBytes) + val cert = JcaX509CertificateConverter().setProvider(BouncyCastleProvider()).getCertificate(certHolder) + val peerIdFromCert = verifyAndExtractPeerId(arrayOf(cert)) + val expectedPeerId = PeerId.fromBase58("QmZcrvr3r4S3QvwFdae3c2EWTfo792Y14UpzCZurhmiWeX") + assertEquals(peerIdFromCert, expectedPeerId) + } + + @Test + fun secp256k1Peer() { + val hex = "3082018230820128a003020102020900f3b305f55622cfdf300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d0301070342000458f7e9581748ff9bdd933b655cc0e5552a1248f840658cc221dec2186b5a2fe4641b86ab7590a3422cdbb1000cf97662f27e5910d7569f22feed8829c8b52e0fa38188308185308182060a2b0601040183a25a01010101ff0471306f042508021221026b053094d1112bce799dc8026040ae6d4eb574157929f1598172061f753d9b1b04463044022040712707e97794c478d93989aaa28ae1f71c03af524a8a4bd2d98424948a782302207b61b7f074b696a25fb9e0059141a811cccc4cc28042d9301b9b2a4015e87470300a06082a8648ce3d04030203480030450220143ae4d86fdc8675d2480bb6912eca5e39165df7f572d836aa2f2d6acfab13f8022100831d1979a98f0c4a6fb5069ca374de92f1a1205c962a6d90ad3d7554cb7d9df4" + val certBytes = Hex.decode(hex) + val certHolder = X509CertificateHolder(certBytes) + val cert = JcaX509CertificateConverter().setProvider(BouncyCastleProvider()).getCertificate(certHolder) + val peerIdFromCert = verifyAndExtractPeerId(arrayOf(cert)) + val expectedPeerId = PeerId.fromBase58("16Uiu2HAm2dSCBFxuge46aEt7U1oejtYuBUZXxASHqmcfVmk4gsbx") + assertEquals(peerIdFromCert, expectedPeerId) + } + + @Test + fun invalidCert() { + val hex = "308201773082011da003020102020830a73c5d896a1109300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d03010703420004bbe62df9a7c1c46b7f1f21d556deec5382a36df146fb29c7f1240e60d7d5328570e3b71d99602b77a65c9b3655f62837f8d66b59f1763b8c9beba3be07778043a37f307d307b060a2b0601040183a25a01010101ff046a3068042408011220ec8094573afb9728088860864f7bcea2d4fd412fef09a8e2d24d482377c20db60440ecabae8354afa2f0af4b8d2ad871e865cb5a7c0c8d3dbdbf42de577f92461a0ebb0a28703e33581af7d2a4f2270fc37aec6261fcc95f8af08f3f4806581c730a300a06082a8648ce3d040302034800304502202dfb17a6fa0f94ee0e2e6a3b9fb6e986f311dee27392058016464bd130930a61022100ba4b937a11c8d3172b81e7cd04aedb79b978c4379c2b5b24d565dd5d67d3cb3c" + val certBytes = Hex.decode(hex) + val certHolder = X509CertificateHolder(certBytes) + val cert = JcaX509CertificateConverter().setProvider(BouncyCastleProvider()).getCertificate(certHolder) + try { + verifyAndExtractPeerId(arrayOf(cert)) + throw java.lang.RuntimeException("Failed") + } catch (e: IllegalStateException) {} + } + + @Test + fun buildEd25519Cert() { + val host = generateEd25519KeyPair() + val conn = generateEd25519KeyPair() + val cert = buildCert(host.first, conn.first) + val peerIdFromCert = verifyAndExtractPeerId(arrayOf(cert)) + val expectedPeerId = PeerId.fromPubKey(host.second) + assertEquals(peerIdFromCert, expectedPeerId) + } + + +} \ No newline at end of file From 1c1edba836d3190a4f22c6d2b0af89a1cd63f795 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 22 Feb 2023 10:16:20 +0000 Subject: [PATCH 03/37] Update to Java 17 minimum, which Ed25519 asn1 needs --- .github/workflows/build.yml | 2 +- build.gradle.kts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4991a5968..1c816f3c1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/setup-java@v3 with: distribution: temurin - java-version: 11 + java-version: 17 - name: Setup Gradle uses: gradle/gradle-build-action@v2 diff --git a/build.gradle.kts b/build.gradle.kts index b6e9a7271..46075ba77 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -70,12 +70,12 @@ configure( } java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } tasks.withType { - kotlinOptions.jvmTarget = "11" + kotlinOptions.jvmTarget = "17" kotlinOptions { languageVersion = "1.6" allWarningsAsErrors = true @@ -121,7 +121,7 @@ configure( outputDirectory.set(buildDir.resolve("dokka")) dokkaSourceSets { configureEach { - jdkVersion.set(11) + jdkVersion.set(17) reportUndocumented.set(false) externalDocumentationLink { url.set(URL("https://netty.io/4.1/api/")) From ff32b9c8c75843d4cb5d2397c951a94471b81050 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 22 Feb 2023 10:28:50 +0000 Subject: [PATCH 04/37] Improve tls cert test --- .../test/kotlin/io/libp2p/security/tls/CertificatesTest.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libp2p/src/test/kotlin/io/libp2p/security/tls/CertificatesTest.kt b/libp2p/src/test/kotlin/io/libp2p/security/tls/CertificatesTest.kt index 69cb9e4e1..bb463888b 100644 --- a/libp2p/src/test/kotlin/io/libp2p/security/tls/CertificatesTest.kt +++ b/libp2p/src/test/kotlin/io/libp2p/security/tls/CertificatesTest.kt @@ -8,6 +8,7 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.util.encoders.Hex import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows class CertificatesTest { @@ -50,10 +51,7 @@ class CertificatesTest { val certBytes = Hex.decode(hex) val certHolder = X509CertificateHolder(certBytes) val cert = JcaX509CertificateConverter().setProvider(BouncyCastleProvider()).getCertificate(certHolder) - try { - verifyAndExtractPeerId(arrayOf(cert)) - throw java.lang.RuntimeException("Failed") - } catch (e: IllegalStateException) {} + assertThrows({ verifyAndExtractPeerId(arrayOf(cert))}) } @Test From 5d946d1db34b41ff1588179cf11f7bd1ce384919 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 22 Feb 2023 10:38:39 +0000 Subject: [PATCH 05/37] Linting --- .../libp2p/security/tls/TLSSecureChannel.kt | 47 +++++++------------ 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt index 53eab2f6d..b4f37f11b 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -5,7 +5,6 @@ import io.libp2p.core.P2PChannel import io.libp2p.core.PeerId import io.libp2p.core.crypto.PrivKey import io.libp2p.core.crypto.PubKey -import io.libp2p.core.crypto.marshalPublicKey import io.libp2p.core.crypto.unmarshalPublicKey import io.libp2p.core.multistream.ProtocolDescriptor import io.libp2p.core.security.SecureChannel @@ -26,9 +25,7 @@ import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.cert.X509v3CertificateBuilder import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter -import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters -import org.bouncycastle.jcajce.interfaces.EdDSAPublicKey import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder import java.math.BigInteger @@ -44,7 +41,6 @@ import java.util.concurrent.CompletableFuture import java.util.logging.Logger import kotlin.experimental.and - private val log = Logger.getLogger(TlsSecureChannel::class.java.name) const val MaxCipheredPacketLength = 65535 val certificatePrefix = "libp2p-tls-handshake:".encodeToByteArray() @@ -86,18 +82,22 @@ class TlsSecureChannel(private val localKey: PrivKey) : ch.pushHandler(handler) val handshake = handler.handshakeFuture() val engine = handler.engine() - handshake.addListener { _ -> handshakeComplete.complete(SecureChannel.Session( - PeerId.fromPubKey(localKey.publicKey()), - verifyAndExtractPeerId(engine.getSession().getPeerCertificates()), - getPublicKeyFromCert(engine.getSession().getPeerCertificates()) - )) } + handshake.addListener { _ -> + handshakeComplete.complete( + SecureChannel.Session( + PeerId.fromPubKey(localKey.publicKey()), + verifyAndExtractPeerId(engine.getSession().getPeerCertificates()), + getPublicKeyFromCert(engine.getSession().getPeerCertificates()) + ) + ) + } return handshakeComplete } } fun getJavaKey(priv: PrivKey): PrivateKey { if (priv.keyType == Crypto.KeyType.Ed25519) { - val kf = KeyFactory.getInstance("Ed25519"); + val kf = KeyFactory.getInstance("Ed25519") val privKeyInfo = PrivateKeyInfo(AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), DEROctetString(priv.raw())) val pkcs8KeySpec = PKCS8EncodedKeySpec(privKeyInfo.encoded) @@ -111,20 +111,20 @@ fun getJavaKey(priv: PrivKey): PrivateKey { fun getJavaPublicKey(pub: PubKey): PublicKey { if (pub.keyType == Crypto.KeyType.Ed25519) { - val kf = KeyFactory.getInstance("Ed25519"); + val kf = KeyFactory.getInstance("Ed25519") // determine if x was odd. var pk = pub.raw() - val lastbyteInt = pk[pk.lastIndex].toInt(); - var xisodd = lastbyteInt.and(255).shr(7) == 1; + val lastbyteInt = pk[pk.lastIndex].toInt() + var xisodd = lastbyteInt.and(255).shr(7) == 1 // make sure most significant bit will be 0 - after reversing. pk[31] = pk[31].and(127); val y = BigInteger(1, pk.reversedArray()); - val paramSpec = NamedParameterSpec("Ed25519"); - val ep = EdECPoint(xisodd, y); - val pubSpec = EdECPublicKeySpec(paramSpec, ep); - return kf.generatePublic(pubSpec); + val paramSpec = NamedParameterSpec("Ed25519") + val ep = EdECPoint(xisodd, y) + val pubSpec = EdECPublicKeySpec(paramSpec, ep) + return kf.generatePublic(pubSpec) } throw IllegalArgumentException("Unsupported TLS key type:" + pub.keyType) } @@ -143,7 +143,7 @@ fun verifyAndExtractPeerId(chain: Array): PeerId { val cert = chain.get(0) // peerid is in the certificate extension val bcCert = org.bouncycastle.asn1.x509.Certificate - .getInstance(ASN1Primitive.fromByteArray(cert.getEncoded())) + .getInstance(ASN1Primitive.fromByteArray(cert.getEncoded())) val bcX509Cert = X509CertificateHolder(bcCert) val libp2pOid = ASN1ObjectIdentifier("1.3.6.1.4.1.53594.1.1") val extension = bcX509Cert.extensions.getExtension(libp2pOid) @@ -174,17 +174,6 @@ fun getPublicKeyFromCert(chain: Array): PubKey { return getPubKey(cert.publicKey) } -fun toAsn1(pub: PubKey): ByteArray { - // Shouldn't be using RSA here - if (pub.keyType == Crypto.KeyType.Ed25519) { - return Ed25519PublicKeyParameters(pub.raw()).encoded - } - if (pub.keyType == Crypto.KeyType.ECDSA) { - - } - throw IllegalStateException("Unsupported key type for TLS: " + pub.keyType) -} - /** Build a self signed cert, with an extension containing the host key + sig(cert public key) * */ From cab2522caacef7e60a0c79e6ecaa39651688ffc2 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 22 Feb 2023 10:49:06 +0000 Subject: [PATCH 06/37] More linting --- .../main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt index b4f37f11b..2ee2c57dd 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -118,8 +118,8 @@ fun getJavaPublicKey(pub: PubKey): PublicKey { val lastbyteInt = pk[pk.lastIndex].toInt() var xisodd = lastbyteInt.and(255).shr(7) == 1 // make sure most significant bit will be 0 - after reversing. - pk[31] = pk[31].and(127); - val y = BigInteger(1, pk.reversedArray()); + pk[31] = pk[31].and(127) + val y = BigInteger(1, pk.reversedArray()) val paramSpec = NamedParameterSpec("Ed25519") val ep = EdECPoint(xisodd, y) @@ -177,7 +177,7 @@ fun getPublicKeyFromCert(chain: Array): PubKey { /** Build a self signed cert, with an extension containing the host key + sig(cert public key) * */ -fun buildCert(hostKey: PrivKey, subjectKey: PrivKey) : X509Certificate { +fun buildCert(hostKey: PrivKey, subjectKey: PrivKey): X509Certificate { val publicKeyAsn1 = getJavaPublicKey(subjectKey.publicKey()).encoded val subPubKeyInfo = SubjectPublicKeyInfo.getInstance(publicKeyAsn1) From ddaece6f878008646a1e6161bb407a95fdf9c2e5 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 22 Feb 2023 10:54:28 +0000 Subject: [PATCH 07/37] Lint tests --- .../test/kotlin/io/libp2p/security/tls/CertificatesTest.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libp2p/src/test/kotlin/io/libp2p/security/tls/CertificatesTest.kt b/libp2p/src/test/kotlin/io/libp2p/security/tls/CertificatesTest.kt index bb463888b..feb2e6a98 100644 --- a/libp2p/src/test/kotlin/io/libp2p/security/tls/CertificatesTest.kt +++ b/libp2p/src/test/kotlin/io/libp2p/security/tls/CertificatesTest.kt @@ -51,7 +51,7 @@ class CertificatesTest { val certBytes = Hex.decode(hex) val certHolder = X509CertificateHolder(certBytes) val cert = JcaX509CertificateConverter().setProvider(BouncyCastleProvider()).getCertificate(certHolder) - assertThrows({ verifyAndExtractPeerId(arrayOf(cert))}) + assertThrows({ verifyAndExtractPeerId(arrayOf(cert)) }) } @Test @@ -63,6 +63,4 @@ class CertificatesTest { val expectedPeerId = PeerId.fromPubKey(host.second) assertEquals(peerIdFromCert, expectedPeerId) } - - -} \ No newline at end of file +} From 3a2cbb0691e30c515c8377286ba6a256fe113dc2 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 23 Feb 2023 01:58:31 +0000 Subject: [PATCH 08/37] First successful libp2p-tls connections!! (Java-Java) Implemented full trust manager TODO: * Muxer negotiation in client hello info * close stream on cipher error --- .../libp2p/security/tls/TLSSecureChannel.kt | 128 +++++++++++++++--- 1 file changed, 111 insertions(+), 17 deletions(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt index 2ee2c57dd..630482829 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -1,8 +1,7 @@ package io.libp2p.security.tls import crypto.pb.Crypto -import io.libp2p.core.P2PChannel -import io.libp2p.core.PeerId +import io.libp2p.core.* import io.libp2p.core.crypto.PrivKey import io.libp2p.core.crypto.PubKey import io.libp2p.core.crypto.unmarshalPublicKey @@ -10,12 +9,19 @@ import io.libp2p.core.multistream.ProtocolDescriptor import io.libp2p.core.security.SecureChannel import io.libp2p.crypto.keys.Ed25519PublicKey import io.libp2p.crypto.keys.generateEd25519KeyPair +import io.libp2p.etc.REMOTE_PEER_ID +import io.libp2p.security.InvalidRemotePubKey +import io.netty.buffer.ByteBuf import io.netty.buffer.PooledByteBufAllocator +import io.netty.channel.ChannelHandlerContext import io.netty.channel.CombinedChannelDuplexHandler +import io.netty.channel.SimpleChannelInboundHandler import io.netty.handler.codec.LengthFieldBasedFrameDecoder import io.netty.handler.codec.LengthFieldPrepender import io.netty.handler.ssl.ClientAuth import io.netty.handler.ssl.SslContextBuilder +import io.netty.handler.ssl.SslHandler +import io.netty.util.ReferenceCountUtil import org.bouncycastle.asn1.* import org.bouncycastle.asn1.edec.EdECObjectIdentifiers import org.bouncycastle.asn1.pkcs.PrivateKeyInfo @@ -33,15 +39,21 @@ import java.security.KeyFactory import java.security.PrivateKey import java.security.PublicKey import java.security.cert.Certificate +import java.security.cert.CertificateException import java.security.cert.X509Certificate +import java.security.interfaces.EdECPublicKey import java.security.spec.* import java.time.Instant import java.util.* import java.util.concurrent.CompletableFuture +import java.util.logging.Level import java.util.logging.Logger +import javax.net.ssl.X509TrustManager import kotlin.experimental.and +import kotlin.experimental.or private val log = Logger.getLogger(TlsSecureChannel::class.java.name) +private val SetupHandlerName = "TlsSetup" const val MaxCipheredPacketLength = 65535 val certificatePrefix = "libp2p-tls-handshake:".encodeToByteArray() @@ -71,18 +83,35 @@ class TlsSecureChannel(private val localKey: PrivKey) : ch.pushHandler(UShortLengthCodec()) // Packet length codec should stay forever. - ch.isInitiator - val connectionKeys = generateEd25519KeyPair() - val javaPrivateKey = getJavaKey(connectionKeys.first) - val sslContext = SslContextBuilder.forServer(javaPrivateKey, listOf(buildCert(localKey, connectionKeys.first))) - .protocols(listOf("TLSv1.3")) - .clientAuth(ClientAuth.REQUIRE) - .build() - val handler = sslContext.newHandler(PooledByteBufAllocator.DEFAULT) - ch.pushHandler(handler) - val handshake = handler.handshakeFuture() - val engine = handler.engine() - handshake.addListener { _ -> + ch.pushHandler(SetupHandlerName, ChannelSetup(localKey, ch.isInitiator, handshakeComplete)) + return handshakeComplete + } +} + +fun buildTlsHandler(localKey: PrivKey, + expectedRemotePeer: Optional, + isInitiator: Boolean, + handshakeComplete: CompletableFuture, + ctx: ChannelHandlerContext): SslHandler { + val connectionKeys = generateEd25519KeyPair() + val javaPrivateKey = getJavaKey(connectionKeys.first) + val sslContext = (if (isInitiator) + SslContextBuilder.forClient().keyManager(javaPrivateKey, listOf(buildCert(localKey, connectionKeys.first))) + else + SslContextBuilder.forServer(javaPrivateKey, listOf(buildCert(localKey, connectionKeys.first)))) + .protocols(listOf("TLSv1.3")) + .ciphers(listOf("TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256")) + .clientAuth(ClientAuth.REQUIRE) + .trustManager(Libp2pTrustManager(expectedRemotePeer)) + .build() + val handler = sslContext.newHandler(PooledByteBufAllocator.DEFAULT) + handler.sslCloseFuture().addListener { _ -> ctx.close() } + val handshake = handler.handshakeFuture() + val engine = handler.engine() + handshake.addListener { fut -> + if (! fut.isSuccess) + handshakeComplete.completeExceptionally(fut.cause().cause) + else handshakeComplete.complete( SecureChannel.Session( PeerId.fromPubKey(localKey.publicKey()), @@ -90,8 +119,65 @@ class TlsSecureChannel(private val localKey: PrivKey) : getPublicKeyFromCert(engine.getSession().getPeerCertificates()) ) ) + } + println("libp2p-tls using suites: " + sslContext.cipherSuites()) + return handler +} + +private class ChannelSetup( + private val localKey: PrivKey, + private val isInitiator: Boolean, + private val handshakeComplete: CompletableFuture +) : SimpleChannelInboundHandler() { + private var activated = false + + override fun channelActive(ctx: ChannelHandlerContext) { + if (! activated) { + activated = true + val expectedRemotePeerId = ctx.channel().attr(REMOTE_PEER_ID).get() + ctx.channel().pipeline().remove(SetupHandlerName) + ctx.channel().pipeline().addLast(buildTlsHandler(localKey, Optional.ofNullable(expectedRemotePeerId), isInitiator, handshakeComplete, ctx)) } - return handshakeComplete + } + + override fun channelRead0(ctx: ChannelHandlerContext, msg: ByteBuf) { + // it seems there is no guarantee from Netty that channelActive() must be called before channelRead() + channelActive(ctx) + ctx.fireChannelActive() + ReferenceCountUtil.retain(msg) + } + + private fun writeAndFlush(ctx: ChannelHandlerContext, bb: ByteBuf) { + ctx.writeAndFlush(bb) + } + + override fun exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable) { + handshakeComplete.completeExceptionally(cause) + log.log(Level.FINE, "TLS setup failed", cause) + ctx.channel().close() + } + + override fun channelUnregistered(ctx: ChannelHandlerContext) { + handshakeComplete.completeExceptionally(ConnectionClosedException("Connection was closed ${ctx.channel()}")) + super.channelUnregistered(ctx) + } +} + +class Libp2pTrustManager(private val expectedRemotePeer: Optional): X509TrustManager { + override fun checkClientTrusted(certs: Array?, authType: String?) { + if (certs?.size != 1) + throw CertificateException() + val claimedPeerId = verifyAndExtractPeerId(arrayOf(certs.get(0))) + if (expectedRemotePeer.map { ex -> ! ex.equals(claimedPeerId) }.orElse(false)) + throw InvalidRemotePubKey() + } + + override fun checkServerTrusted(certs: Array?, authType: String?) { + return checkClientTrusted(certs, authType) + } + + override fun getAcceptedIssuers(): Array { + return arrayOf() } } @@ -130,8 +216,16 @@ fun getJavaPublicKey(pub: PubKey): PublicKey { } fun getPubKey(pub: PublicKey): PubKey { - if (pub.algorithm.equals("Ed25519")) - return Ed25519PublicKey(Ed25519PublicKeyParameters(pub.encoded)) + if (pub.algorithm.equals("EdDSA") || pub.algorithm.equals("Ed25519")) { + // It seems batshit that we have to do this, but haven't found an equivalent library call + val point = (pub as EdECPublicKey).point + var pk = point.y.toByteArray().reversedArray() + if (pk.size == 31) + pk = pk.plus(0) + if (point.isXOdd) + pk[31] = pk[31].or(0x80.toByte()) + return Ed25519PublicKey(Ed25519PublicKeyParameters(pk)) + } if (pub.algorithm.equals("RSA")) throw IllegalStateException("Unimplemented RSA public key support for TLS") throw IllegalStateException("Unsupported key type: " + pub.algorithm) From 923a043576fab69480f67404e1a645c43995d186 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 23 Feb 2023 09:34:48 +0000 Subject: [PATCH 09/37] Add TLS tests --- .../libp2p/security/tls/TLSSecureChannel.kt | 22 ++++++----- .../security/tls/TlsSecureChannelTest.kt | 39 +++++++++++++++++++ 2 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt index 630482829..e1bde5ca1 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -88,17 +88,21 @@ class TlsSecureChannel(private val localKey: PrivKey) : } } -fun buildTlsHandler(localKey: PrivKey, - expectedRemotePeer: Optional, - isInitiator: Boolean, - handshakeComplete: CompletableFuture, - ctx: ChannelHandlerContext): SslHandler { +fun buildTlsHandler( + localKey: PrivKey, + expectedRemotePeer: Optional, + isInitiator: Boolean, + handshakeComplete: CompletableFuture, + ctx: ChannelHandlerContext +): SslHandler { val connectionKeys = generateEd25519KeyPair() val javaPrivateKey = getJavaKey(connectionKeys.first) - val sslContext = (if (isInitiator) - SslContextBuilder.forClient().keyManager(javaPrivateKey, listOf(buildCert(localKey, connectionKeys.first))) - else - SslContextBuilder.forServer(javaPrivateKey, listOf(buildCert(localKey, connectionKeys.first)))) + val sslContext = ( + if (isInitiator) + SslContextBuilder.forClient().keyManager(javaPrivateKey, listOf(buildCert(localKey, connectionKeys.first))) + else + SslContextBuilder.forServer(javaPrivateKey, listOf(buildCert(localKey, connectionKeys.first))) + ) .protocols(listOf("TLSv1.3")) .ciphers(listOf("TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256")) .clientAuth(ClientAuth.REQUIRE) diff --git a/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt b/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt new file mode 100644 index 000000000..fd4a1e7fa --- /dev/null +++ b/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt @@ -0,0 +1,39 @@ +package io.libp2p.security.tls + +import io.libp2p.core.PeerId +import io.libp2p.core.crypto.KEY_TYPE +import io.libp2p.core.crypto.generateKeyPair +import io.libp2p.security.InvalidRemotePubKey +import io.libp2p.security.SecureChannelTestBase +import io.libp2p.security.logger +import io.libp2p.tools.TestChannel +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Tag +import org.junit.jupiter.api.Test +import java.util.concurrent.TimeUnit +import java.util.logging.Level + +@Tag("secure-channel") +class TlsSecureChannelTest : SecureChannelTestBase( + ::TlsSecureChannel, + TlsSecureChannel.announce +) { + @Test + fun `incorrect initiator remote PeerId should throw`() { + val (privKey1, _) = generateKeyPair(KEY_TYPE.ECDSA) + val (privKey2, _) = generateKeyPair(KEY_TYPE.ECDSA) + val (_, wrongPubKey) = generateKeyPair(KEY_TYPE.ECDSA) + + val protocolSelect1 = makeSelector(privKey1) + val protocolSelect2 = makeSelector(privKey2) + + val eCh1 = makeDialChannel("#1", protocolSelect1, PeerId.fromPubKey(wrongPubKey)) + val eCh2 = makeListenChannel("#2", protocolSelect2) + + logger.log(Level.FINE, "Connecting channels...") + TestChannel.interConnect(eCh1, eCh2) + + Assertions.assertThatThrownBy { protocolSelect1.selectedFuture.get(10, TimeUnit.SECONDS) } + .hasCauseInstanceOf(InvalidRemotePubKey::class.java) + } +} From 52fc181b2526df0aa3fed17c8a4643d581d0ba3f Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 23 Feb 2023 09:39:49 +0000 Subject: [PATCH 10/37] Linting --- .../io/libp2p/security/tls/TLSSecureChannel.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt index e1bde5ca1..e2364d48c 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -98,11 +98,11 @@ fun buildTlsHandler( val connectionKeys = generateEd25519KeyPair() val javaPrivateKey = getJavaKey(connectionKeys.first) val sslContext = ( - if (isInitiator) - SslContextBuilder.forClient().keyManager(javaPrivateKey, listOf(buildCert(localKey, connectionKeys.first))) - else - SslContextBuilder.forServer(javaPrivateKey, listOf(buildCert(localKey, connectionKeys.first))) - ) + if (isInitiator) + SslContextBuilder.forClient().keyManager(javaPrivateKey, listOf(buildCert(localKey, connectionKeys.first))) + else + SslContextBuilder.forServer(javaPrivateKey, listOf(buildCert(localKey, connectionKeys.first))) + ) .protocols(listOf("TLSv1.3")) .ciphers(listOf("TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256")) .clientAuth(ClientAuth.REQUIRE) @@ -167,7 +167,7 @@ private class ChannelSetup( } } -class Libp2pTrustManager(private val expectedRemotePeer: Optional): X509TrustManager { +class Libp2pTrustManager(private val expectedRemotePeer: Optional) : X509TrustManager { override fun checkClientTrusted(certs: Array?, authType: String?) { if (certs?.size != 1) throw CertificateException() From c85991e1cf77c6a8c107d89a10bc71403f8dcf14 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 23 Feb 2023 09:53:48 +0000 Subject: [PATCH 11/37] Use jdk17 in jitpack --- jitpack.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 jitpack.yml diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 000000000..1e41e00b7 --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,2 @@ +jdk: + - openjdk17 \ No newline at end of file From aa867f6c2b02e848101ea2d45c97fc1cf4534821 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 24 Feb 2023 10:46:38 +0000 Subject: [PATCH 12/37] Implement early muxer negiation in TLS using ALPN Remove Secio from defaults!! Kubo still drops us during handshake --- .../java/io/libp2p/core/dsl/HostBuilder.java | 11 +++--- .../kotlin/io/libp2p/core/dsl/Builders.kt | 9 +++-- .../io/libp2p/core/security/SecureChannel.kt | 7 +++- .../noise/NoiseSecureChannelSession.kt | 2 +- .../security/noise/NoiseXXSecureChannel.kt | 2 +- .../plaintext/PlaintextInsecureChannel.kt | 5 ++- .../security/secio/SecIoSecureChannel.kt | 5 ++- .../libp2p/security/tls/TLSSecureChannel.kt | 38 +++++++++++++------ .../io/libp2p/transport/ConnectionUpgrader.kt | 15 +++++++- .../implementation/ConnectionBuilder.kt | 2 +- .../security/CipherSecureChannelTest.kt | 12 +++--- .../libp2p/security/SecureChannelTestBase.kt | 9 +++-- .../security/noise/NoiseHandshakeTest.kt | 6 +-- .../security/noise/NoiseSecureChannelTest.kt | 1 + .../plaintext/PlaintextInsecureChannelTest.kt | 1 + .../libp2p/security/secio/EchoSampleTest.kt | 2 +- .../security/secio/SecIoSecureChannelTest.kt | 1 + .../security/tls/TlsSecureChannelTest.kt | 10 ++++- .../kotlin/io/libp2p/pubsub/TestRouter.kt | 2 +- .../transport/NullConnectionUpgrader.kt | 3 +- 20 files changed, 95 insertions(+), 48 deletions(-) diff --git a/libp2p/src/main/java/io/libp2p/core/dsl/HostBuilder.java b/libp2p/src/main/java/io/libp2p/core/dsl/HostBuilder.java index f32724268..e5e25b761 100644 --- a/libp2p/src/main/java/io/libp2p/core/dsl/HostBuilder.java +++ b/libp2p/src/main/java/io/libp2p/core/dsl/HostBuilder.java @@ -3,7 +3,7 @@ import io.libp2p.core.Host; import io.libp2p.core.crypto.PrivKey; import io.libp2p.core.multistream.ProtocolBinding; -import io.libp2p.core.mux.StreamMuxerProtocol; +import io.libp2p.core.mux.*; import io.libp2p.core.security.SecureChannel; import io.libp2p.core.transport.Transport; import io.libp2p.transport.ConnectionUpgrader; @@ -11,8 +11,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.function.Function; -import java.util.function.Supplier; +import java.util.function.*; public class HostBuilder { public HostBuilder() { this(DefaultMode.Standard); } @@ -41,7 +40,7 @@ public final HostBuilder transport( @SafeVarargs public final HostBuilder secureChannel( - Function... secureChannels) { + BiFunction, SecureChannel>... secureChannels) { secureChannels_.addAll(Arrays.asList(secureChannels)); return this; } @@ -76,7 +75,7 @@ public Host build() { b.getTransports().add(t::apply) ); secureChannels_.forEach(sc -> - b.getSecureChannels().add(sc::apply) + b.getSecureChannels().add((k, m) -> sc.apply(k, (List)m)) ); muxers_.forEach(m -> b.getMuxers().add(m.get()) @@ -91,7 +90,7 @@ public Host build() { private DefaultMode defaultMode_; private List> transports_ = new ArrayList<>(); - private List> secureChannels_ = new ArrayList<>(); + private List, SecureChannel>> secureChannels_ = new ArrayList<>(); private List> muxers_ = new ArrayList<>(); private List> protocols_ = new ArrayList<>(); private List listenAddresses_ = new ArrayList<>(); diff --git a/libp2p/src/main/kotlin/io/libp2p/core/dsl/Builders.kt b/libp2p/src/main/kotlin/io/libp2p/core/dsl/Builders.kt index 203aa476f..d0482e122 100644 --- a/libp2p/src/main/kotlin/io/libp2p/core/dsl/Builders.kt +++ b/libp2p/src/main/kotlin/io/libp2p/core/dsl/Builders.kt @@ -27,6 +27,7 @@ import io.libp2p.host.HostImpl import io.libp2p.host.MemoryAddressBook import io.libp2p.network.NetworkImpl import io.libp2p.protocol.IdentifyBinding +import io.libp2p.security.noise.NoiseXXSecureChannel import io.libp2p.security.secio.SecIoSecureChannel import io.libp2p.transport.ConnectionUpgrader import io.libp2p.transport.tcp.TcpTransport @@ -35,7 +36,7 @@ import io.netty.handler.logging.LogLevel import io.netty.handler.logging.LoggingHandler typealias TransportCtor = (ConnectionUpgrader) -> Transport -typealias SecureChannelCtor = (PrivKey) -> SecureChannel +typealias SecureChannelCtor = (PrivKey, List) -> SecureChannel typealias IdentityFactory = () -> PrivKey class HostConfigurationException(message: String) : RuntimeException(message) @@ -131,7 +132,7 @@ open class Builder { if (def == Defaults.Standard) { if (identity.factory == null) identity.random() if (transports.values.isEmpty()) transports { add(::TcpTransport) } - if (secureChannels.values.isEmpty()) secureChannels { add(::SecIoSecureChannel) } + if (secureChannels.values.isEmpty()) secureChannels { add(::NoiseXXSecureChannel) } if (muxers.values.isEmpty()) muxers { add(StreamMuxerProtocol.Mplex) } } @@ -160,8 +161,6 @@ open class Builder { val privKey = identity.factory!!() - val secureChannels = secureChannels.values.map { it(privKey) } - protocols.values.mapNotNull { (it as? IdentifyBinding) }.map { it.protocol }.find { it.idMessage == null }?.apply { // initializing Identify with appropriate values IdentifyOuterClass.Identify.newBuilder().apply { @@ -177,6 +176,8 @@ open class Builder { val muxers = muxers.map { it.createMuxer(streamMultistreamProtocol, protocols.values) } + val secureChannels = secureChannels.values.map { it(privKey, muxers.flatMap { it.protocolDescriptor.announceProtocols }) } + if (debug.muxFramesHandler.handlers.isNotEmpty()) { val broadcast = ChannelVisitor.createBroadcast(*debug.muxFramesHandler.handlers.toTypedArray()) muxers.mapNotNull { it as? StreamMuxerDebug }.forEach { diff --git a/libp2p/src/main/kotlin/io/libp2p/core/security/SecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/core/security/SecureChannel.kt index ce12552b9..66fd4ed19 100644 --- a/libp2p/src/main/kotlin/io/libp2p/core/security/SecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/core/security/SecureChannel.kt @@ -22,6 +22,11 @@ interface SecureChannel : ProtocolBinding { /** * The public key of the remote peer. */ - val remotePubKey: PubKey + val remotePubKey: PubKey, + + /** The id of the next protocol, used to select the muxer + * + */ + val nextProto: String ) } diff --git a/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseSecureChannelSession.kt b/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseSecureChannelSession.kt index b6e5485ef..d1ffc03fa 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseSecureChannelSession.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseSecureChannelSession.kt @@ -11,4 +11,4 @@ class NoiseSecureChannelSession( remotePubKey: PubKey, val aliceCipher: CipherState, val bobCipher: CipherState -) : SecureChannel.Session(localId, remoteId, remotePubKey) +) : SecureChannel.Session(localId, remoteId, remotePubKey, "") diff --git a/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseXXSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseXXSecureChannel.kt index c894a366c..4c1ccd70b 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseXXSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseXXSecureChannel.kt @@ -45,7 +45,7 @@ class UShortLengthCodec : CombinedChannelDuplexHandler) : SecureChannel { companion object { diff --git a/libp2p/src/main/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannel.kt index 66518ef18..2a5da5173 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannel.kt @@ -22,7 +22,7 @@ import io.netty.handler.codec.LengthFieldPrepender import plaintext.pb.Plaintext import java.util.concurrent.CompletableFuture -class PlaintextInsecureChannel(private val localKey: PrivKey) : SecureChannel { +class PlaintextInsecureChannel(private val localKey: PrivKey, private val muxerIds: List) : SecureChannel { override val protocolDescriptor = ProtocolDescriptor("/plaintext/2.0.0") override fun initChannel(ch: P2PChannel, selectedProtocol: String): CompletableFuture { @@ -107,7 +107,8 @@ class PlaintextHandshakeHandler( val session = SecureChannel.Session( localPeerId, remotePeerId, - remotePubKey + remotePubKey, + "" ) handshakeCompleted.complete(session) diff --git a/libp2p/src/main/kotlin/io/libp2p/security/secio/SecIoSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/secio/SecIoSecureChannel.kt index c0a026d54..13379ab9c 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/secio/SecIoSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/secio/SecIoSecureChannel.kt @@ -18,7 +18,7 @@ import java.util.concurrent.CompletableFuture private val log = LoggerFactory.getLogger(SecIoSecureChannel::class.java) private val HandshakeHandlerName = "SecIoHandshake" -class SecIoSecureChannel(private val localKey: PrivKey) : SecureChannel { +class SecIoSecureChannel(private val localKey: PrivKey, private val muxerIds: List) : SecureChannel { override val protocolDescriptor = ProtocolDescriptor("/secio/1.0.0") override fun initChannel(ch: P2PChannel, selectedProtocol: String): CompletableFuture { @@ -68,7 +68,8 @@ private class SecIoHandshake( val session = SecureChannel.Session( PeerId.fromPubKey(secIoCodec.local.permanentPubKey), PeerId.fromPubKey(secIoCodec.remote.permanentPubKey), - secIoCodec.remote.permanentPubKey + secIoCodec.remote.permanentPubKey, + "" ) handshakeComplete.complete(session) ctx.channel().pipeline().remove(HandshakeHandlerName) diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt index e2364d48c..9152a484d 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -18,6 +18,7 @@ import io.netty.channel.CombinedChannelDuplexHandler import io.netty.channel.SimpleChannelInboundHandler import io.netty.handler.codec.LengthFieldBasedFrameDecoder import io.netty.handler.codec.LengthFieldPrepender +import io.netty.handler.ssl.ApplicationProtocolConfig import io.netty.handler.ssl.ClientAuth import io.netty.handler.ssl.SslContextBuilder import io.netty.handler.ssl.SslHandler @@ -62,7 +63,7 @@ class UShortLengthCodec : CombinedChannelDuplexHandler) : SecureChannel { companion object { @@ -83,7 +84,7 @@ class TlsSecureChannel(private val localKey: PrivKey) : ch.pushHandler(UShortLengthCodec()) // Packet length codec should stay forever. - ch.pushHandler(SetupHandlerName, ChannelSetup(localKey, ch.isInitiator, handshakeComplete)) + ch.pushHandler(SetupHandlerName, ChannelSetup(localKey, muxerIds, ch, handshakeComplete)) return handshakeComplete } } @@ -91,14 +92,16 @@ class TlsSecureChannel(private val localKey: PrivKey) : fun buildTlsHandler( localKey: PrivKey, expectedRemotePeer: Optional, - isInitiator: Boolean, + muxerIds: List, + ch: P2PChannel, handshakeComplete: CompletableFuture, ctx: ChannelHandlerContext ): SslHandler { val connectionKeys = generateEd25519KeyPair() val javaPrivateKey = getJavaKey(connectionKeys.first) + println("TLS supporting muxers: " + muxerIds) val sslContext = ( - if (isInitiator) + if (ch.isInitiator) SslContextBuilder.forClient().keyManager(javaPrivateKey, listOf(buildCert(localKey, connectionKeys.first))) else SslContextBuilder.forServer(javaPrivateKey, listOf(buildCert(localKey, connectionKeys.first))) @@ -107,22 +110,34 @@ fun buildTlsHandler( .ciphers(listOf("TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256")) .clientAuth(ClientAuth.REQUIRE) .trustManager(Libp2pTrustManager(expectedRemotePeer)) + .applicationProtocolConfig( + ApplicationProtocolConfig(ApplicationProtocolConfig.Protocol.ALPN, + ApplicationProtocolConfig.SelectorFailureBehavior.FATAL_ALERT, + ApplicationProtocolConfig.SelectedListenerFailureBehavior.FATAL_ALERT, muxerIds.plus("libp2p"))) .build() val handler = sslContext.newHandler(PooledByteBufAllocator.DEFAULT) handler.sslCloseFuture().addListener { _ -> ctx.close() } val handshake = handler.handshakeFuture() val engine = handler.engine() handshake.addListener { fut -> - if (! fut.isSuccess) - handshakeComplete.completeExceptionally(fut.cause().cause) - else + if (! fut.isSuccess) { + var cause = fut.cause() + if (cause != null && cause.cause != null) + cause = cause.cause + handshakeComplete.completeExceptionally(cause) + } else { + val negotiatedProtocols = sslContext.applicationProtocolNegotiator().protocols() + println(negotiatedProtocols) + val selectedProtocol = negotiatedProtocols.filter { name -> muxerIds.contains(name) }.get(0) handshakeComplete.complete( SecureChannel.Session( PeerId.fromPubKey(localKey.publicKey()), - verifyAndExtractPeerId(engine.getSession().getPeerCertificates()), - getPublicKeyFromCert(engine.getSession().getPeerCertificates()) + verifyAndExtractPeerId(engine.session.peerCertificates), + getPublicKeyFromCert(engine.session.peerCertificates), + selectedProtocol ) ) + } } println("libp2p-tls using suites: " + sslContext.cipherSuites()) return handler @@ -130,7 +145,8 @@ fun buildTlsHandler( private class ChannelSetup( private val localKey: PrivKey, - private val isInitiator: Boolean, + private val muxerIds: List, + private val ch: P2PChannel, private val handshakeComplete: CompletableFuture ) : SimpleChannelInboundHandler() { private var activated = false @@ -140,7 +156,7 @@ private class ChannelSetup( activated = true val expectedRemotePeerId = ctx.channel().attr(REMOTE_PEER_ID).get() ctx.channel().pipeline().remove(SetupHandlerName) - ctx.channel().pipeline().addLast(buildTlsHandler(localKey, Optional.ofNullable(expectedRemotePeerId), isInitiator, handshakeComplete, ctx)) + ctx.channel().pipeline().addLast(buildTlsHandler(localKey, Optional.ofNullable(expectedRemotePeerId), muxerIds, ch, handshakeComplete, ctx)) } } diff --git a/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt b/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt index 9aa6776d0..82ba9fed7 100644 --- a/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt +++ b/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt @@ -1,10 +1,12 @@ package io.libp2p.transport import io.libp2p.core.Connection +import io.libp2p.core.NoSuchLocalProtocolException import io.libp2p.core.multistream.MultistreamProtocol import io.libp2p.core.multistream.ProtocolBinding import io.libp2p.core.mux.StreamMuxer import io.libp2p.core.security.SecureChannel +import io.libp2p.etc.types.forward import java.util.concurrent.CompletableFuture /** @@ -31,7 +33,18 @@ open class ConnectionUpgrader( connection, muxers ) - } // establishMuxer + } + + open fun establishMuxer(muxerId: String, connection: Connection): CompletableFuture { + if (muxerId.isEmpty() || muxerId.equals("libp2p")) { + return establishMuxer(connection) + } + val muxer = muxers.find { m -> m.protocolDescriptor.announceProtocols.contains(muxerId) } + ?: throw NoSuchLocalProtocolException("Early Muxer negotiation selected unsupported muxer: ${muxerId}") + val res = CompletableFuture() + muxer.initChannel(connection, muxerId).forward(res) + return res + } private fun , R> establish( multistreamProtocol: MultistreamProtocol, diff --git a/libp2p/src/main/kotlin/io/libp2p/transport/implementation/ConnectionBuilder.kt b/libp2p/src/main/kotlin/io/libp2p/transport/implementation/ConnectionBuilder.kt index b4efd1073..01ec6eedf 100644 --- a/libp2p/src/main/kotlin/io/libp2p/transport/implementation/ConnectionBuilder.kt +++ b/libp2p/src/main/kotlin/io/libp2p/transport/implementation/ConnectionBuilder.kt @@ -32,7 +32,7 @@ class ConnectionBuilder( upgrader.establishSecureChannel(connection) .thenCompose { connection.setSecureSession(it) - upgrader.establishMuxer(connection) + upgrader.establishMuxer(it.nextProto, connection) }.thenApply { connection.setMuxerSession(it) connHandler.handleConnection(connection) diff --git a/libp2p/src/test/kotlin/io/libp2p/security/CipherSecureChannelTest.kt b/libp2p/src/test/kotlin/io/libp2p/security/CipherSecureChannelTest.kt index 7d0cd9bcc..6362bc717 100644 --- a/libp2p/src/test/kotlin/io/libp2p/security/CipherSecureChannelTest.kt +++ b/libp2p/src/test/kotlin/io/libp2p/security/CipherSecureChannelTest.kt @@ -12,8 +12,8 @@ import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.jupiter.api.Test import java.util.concurrent.TimeUnit.SECONDS -abstract class CipherSecureChannelTest(secureChannelCtor: SecureChannelCtor, announce: String) : - SecureChannelTestBase(secureChannelCtor, announce) { +abstract class CipherSecureChannelTest(secureChannelCtor: SecureChannelCtor, muxerIds: List, announce: String) : + SecureChannelTestBase(secureChannelCtor, muxerIds, announce) { @Test fun `incorrect initiator remote PeerId should throw`() { @@ -21,8 +21,8 @@ abstract class CipherSecureChannelTest(secureChannelCtor: SecureChannelCtor, ann val (privKey2, _) = generateKeyPair(KEY_TYPE.ECDSA) val (_, wrongPubKey) = generateKeyPair(KEY_TYPE.ECDSA) - val protocolSelect1 = makeSelector(privKey1) - val protocolSelect2 = makeSelector(privKey2) + val protocolSelect1 = makeSelector(privKey1, muxerIds) + val protocolSelect2 = makeSelector(privKey2, muxerIds) val eCh1 = makeDialChannel("#1", protocolSelect1, PeerId.fromPubKey(wrongPubKey)) val eCh2 = makeListenChannel("#2", protocolSelect2) @@ -39,8 +39,8 @@ abstract class CipherSecureChannelTest(secureChannelCtor: SecureChannelCtor, ann val (privKey1, _) = generateKeyPair(KEY_TYPE.ECDSA) val (privKey2, pubKey2) = generateKeyPair(KEY_TYPE.ECDSA) - val protocolSelect1 = makeSelector(privKey1) - val protocolSelect2 = makeSelector(privKey2) + val protocolSelect1 = makeSelector(privKey1, muxerIds) + val protocolSelect2 = makeSelector(privKey2, muxerIds) val eCh1 = makeDialChannel("#1", protocolSelect1, PeerId.fromPubKey(pubKey2)) val eCh2 = makeListenChannel("#2", protocolSelect2) diff --git a/libp2p/src/test/kotlin/io/libp2p/security/SecureChannelTestBase.kt b/libp2p/src/test/kotlin/io/libp2p/security/SecureChannelTestBase.kt index cf69d61fb..4dd736876 100644 --- a/libp2p/src/test/kotlin/io/libp2p/security/SecureChannelTestBase.kt +++ b/libp2p/src/test/kotlin/io/libp2p/security/SecureChannelTestBase.kt @@ -26,12 +26,13 @@ import java.nio.charset.StandardCharsets import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.TimeUnit -typealias SecureChannelCtor = (PrivKey) -> SecureChannel +typealias SecureChannelCtor = (PrivKey, List) -> SecureChannel val logger = LoggerFactory.getLogger(SecureChannelTestBase::class.java) abstract class SecureChannelTestBase( val secureChannelCtor: SecureChannelCtor, + val muxerIds: List, val announce: String ) { init { @@ -56,8 +57,8 @@ abstract class SecureChannelTestBase( val (privKey1, _) = generateKeyPair(KEY_TYPE.ECDSA) val (privKey2, pubKey2) = generateKeyPair(KEY_TYPE.ECDSA) - val protocolSelect1 = makeSelector(privKey1) - val protocolSelect2 = makeSelector(privKey2) + val protocolSelect1 = makeSelector(privKey1, muxerIds) + val protocolSelect2 = makeSelector(privKey2, muxerIds) val eCh1 = makeDialChannel("#1", protocolSelect1, PeerId.fromPubKey(pubKey2)) val eCh2 = makeListenChannel("#2", protocolSelect2) @@ -117,7 +118,7 @@ abstract class SecureChannelTestBase( } } // secureInterconnect - protected fun makeSelector(key: PrivKey) = ProtocolSelect(listOf(secureChannelCtor(key))) + protected fun makeSelector(key: PrivKey, muxers: List) = ProtocolSelect(listOf(secureChannelCtor(key, muxers))) protected fun makeDialChannel( name: String, diff --git a/libp2p/src/test/kotlin/io/libp2p/security/noise/NoiseHandshakeTest.kt b/libp2p/src/test/kotlin/io/libp2p/security/noise/NoiseHandshakeTest.kt index a2dfb0282..a6f6454b2 100644 --- a/libp2p/src/test/kotlin/io/libp2p/security/noise/NoiseHandshakeTest.kt +++ b/libp2p/src/test/kotlin/io/libp2p/security/noise/NoiseHandshakeTest.kt @@ -146,7 +146,7 @@ class NoiseHandshakeTest { fun testAnnounceAndMatch() { val (privKey1, _) = generateKeyPair(KEY_TYPE.ECDSA) - val ch1 = NoiseXXSecureChannel(privKey1) + val ch1 = NoiseXXSecureChannel(privKey1, listOf()) Assertions.assertTrue( ch1.protocolDescriptor.matchesAny(ch1.protocolDescriptor.announceProtocols) @@ -156,11 +156,11 @@ class NoiseHandshakeTest { @Test fun testStaticNoiseKeyPerProcess() { val (privKey1, _) = generateKeyPair(KEY_TYPE.ECDSA) - NoiseXXSecureChannel(privKey1) + NoiseXXSecureChannel(privKey1, listOf()) val b1 = NoiseXXSecureChannel.localStaticPrivateKey25519.copyOf() val (privKey2, _) = generateKeyPair(KEY_TYPE.ECDSA) - NoiseXXSecureChannel(privKey2) + NoiseXXSecureChannel(privKey2, listOf()) val b2 = NoiseXXSecureChannel.localStaticPrivateKey25519.copyOf() Assertions.assertTrue(b1.contentEquals(b2), "NoiseXX static keys are not maintained between sessions.") diff --git a/libp2p/src/test/kotlin/io/libp2p/security/noise/NoiseSecureChannelTest.kt b/libp2p/src/test/kotlin/io/libp2p/security/noise/NoiseSecureChannelTest.kt index 031144ba5..b08ad60a2 100644 --- a/libp2p/src/test/kotlin/io/libp2p/security/noise/NoiseSecureChannelTest.kt +++ b/libp2p/src/test/kotlin/io/libp2p/security/noise/NoiseSecureChannelTest.kt @@ -8,5 +8,6 @@ import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable @Tag("secure-channel") class NoiseSecureChannelTest : CipherSecureChannelTest( ::NoiseXXSecureChannel, + listOf(), NoiseXXSecureChannel.announce ) diff --git a/libp2p/src/test/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannelTest.kt b/libp2p/src/test/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannelTest.kt index d16e41c82..a3b751ab8 100644 --- a/libp2p/src/test/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannelTest.kt +++ b/libp2p/src/test/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannelTest.kt @@ -6,5 +6,6 @@ import org.junit.jupiter.api.Tag @Tag("secure-channel") class PlaintextInsecureChannelTest : SecureChannelTestBase( ::PlaintextInsecureChannel, + listOf(), "/plaintext/2.0.0" ) diff --git a/libp2p/src/test/kotlin/io/libp2p/security/secio/EchoSampleTest.kt b/libp2p/src/test/kotlin/io/libp2p/security/secio/EchoSampleTest.kt index 595926ad4..f1c8b097c 100644 --- a/libp2p/src/test/kotlin/io/libp2p/security/secio/EchoSampleTest.kt +++ b/libp2p/src/test/kotlin/io/libp2p/security/secio/EchoSampleTest.kt @@ -63,7 +63,7 @@ class EchoSampleTest { } val upgrader = ConnectionUpgrader( MultistreamProtocolV1.copyWithHandlers(nettyToChannelHandler(LoggingHandler("#1", LogLevel.INFO))), - listOf(SecIoSecureChannel(privKey1)), + listOf(SecIoSecureChannel(privKey1, listOf())), MultistreamProtocolV1.copyWithHandlers(nettyToChannelHandler(LoggingHandler("#2", LogLevel.INFO))), listOf(muxer) ) diff --git a/libp2p/src/test/kotlin/io/libp2p/security/secio/SecIoSecureChannelTest.kt b/libp2p/src/test/kotlin/io/libp2p/security/secio/SecIoSecureChannelTest.kt index 96eefa6bf..4df5314f3 100644 --- a/libp2p/src/test/kotlin/io/libp2p/security/secio/SecIoSecureChannelTest.kt +++ b/libp2p/src/test/kotlin/io/libp2p/security/secio/SecIoSecureChannelTest.kt @@ -6,5 +6,6 @@ import org.junit.jupiter.api.Tag @Tag("secure-channel") class SecIoSecureChannelTest : CipherSecureChannelTest( ::SecIoSecureChannel, + listOf(), "/secio/1.0.0" ) diff --git a/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt b/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt index fd4a1e7fa..33beada06 100644 --- a/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt +++ b/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt @@ -3,6 +3,9 @@ package io.libp2p.security.tls import io.libp2p.core.PeerId import io.libp2p.core.crypto.KEY_TYPE import io.libp2p.core.crypto.generateKeyPair +import io.libp2p.core.multistream.MultistreamProtocolDebug +import io.libp2p.core.mux.StreamMuxerProtocol +import io.libp2p.multistream.MultistreamProtocolDebugV1 import io.libp2p.security.InvalidRemotePubKey import io.libp2p.security.SecureChannelTestBase import io.libp2p.security.logger @@ -13,9 +16,12 @@ import org.junit.jupiter.api.Test import java.util.concurrent.TimeUnit import java.util.logging.Level +val MultistreamProtocolV1: MultistreamProtocolDebug = MultistreamProtocolDebugV1() + @Tag("secure-channel") class TlsSecureChannelTest : SecureChannelTestBase( ::TlsSecureChannel, + listOf(StreamMuxerProtocol.Yamux.createMuxer(MultistreamProtocolV1, listOf()).protocolDescriptor.announceProtocols.get(0)), TlsSecureChannel.announce ) { @Test @@ -24,8 +30,8 @@ class TlsSecureChannelTest : SecureChannelTestBase( val (privKey2, _) = generateKeyPair(KEY_TYPE.ECDSA) val (_, wrongPubKey) = generateKeyPair(KEY_TYPE.ECDSA) - val protocolSelect1 = makeSelector(privKey1) - val protocolSelect2 = makeSelector(privKey2) + val protocolSelect1 = makeSelector(privKey1, muxerIds) + val protocolSelect2 = makeSelector(privKey2, muxerIds) val eCh1 = makeDialChannel("#1", protocolSelect1, PeerId.fromPubKey(wrongPubKey)) val eCh2 = makeListenChannel("#2", protocolSelect2) diff --git a/libp2p/src/testFixtures/kotlin/io/libp2p/pubsub/TestRouter.kt b/libp2p/src/testFixtures/kotlin/io/libp2p/pubsub/TestRouter.kt index cf8fd7e7e..f62b2a475 100644 --- a/libp2p/src/testFixtures/kotlin/io/libp2p/pubsub/TestRouter.kt +++ b/libp2p/src/testFixtures/kotlin/io/libp2p/pubsub/TestRouter.kt @@ -68,7 +68,7 @@ class TestRouter( ConnectionOverNetty(parentChannel, NullTransport(), initiator) connection.setSecureSession( SecureChannel.Session( - peerId, remoteRouter.peerId, remoteRouter.keyPair.second + peerId, remoteRouter.peerId, remoteRouter.keyPair.second, "" ) ) diff --git a/libp2p/src/testFixtures/kotlin/io/libp2p/transport/NullConnectionUpgrader.kt b/libp2p/src/testFixtures/kotlin/io/libp2p/transport/NullConnectionUpgrader.kt index 64e6c173f..2d24866d6 100644 --- a/libp2p/src/testFixtures/kotlin/io/libp2p/transport/NullConnectionUpgrader.kt +++ b/libp2p/src/testFixtures/kotlin/io/libp2p/transport/NullConnectionUpgrader.kt @@ -25,7 +25,8 @@ class NullConnectionUpgrader : val nonsenseSession = SecureChannel.Session( PeerId.random(), PeerId.random(), - generateKeyPair(KEY_TYPE.RSA).second + generateKeyPair(KEY_TYPE.RSA).second, + "" ) return CompletableFuture.completedFuture(nonsenseSession) } // establishSecureChannel From c27715ff312e06c3371723a34b515429eec5759d Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 24 Feb 2023 11:26:33 +0000 Subject: [PATCH 13/37] Linting --- libp2p/src/main/kotlin/io/libp2p/core/dsl/Builders.kt | 1 - .../kotlin/io/libp2p/security/tls/TLSSecureChannel.kt | 9 ++++++--- .../kotlin/io/libp2p/transport/ConnectionUpgrader.kt | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/core/dsl/Builders.kt b/libp2p/src/main/kotlin/io/libp2p/core/dsl/Builders.kt index d0482e122..9a9df2905 100644 --- a/libp2p/src/main/kotlin/io/libp2p/core/dsl/Builders.kt +++ b/libp2p/src/main/kotlin/io/libp2p/core/dsl/Builders.kt @@ -28,7 +28,6 @@ import io.libp2p.host.MemoryAddressBook import io.libp2p.network.NetworkImpl import io.libp2p.protocol.IdentifyBinding import io.libp2p.security.noise.NoiseXXSecureChannel -import io.libp2p.security.secio.SecIoSecureChannel import io.libp2p.transport.ConnectionUpgrader import io.libp2p.transport.tcp.TcpTransport import io.netty.channel.ChannelHandler diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt index 9152a484d..61a3accbf 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -111,9 +111,12 @@ fun buildTlsHandler( .clientAuth(ClientAuth.REQUIRE) .trustManager(Libp2pTrustManager(expectedRemotePeer)) .applicationProtocolConfig( - ApplicationProtocolConfig(ApplicationProtocolConfig.Protocol.ALPN, - ApplicationProtocolConfig.SelectorFailureBehavior.FATAL_ALERT, - ApplicationProtocolConfig.SelectedListenerFailureBehavior.FATAL_ALERT, muxerIds.plus("libp2p"))) + ApplicationProtocolConfig( + ApplicationProtocolConfig.Protocol.ALPN, + ApplicationProtocolConfig.SelectorFailureBehavior.FATAL_ALERT, + ApplicationProtocolConfig.SelectedListenerFailureBehavior.FATAL_ALERT, muxerIds.plus("libp2p") + ) + ) .build() val handler = sslContext.newHandler(PooledByteBufAllocator.DEFAULT) handler.sslCloseFuture().addListener { _ -> ctx.close() } diff --git a/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt b/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt index 82ba9fed7..b5fd86837 100644 --- a/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt +++ b/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt @@ -40,7 +40,7 @@ open class ConnectionUpgrader( return establishMuxer(connection) } val muxer = muxers.find { m -> m.protocolDescriptor.announceProtocols.contains(muxerId) } - ?: throw NoSuchLocalProtocolException("Early Muxer negotiation selected unsupported muxer: ${muxerId}") + ?: throw NoSuchLocalProtocolException("Early Muxer negotiation selected unsupported muxer: $muxerId") val res = CompletableFuture() muxer.initChannel(connection, muxerId).forward(res) return res From d56d8806267e43b5560a3110723d96e19f4c6776 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 24 Feb 2023 12:24:05 +0000 Subject: [PATCH 14/37] Support ECDSA certificate keys in TLS as well as Ed25519 But default to Ed25519 --- .../kotlin/io/libp2p/crypto/keys/Ecdsa.kt | 4 +++ .../libp2p/security/tls/TLSSecureChannel.kt | 35 ++++++++++++++++--- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/crypto/keys/Ecdsa.kt b/libp2p/src/main/kotlin/io/libp2p/crypto/keys/Ecdsa.kt index b8f3e124e..3ff0de363 100644 --- a/libp2p/src/main/kotlin/io/libp2p/crypto/keys/Ecdsa.kt +++ b/libp2p/src/main/kotlin/io/libp2p/crypto/keys/Ecdsa.kt @@ -91,6 +91,10 @@ class EcdsaPublicKey(val pub: JavaECPublicKey) : PubKey(Crypto.KeyType.ECDSA) { override fun raw(): ByteArray = pub.encoded + fun javaKey(): JavaECPublicKey { + return pub + } + fun toUncompressedBytes(): ByteArray = byteArrayOf(0x04) + pub.w.affineX.toBytes(32) + pub.w.affineY.toBytes(32) diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt index 61a3accbf..187e83814 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -7,7 +7,10 @@ import io.libp2p.core.crypto.PubKey import io.libp2p.core.crypto.unmarshalPublicKey import io.libp2p.core.multistream.ProtocolDescriptor import io.libp2p.core.security.SecureChannel +import io.libp2p.crypto.Libp2pCrypto +import io.libp2p.crypto.keys.EcdsaPublicKey import io.libp2p.crypto.keys.Ed25519PublicKey +import io.libp2p.crypto.keys.generateEcdsaKeyPair import io.libp2p.crypto.keys.generateEd25519KeyPair import io.libp2p.etc.REMOTE_PEER_ID import io.libp2p.security.InvalidRemotePubKey @@ -42,6 +45,7 @@ import java.security.PublicKey import java.security.cert.Certificate import java.security.cert.CertificateException import java.security.cert.X509Certificate +import java.security.interfaces.ECPublicKey import java.security.interfaces.EdECPublicKey import java.security.spec.* import java.time.Instant @@ -63,9 +67,11 @@ class UShortLengthCodec : CombinedChannelDuplexHandler) : +class TlsSecureChannel(private val localKey: PrivKey, private val muxerIds: List, private val certAlgorithm: String) : SecureChannel { + constructor(localKey: PrivKey, muxerIds: List) : this(localKey, muxerIds, "Ed25519") {} + companion object { const val announce = "/tls/1.0.0" } @@ -84,7 +90,7 @@ class TlsSecureChannel(private val localKey: PrivKey, private val muxerIds: List ch.pushHandler(UShortLengthCodec()) // Packet length codec should stay forever. - ch.pushHandler(SetupHandlerName, ChannelSetup(localKey, muxerIds, ch, handshakeComplete)) + ch.pushHandler(SetupHandlerName, ChannelSetup(localKey, muxerIds, certAlgorithm, ch, handshakeComplete)) return handshakeComplete } } @@ -93,11 +99,12 @@ fun buildTlsHandler( localKey: PrivKey, expectedRemotePeer: Optional, muxerIds: List, + certAlgorithm: String, ch: P2PChannel, handshakeComplete: CompletableFuture, ctx: ChannelHandlerContext ): SslHandler { - val connectionKeys = generateEd25519KeyPair() + val connectionKeys = if (certAlgorithm.equals("ECDSA")) generateEcdsaKeyPair() else generateEd25519KeyPair() val javaPrivateKey = getJavaKey(connectionKeys.first) println("TLS supporting muxers: " + muxerIds) val sslContext = ( @@ -149,6 +156,7 @@ fun buildTlsHandler( private class ChannelSetup( private val localKey: PrivKey, private val muxerIds: List, + private val certAlgorithm: String, private val ch: P2PChannel, private val handshakeComplete: CompletableFuture ) : SimpleChannelInboundHandler() { @@ -159,7 +167,7 @@ private class ChannelSetup( activated = true val expectedRemotePeerId = ctx.channel().attr(REMOTE_PEER_ID).get() ctx.channel().pipeline().remove(SetupHandlerName) - ctx.channel().pipeline().addLast(buildTlsHandler(localKey, Optional.ofNullable(expectedRemotePeerId), muxerIds, ch, handshakeComplete, ctx)) + ctx.channel().pipeline().addLast(buildTlsHandler(localKey, Optional.ofNullable(expectedRemotePeerId), muxerIds, certAlgorithm, ch, handshakeComplete, ctx)) } } @@ -212,6 +220,12 @@ fun getJavaKey(priv: PrivKey): PrivateKey { val pkcs8KeySpec = PKCS8EncodedKeySpec(privKeyInfo.encoded) return kf.generatePrivate(pkcs8KeySpec) } + if (priv.keyType == Crypto.KeyType.ECDSA) { + val kf = KeyFactory.getInstance("ECDSA", Libp2pCrypto.provider) + val pkcs8KeySpec = PKCS8EncodedKeySpec(priv.raw()) + return kf.generatePrivate(pkcs8KeySpec) + } + if (priv.keyType == Crypto.KeyType.RSA) { throw IllegalStateException("Unimplemented RSA key support for TLS") } @@ -235,6 +249,9 @@ fun getJavaPublicKey(pub: PubKey): PublicKey { val pubSpec = EdECPublicKeySpec(paramSpec, ep) return kf.generatePublic(pubSpec) } + if (pub.keyType == Crypto.KeyType.ECDSA) { + return (pub as EcdsaPublicKey).javaKey() + } throw IllegalArgumentException("Unsupported TLS key type:" + pub.keyType) } @@ -249,6 +266,9 @@ fun getPubKey(pub: PublicKey): PubKey { pk[31] = pk[31].or(0x80.toByte()) return Ed25519PublicKey(Ed25519PublicKeyParameters(pk)) } + if (pub.algorithm.equals("EC")) { + return EcdsaPublicKey(pub as ECPublicKey) + } if (pub.algorithm.equals("RSA")) throw IllegalStateException("Unimplemented RSA public key support for TLS") throw IllegalStateException("Unsupported key type: " + pub.algorithm) @@ -317,7 +337,12 @@ fun buildCert(hostKey: PrivKey, subjectKey: PrivKey): X509Certificate { subject, subPubKeyInfo ).addExtension(ASN1ObjectIdentifier("1.3.6.1.4.1.53594.1.1"), false, extension) - val signer = JcaContentSignerBuilder("Ed25519") + val sigAlg = when (subjectKey.keyType) { + Crypto.KeyType.Ed25519 -> "Ed25519" + Crypto.KeyType.ECDSA -> "SHA256withECDSA" + else -> throw IllegalStateException("Unsupported certificate key type: " + subjectKey.keyType) + } + val signer = JcaContentSignerBuilder(sigAlg) .setProvider(BouncyCastleProvider()) .build(getJavaKey(subjectKey)) return JcaX509CertificateConverter().getCertificate(certBuilder.build(signer)) From 933dba33b512811adebed5b94efa4e062c2991f6 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 24 Feb 2023 22:15:37 +0000 Subject: [PATCH 15/37] Remove UShortLengthCodec in tls. Mark certificate extension as critical --- .../libp2p/security/tls/TLSSecureChannel.kt | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt index 187e83814..68cf43e41 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -17,10 +17,7 @@ import io.libp2p.security.InvalidRemotePubKey import io.netty.buffer.ByteBuf import io.netty.buffer.PooledByteBufAllocator import io.netty.channel.ChannelHandlerContext -import io.netty.channel.CombinedChannelDuplexHandler import io.netty.channel.SimpleChannelInboundHandler -import io.netty.handler.codec.LengthFieldBasedFrameDecoder -import io.netty.handler.codec.LengthFieldPrepender import io.netty.handler.ssl.ApplicationProtocolConfig import io.netty.handler.ssl.ClientAuth import io.netty.handler.ssl.SslContextBuilder @@ -36,7 +33,6 @@ import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.cert.X509v3CertificateBuilder import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters -import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder import java.math.BigInteger import java.security.KeyFactory @@ -59,14 +55,8 @@ import kotlin.experimental.or private val log = Logger.getLogger(TlsSecureChannel::class.java.name) private val SetupHandlerName = "TlsSetup" -const val MaxCipheredPacketLength = 65535 val certificatePrefix = "libp2p-tls-handshake:".encodeToByteArray() -class UShortLengthCodec : CombinedChannelDuplexHandler( - LengthFieldBasedFrameDecoder(MaxCipheredPacketLength + 2, 0, 2, 0, 2), - LengthFieldPrepender(2) -) - class TlsSecureChannel(private val localKey: PrivKey, private val muxerIds: List, private val certAlgorithm: String) : SecureChannel { @@ -87,9 +77,6 @@ class TlsSecureChannel(private val localKey: PrivKey, private val muxerIds: List selectedProtocol: String ): CompletableFuture { val handshakeComplete = CompletableFuture() - - ch.pushHandler(UShortLengthCodec()) // Packet length codec should stay forever. - ch.pushHandler(SetupHandlerName, ChannelSetup(localKey, muxerIds, certAlgorithm, ch, handshakeComplete)) return handshakeComplete } @@ -105,6 +92,7 @@ fun buildTlsHandler( ctx: ChannelHandlerContext ): SslHandler { val connectionKeys = if (certAlgorithm.equals("ECDSA")) generateEcdsaKeyPair() else generateEd25519KeyPair() + println(certAlgorithm) val javaPrivateKey = getJavaKey(connectionKeys.first) println("TLS supporting muxers: " + muxerIds) val sslContext = ( @@ -322,7 +310,7 @@ fun buildCert(hostKey: PrivKey, subjectKey: PrivKey): X509Certificate { val validFrom = Date.from(now.minusSeconds(3600)) val oneYear = 60L * 60 * 24 * 365 val validTo = Date.from(now.plusSeconds(oneYear)) - val issuer = X500Name("CN=Nabu,O=Peergos,L=Oxford,C=UK") + val issuer = X500Name("O=Peergos,L=Oxford,C=UK") val subject = issuer val signature = hostKey.sign(certificatePrefix.plus(publicKeyAsn1)) @@ -336,14 +324,14 @@ fun buildCert(hostKey: PrivKey, subjectKey: PrivKey): X509Certificate { validTo, subject, subPubKeyInfo - ).addExtension(ASN1ObjectIdentifier("1.3.6.1.4.1.53594.1.1"), false, extension) + ).addExtension(ASN1ObjectIdentifier("1.3.6.1.4.1.53594.1.1"), true, extension) val sigAlg = when (subjectKey.keyType) { Crypto.KeyType.Ed25519 -> "Ed25519" Crypto.KeyType.ECDSA -> "SHA256withECDSA" else -> throw IllegalStateException("Unsupported certificate key type: " + subjectKey.keyType) } val signer = JcaContentSignerBuilder(sigAlg) - .setProvider(BouncyCastleProvider()) + .setProvider(Libp2pCrypto.provider) .build(getJavaKey(subjectKey)) return JcaX509CertificateConverter().getCertificate(certBuilder.build(signer)) } From 38b4b4658901c0ffe9958f3bed418aefd37f1a76 Mon Sep 17 00:00:00 2001 From: ian Date: Sat, 25 Feb 2023 00:20:27 +0000 Subject: [PATCH 16/37] Hooray first successful TLS handshake with Kubo! Don't remove the SetupHandler before adding the TLS handler. --- .../src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt index 68cf43e41..2c44dbb82 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -154,8 +154,8 @@ private class ChannelSetup( if (! activated) { activated = true val expectedRemotePeerId = ctx.channel().attr(REMOTE_PEER_ID).get() - ctx.channel().pipeline().remove(SetupHandlerName) ctx.channel().pipeline().addLast(buildTlsHandler(localKey, Optional.ofNullable(expectedRemotePeerId), muxerIds, certAlgorithm, ch, handshakeComplete, ctx)) + ctx.channel().pipeline().remove(SetupHandlerName) } } From 66353e55b4758a39c9b7a7dd59401640c0fa6891 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 27 Feb 2023 12:41:34 +0000 Subject: [PATCH 17/37] Push muxer channel initializer rather than directly initialise channel in early muxer negotiation --- .../main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt b/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt index b5fd86837..9835e121e 100644 --- a/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt +++ b/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt @@ -6,7 +6,9 @@ import io.libp2p.core.multistream.MultistreamProtocol import io.libp2p.core.multistream.ProtocolBinding import io.libp2p.core.mux.StreamMuxer import io.libp2p.core.security.SecureChannel +import io.libp2p.etc.getP2PChannel import io.libp2p.etc.types.forward +import io.libp2p.etc.util.netty.nettyInitializer import java.util.concurrent.CompletableFuture /** @@ -42,7 +44,11 @@ open class ConnectionUpgrader( val muxer = muxers.find { m -> m.protocolDescriptor.announceProtocols.contains(muxerId) } ?: throw NoSuchLocalProtocolException("Early Muxer negotiation selected unsupported muxer: $muxerId") val res = CompletableFuture() - muxer.initChannel(connection, muxerId).forward(res) + connection.pushHandler( + nettyInitializer { + muxer.initChannel(it.channel.getP2PChannel(), "").forward(res) + } + ) return res } From f5fd8f2ed372132c244f7d784412bfe19c3423a4 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 27 Feb 2023 12:44:04 +0000 Subject: [PATCH 18/37] Use context allocator in TLS Handle no early negotiated muxer protocol in TLS --- .../main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt index 2c44dbb82..1cce15c83 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -15,7 +15,6 @@ import io.libp2p.crypto.keys.generateEd25519KeyPair import io.libp2p.etc.REMOTE_PEER_ID import io.libp2p.security.InvalidRemotePubKey import io.netty.buffer.ByteBuf -import io.netty.buffer.PooledByteBufAllocator import io.netty.channel.ChannelHandlerContext import io.netty.channel.SimpleChannelInboundHandler import io.netty.handler.ssl.ApplicationProtocolConfig @@ -92,7 +91,7 @@ fun buildTlsHandler( ctx: ChannelHandlerContext ): SslHandler { val connectionKeys = if (certAlgorithm.equals("ECDSA")) generateEcdsaKeyPair() else generateEd25519KeyPair() - println(certAlgorithm) + println("TLS using key type: " + certAlgorithm) val javaPrivateKey = getJavaKey(connectionKeys.first) println("TLS supporting muxers: " + muxerIds) val sslContext = ( @@ -113,7 +112,7 @@ fun buildTlsHandler( ) ) .build() - val handler = sslContext.newHandler(PooledByteBufAllocator.DEFAULT) + val handler = sslContext.newHandler(ctx.alloc()) handler.sslCloseFuture().addListener { _ -> ctx.close() } val handshake = handler.handshakeFuture() val engine = handler.engine() @@ -126,7 +125,7 @@ fun buildTlsHandler( } else { val negotiatedProtocols = sslContext.applicationProtocolNegotiator().protocols() println(negotiatedProtocols) - val selectedProtocol = negotiatedProtocols.filter { name -> muxerIds.contains(name) }.get(0) + val selectedProtocol = negotiatedProtocols.filter { name -> muxerIds.contains(name) }.getOrElse(0, defaultValue = { _ -> "" }) handshakeComplete.complete( SecureChannel.Session( PeerId.fromPubKey(localKey.publicKey()), From fde75d09e59baddc1cf56b6f3437c17936c40c52 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 3 Mar 2023 09:08:31 +0000 Subject: [PATCH 19/37] Ping over TLS working to kubo!! --- .../io/libp2p/security/tls/TLSSecureChannel.kt | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt index 1cce15c83..376958969 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -21,7 +21,6 @@ import io.netty.handler.ssl.ApplicationProtocolConfig import io.netty.handler.ssl.ClientAuth import io.netty.handler.ssl.SslContextBuilder import io.netty.handler.ssl.SslHandler -import io.netty.util.ReferenceCountUtil import org.bouncycastle.asn1.* import org.bouncycastle.asn1.edec.EdECObjectIdentifiers import org.bouncycastle.asn1.pkcs.PrivateKeyInfo @@ -91,9 +90,7 @@ fun buildTlsHandler( ctx: ChannelHandlerContext ): SslHandler { val connectionKeys = if (certAlgorithm.equals("ECDSA")) generateEcdsaKeyPair() else generateEd25519KeyPair() - println("TLS using key type: " + certAlgorithm) val javaPrivateKey = getJavaKey(connectionKeys.first) - println("TLS supporting muxers: " + muxerIds) val sslContext = ( if (ch.isInitiator) SslContextBuilder.forClient().keyManager(javaPrivateKey, listOf(buildCert(localKey, connectionKeys.first))) @@ -108,7 +105,9 @@ fun buildTlsHandler( ApplicationProtocolConfig( ApplicationProtocolConfig.Protocol.ALPN, ApplicationProtocolConfig.SelectorFailureBehavior.FATAL_ALERT, - ApplicationProtocolConfig.SelectedListenerFailureBehavior.FATAL_ALERT, muxerIds.plus("libp2p") + ApplicationProtocolConfig.SelectedListenerFailureBehavior.FATAL_ALERT, + listOf("libp2p") +// muxerIds.plus("libp2p") ) ) .build() @@ -124,7 +123,6 @@ fun buildTlsHandler( handshakeComplete.completeExceptionally(cause) } else { val negotiatedProtocols = sslContext.applicationProtocolNegotiator().protocols() - println(negotiatedProtocols) val selectedProtocol = negotiatedProtocols.filter { name -> muxerIds.contains(name) }.getOrElse(0, defaultValue = { _ -> "" }) handshakeComplete.complete( SecureChannel.Session( @@ -134,9 +132,9 @@ fun buildTlsHandler( selectedProtocol ) ) + ctx.fireChannelActive() } } - println("libp2p-tls using suites: " + sslContext.cipherSuites()) return handler } @@ -161,12 +159,8 @@ private class ChannelSetup( override fun channelRead0(ctx: ChannelHandlerContext, msg: ByteBuf) { // it seems there is no guarantee from Netty that channelActive() must be called before channelRead() channelActive(ctx) + ctx.fireChannelRead(msg) ctx.fireChannelActive() - ReferenceCountUtil.retain(msg) - } - - private fun writeAndFlush(ctx: ChannelHandlerContext, bb: ByteBuf) { - ctx.writeAndFlush(bb) } override fun exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable) { From 8d54096bd0ce2d9c2ee275cd8148a8e652e80a38 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 3 Mar 2023 09:10:44 +0000 Subject: [PATCH 20/37] Reenable early muxer negotiation in TLS (It works!) --- .../src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt | 3 +-- .../src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt index 376958969..373abc108 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -106,8 +106,7 @@ fun buildTlsHandler( ApplicationProtocolConfig.Protocol.ALPN, ApplicationProtocolConfig.SelectorFailureBehavior.FATAL_ALERT, ApplicationProtocolConfig.SelectedListenerFailureBehavior.FATAL_ALERT, - listOf("libp2p") -// muxerIds.plus("libp2p") + muxerIds.plus("libp2p") ) ) .build() diff --git a/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt b/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt index 9835e121e..487f3cbeb 100644 --- a/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt +++ b/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt @@ -46,7 +46,7 @@ open class ConnectionUpgrader( val res = CompletableFuture() connection.pushHandler( nettyInitializer { - muxer.initChannel(it.channel.getP2PChannel(), "").forward(res) + muxer.initChannel(it.channel.getP2PChannel(), muxerId).forward(res) } ) return res From 647afe3060fb066fb932b59f3093b83a79060581 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 3 Mar 2023 09:14:13 +0000 Subject: [PATCH 21/37] Change test to use tls and yamux --- libp2p/src/test/java/io/libp2p/core/HostTestJava.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libp2p/src/test/java/io/libp2p/core/HostTestJava.java b/libp2p/src/test/java/io/libp2p/core/HostTestJava.java index b3e729112..c1284aa01 100644 --- a/libp2p/src/test/java/io/libp2p/core/HostTestJava.java +++ b/libp2p/src/test/java/io/libp2p/core/HostTestJava.java @@ -9,7 +9,7 @@ import io.libp2p.core.mux.StreamMuxerProtocol; import io.libp2p.protocol.Ping; import io.libp2p.protocol.PingController; -import io.libp2p.security.secio.SecIoSecureChannel; +import io.libp2p.security.tls.*; import io.libp2p.transport.tcp.TcpTransport; import kotlin.Pair; import org.junit.jupiter.api.Assertions; @@ -37,14 +37,14 @@ void ping() throws Exception { Host clientHost = new HostBuilder() .transport(TcpTransport::new) - .secureChannel(SecIoSecureChannel::new) - .muxer(StreamMuxerProtocol::getMplex) + .secureChannel(TlsSecureChannel::new) + .muxer(StreamMuxerProtocol::getYamux) .build(); Host serverHost = new HostBuilder() .transport(TcpTransport::new) - .secureChannel(SecIoSecureChannel::new) - .muxer(StreamMuxerProtocol::getMplex) + .secureChannel(TlsSecureChannel::new) + .muxer(StreamMuxerProtocol::getYamux) .protocol(new Ping()) .listen(localListenAddress) .build(); From ec080fef01a7e4f4fbd2ded0609877f9b716e187 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 5 Apr 2023 14:14:55 +0100 Subject: [PATCH 22/37] Expose EventLoop on Stream --- .../java/io/libp2p/tools/p2pd/NettyStream.java | 7 ++++++- .../java/io/libp2p/tools/p2pd/libp2pj/Stream.java | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/libp2p/src/testFixtures/java/io/libp2p/tools/p2pd/NettyStream.java b/libp2p/src/testFixtures/java/io/libp2p/tools/p2pd/NettyStream.java index b981ab62e..7212ab72d 100644 --- a/libp2p/src/testFixtures/java/io/libp2p/tools/p2pd/NettyStream.java +++ b/libp2p/src/testFixtures/java/io/libp2p/tools/p2pd/NettyStream.java @@ -3,7 +3,7 @@ import io.libp2p.tools.p2pd.libp2pj.Muxer; import io.libp2p.tools.p2pd.libp2pj.Stream; import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; +import io.netty.channel.*; import java.nio.ByteBuffer; @@ -30,6 +30,11 @@ public NettyStream(Channel channel, boolean initiator) { this(channel, initiator, null, null); } + @Override + public EventLoop eventLoop() { + return channel.eventLoop(); + } + @Override public void write(ByteBuffer data) { channel.write(Unpooled.wrappedBuffer(data)); diff --git a/libp2p/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Stream.java b/libp2p/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Stream.java index 623882c11..ba4845bd3 100644 --- a/libp2p/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Stream.java +++ b/libp2p/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Stream.java @@ -1,5 +1,7 @@ package io.libp2p.tools.p2pd.libp2pj; +import io.netty.channel.*; + import java.nio.ByteBuffer; /** @@ -9,6 +11,8 @@ public interface Stream { boolean isInitiator(); + EventLoop eventLoop(); + void write(ByteBuffer data); void flush(); From ea864894069a86d6e9dd08092234c96b64e668fd Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 5 Apr 2023 14:39:12 +0100 Subject: [PATCH 23/37] Expose EventLoop on Kotlin Stream --- libp2p/src/main/kotlin/io/libp2p/core/Stream.kt | 6 ++++++ .../io/libp2p/transport/implementation/StreamOverNetty.kt | 6 ++++++ libp2p/src/testFixtures/kotlin/io/libp2p/tools/Stubs.kt | 6 ++++++ .../kotlin/io/libp2p/tools/TestStreamChannel.kt | 5 +++++ 4 files changed, 23 insertions(+) diff --git a/libp2p/src/main/kotlin/io/libp2p/core/Stream.kt b/libp2p/src/main/kotlin/io/libp2p/core/Stream.kt index c20a49d68..309759356 100644 --- a/libp2p/src/main/kotlin/io/libp2p/core/Stream.kt +++ b/libp2p/src/main/kotlin/io/libp2p/core/Stream.kt @@ -2,6 +2,7 @@ package io.libp2p.core import io.libp2p.protocol.ProtocolMessageHandler import io.libp2p.protocol.ProtocolMessageHandlerAdapter +import io.netty.channel.EventLoop import java.util.concurrent.CompletableFuture /** @@ -10,6 +11,11 @@ import java.util.concurrent.CompletableFuture interface Stream : P2PChannel { val connection: Connection + /** + * Return the underlying EventLoop + */ + fun eventLoop(): EventLoop + /** * Returns the [PeerId] of the remote peer [Connection] which this * [Stream] created on diff --git a/libp2p/src/main/kotlin/io/libp2p/transport/implementation/StreamOverNetty.kt b/libp2p/src/main/kotlin/io/libp2p/transport/implementation/StreamOverNetty.kt index b0324a7e7..04a39ff22 100644 --- a/libp2p/src/main/kotlin/io/libp2p/transport/implementation/StreamOverNetty.kt +++ b/libp2p/src/main/kotlin/io/libp2p/transport/implementation/StreamOverNetty.kt @@ -6,6 +6,7 @@ import io.libp2p.core.Stream import io.libp2p.etc.PROTOCOL import io.libp2p.etc.types.toVoidCompletableFuture import io.netty.channel.Channel +import io.netty.channel.EventLoop import java.util.concurrent.CompletableFuture class StreamOverNetty( @@ -13,10 +14,15 @@ class StreamOverNetty( override val connection: Connection, initiator: Boolean ) : Stream, P2PChannelOverNetty(ch, initiator) { + + val group: EventLoop init { nettyChannel.attr(PROTOCOL).set(CompletableFuture()) + group = ch.eventLoop() } + override fun eventLoop() = group + /** * Returns the [PeerId] of the remote peer [Connection] which this * [Stream] created on diff --git a/libp2p/src/testFixtures/kotlin/io/libp2p/tools/Stubs.kt b/libp2p/src/testFixtures/kotlin/io/libp2p/tools/Stubs.kt index 8f60a0142..005d36a47 100644 --- a/libp2p/src/testFixtures/kotlin/io/libp2p/tools/Stubs.kt +++ b/libp2p/src/testFixtures/kotlin/io/libp2p/tools/Stubs.kt @@ -6,6 +6,7 @@ import io.libp2p.core.Stream import io.libp2p.core.multiformats.Multiaddr import io.libp2p.etc.util.P2PService import io.netty.channel.ChannelHandler +import io.netty.channel.EventLoop import java.util.concurrent.CompletableFuture class ConnectionStub : Connection { @@ -25,6 +26,11 @@ class ConnectionStub : Connection { class StreamStub : Stream { private val remotePeerId = PeerId.random() override val connection = ConnectionStub() + + override fun eventLoop(): EventLoop { + TODO("Not yet implemented") + } + override fun remotePeerId() = remotePeerId override fun getProtocol() = CompletableFuture.completedFuture("nop") override fun pushHandler(handler: ChannelHandler) = TODO() diff --git a/libp2p/src/testFixtures/kotlin/io/libp2p/tools/TestStreamChannel.kt b/libp2p/src/testFixtures/kotlin/io/libp2p/tools/TestStreamChannel.kt index a920bc3d8..8808c87a9 100644 --- a/libp2p/src/testFixtures/kotlin/io/libp2p/tools/TestStreamChannel.kt +++ b/libp2p/src/testFixtures/kotlin/io/libp2p/tools/TestStreamChannel.kt @@ -15,6 +15,7 @@ import io.libp2p.etc.util.netty.nettyInitializer import io.libp2p.transport.implementation.P2PChannelOverNetty import io.netty.channel.Channel import io.netty.channel.ChannelHandler +import io.netty.channel.EventLoop import io.netty.channel.embedded.EmbeddedChannel import java.util.concurrent.CompletableFuture @@ -42,6 +43,10 @@ private class TestStream(ch: Channel, initiator: Boolean) : P2PChannelOverNetty( nettyChannel.attr(PROTOCOL).set(CompletableFuture()) } + override fun eventLoop(): EventLoop { + TODO("Not yet implemented") + } + override fun remotePeerId(): PeerId { return PeerId(ByteArray(32)) } From 0a359bc6ce20202fef5e50bf33c05996c2482cdb Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 24 May 2023 13:17:32 +0100 Subject: [PATCH 24/37] Fix TLS test --- .../test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt b/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt index 33beada06..0a148fa7a 100644 --- a/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt +++ b/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt @@ -36,7 +36,6 @@ class TlsSecureChannelTest : SecureChannelTestBase( val eCh1 = makeDialChannel("#1", protocolSelect1, PeerId.fromPubKey(wrongPubKey)) val eCh2 = makeListenChannel("#2", protocolSelect2) - logger.log(Level.FINE, "Connecting channels...") TestChannel.interConnect(eCh1, eCh2) Assertions.assertThatThrownBy { protocolSelect1.selectedFuture.get(10, TimeUnit.SECONDS) } From bdf4690c076f6ae841667bbb0ec64bf181d8ddd0 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 24 May 2023 13:25:30 +0100 Subject: [PATCH 25/37] Lint test --- .../test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt b/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt index 0a148fa7a..f32d6778b 100644 --- a/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt +++ b/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt @@ -8,13 +8,11 @@ import io.libp2p.core.mux.StreamMuxerProtocol import io.libp2p.multistream.MultistreamProtocolDebugV1 import io.libp2p.security.InvalidRemotePubKey import io.libp2p.security.SecureChannelTestBase -import io.libp2p.security.logger import io.libp2p.tools.TestChannel import org.assertj.core.api.Assertions import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Test import java.util.concurrent.TimeUnit -import java.util.logging.Level val MultistreamProtocolV1: MultistreamProtocolDebug = MultistreamProtocolDebugV1() From d3ed41eb8dcc5c1a55e5c95ac5b92b5a287acd26 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 24 May 2023 14:09:09 +0100 Subject: [PATCH 26/37] Fix simulation test classes --- .../kotlin/io/libp2p/simulate/stream/Libp2pConnectionImpl.kt | 3 ++- .../kotlin/io/libp2p/simulate/stream/Libp2pStreamImpl.kt | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/simulator/src/main/kotlin/io/libp2p/simulate/stream/Libp2pConnectionImpl.kt b/tools/simulator/src/main/kotlin/io/libp2p/simulate/stream/Libp2pConnectionImpl.kt index 55c6f4f1e..f23d66285 100644 --- a/tools/simulator/src/main/kotlin/io/libp2p/simulate/stream/Libp2pConnectionImpl.kt +++ b/tools/simulator/src/main/kotlin/io/libp2p/simulate/stream/Libp2pConnectionImpl.kt @@ -24,7 +24,8 @@ class Libp2pConnectionImpl( SecureChannel.Session( PeerId.fromPubKey(localPubkey), PeerId.fromPubKey(remotePubkey), - remotePubkey + remotePubkey, + "" ) ) } diff --git a/tools/simulator/src/main/kotlin/io/libp2p/simulate/stream/Libp2pStreamImpl.kt b/tools/simulator/src/main/kotlin/io/libp2p/simulate/stream/Libp2pStreamImpl.kt index 4752ac5e4..0351535f1 100644 --- a/tools/simulator/src/main/kotlin/io/libp2p/simulate/stream/Libp2pStreamImpl.kt +++ b/tools/simulator/src/main/kotlin/io/libp2p/simulate/stream/Libp2pStreamImpl.kt @@ -6,6 +6,7 @@ import io.libp2p.etc.PROTOCOL import io.libp2p.etc.types.toVoidCompletableFuture import io.libp2p.transport.implementation.P2PChannelOverNetty import io.netty.channel.Channel +import io.netty.channel.EventLoop import java.util.concurrent.CompletableFuture class Libp2pStreamImpl( @@ -28,4 +29,8 @@ class Libp2pStreamImpl( override fun closeWrite(): CompletableFuture = nettyChannel.disconnect().toVoidCompletableFuture() + + override fun eventLoop(): EventLoop { + TODO("Not yet implemented") + } } From f1dd5bf75cff3d3ea6f907e4b1a34e4e5fd42d6f Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 30 May 2023 13:36:37 +0100 Subject: [PATCH 27/37] Revert "Expose EventLoop on Kotlin Stream" This reverts commit ea864894069a86d6e9dd08092234c96b64e668fd. --- libp2p/src/main/kotlin/io/libp2p/core/Stream.kt | 6 ------ .../io/libp2p/transport/implementation/StreamOverNetty.kt | 6 ------ libp2p/src/testFixtures/kotlin/io/libp2p/tools/Stubs.kt | 6 ------ .../kotlin/io/libp2p/tools/TestStreamChannel.kt | 5 ----- 4 files changed, 23 deletions(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/core/Stream.kt b/libp2p/src/main/kotlin/io/libp2p/core/Stream.kt index 309759356..c20a49d68 100644 --- a/libp2p/src/main/kotlin/io/libp2p/core/Stream.kt +++ b/libp2p/src/main/kotlin/io/libp2p/core/Stream.kt @@ -2,7 +2,6 @@ package io.libp2p.core import io.libp2p.protocol.ProtocolMessageHandler import io.libp2p.protocol.ProtocolMessageHandlerAdapter -import io.netty.channel.EventLoop import java.util.concurrent.CompletableFuture /** @@ -11,11 +10,6 @@ import java.util.concurrent.CompletableFuture interface Stream : P2PChannel { val connection: Connection - /** - * Return the underlying EventLoop - */ - fun eventLoop(): EventLoop - /** * Returns the [PeerId] of the remote peer [Connection] which this * [Stream] created on diff --git a/libp2p/src/main/kotlin/io/libp2p/transport/implementation/StreamOverNetty.kt b/libp2p/src/main/kotlin/io/libp2p/transport/implementation/StreamOverNetty.kt index 04a39ff22..b0324a7e7 100644 --- a/libp2p/src/main/kotlin/io/libp2p/transport/implementation/StreamOverNetty.kt +++ b/libp2p/src/main/kotlin/io/libp2p/transport/implementation/StreamOverNetty.kt @@ -6,7 +6,6 @@ import io.libp2p.core.Stream import io.libp2p.etc.PROTOCOL import io.libp2p.etc.types.toVoidCompletableFuture import io.netty.channel.Channel -import io.netty.channel.EventLoop import java.util.concurrent.CompletableFuture class StreamOverNetty( @@ -14,15 +13,10 @@ class StreamOverNetty( override val connection: Connection, initiator: Boolean ) : Stream, P2PChannelOverNetty(ch, initiator) { - - val group: EventLoop init { nettyChannel.attr(PROTOCOL).set(CompletableFuture()) - group = ch.eventLoop() } - override fun eventLoop() = group - /** * Returns the [PeerId] of the remote peer [Connection] which this * [Stream] created on diff --git a/libp2p/src/testFixtures/kotlin/io/libp2p/tools/Stubs.kt b/libp2p/src/testFixtures/kotlin/io/libp2p/tools/Stubs.kt index 005d36a47..8f60a0142 100644 --- a/libp2p/src/testFixtures/kotlin/io/libp2p/tools/Stubs.kt +++ b/libp2p/src/testFixtures/kotlin/io/libp2p/tools/Stubs.kt @@ -6,7 +6,6 @@ import io.libp2p.core.Stream import io.libp2p.core.multiformats.Multiaddr import io.libp2p.etc.util.P2PService import io.netty.channel.ChannelHandler -import io.netty.channel.EventLoop import java.util.concurrent.CompletableFuture class ConnectionStub : Connection { @@ -26,11 +25,6 @@ class ConnectionStub : Connection { class StreamStub : Stream { private val remotePeerId = PeerId.random() override val connection = ConnectionStub() - - override fun eventLoop(): EventLoop { - TODO("Not yet implemented") - } - override fun remotePeerId() = remotePeerId override fun getProtocol() = CompletableFuture.completedFuture("nop") override fun pushHandler(handler: ChannelHandler) = TODO() diff --git a/libp2p/src/testFixtures/kotlin/io/libp2p/tools/TestStreamChannel.kt b/libp2p/src/testFixtures/kotlin/io/libp2p/tools/TestStreamChannel.kt index 8808c87a9..a920bc3d8 100644 --- a/libp2p/src/testFixtures/kotlin/io/libp2p/tools/TestStreamChannel.kt +++ b/libp2p/src/testFixtures/kotlin/io/libp2p/tools/TestStreamChannel.kt @@ -15,7 +15,6 @@ import io.libp2p.etc.util.netty.nettyInitializer import io.libp2p.transport.implementation.P2PChannelOverNetty import io.netty.channel.Channel import io.netty.channel.ChannelHandler -import io.netty.channel.EventLoop import io.netty.channel.embedded.EmbeddedChannel import java.util.concurrent.CompletableFuture @@ -43,10 +42,6 @@ private class TestStream(ch: Channel, initiator: Boolean) : P2PChannelOverNetty( nettyChannel.attr(PROTOCOL).set(CompletableFuture()) } - override fun eventLoop(): EventLoop { - TODO("Not yet implemented") - } - override fun remotePeerId(): PeerId { return PeerId(ByteArray(32)) } From 4324ccbb9184e6b14f08bfc721ff98dc5541aadb Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 30 May 2023 13:36:55 +0100 Subject: [PATCH 28/37] Revert "Expose EventLoop on Stream" This reverts commit ec080fef01a7e4f4fbd2ded0609877f9b716e187. --- .../java/io/libp2p/tools/p2pd/NettyStream.java | 7 +------ .../java/io/libp2p/tools/p2pd/libp2pj/Stream.java | 4 ---- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/libp2p/src/testFixtures/java/io/libp2p/tools/p2pd/NettyStream.java b/libp2p/src/testFixtures/java/io/libp2p/tools/p2pd/NettyStream.java index 7212ab72d..b981ab62e 100644 --- a/libp2p/src/testFixtures/java/io/libp2p/tools/p2pd/NettyStream.java +++ b/libp2p/src/testFixtures/java/io/libp2p/tools/p2pd/NettyStream.java @@ -3,7 +3,7 @@ import io.libp2p.tools.p2pd.libp2pj.Muxer; import io.libp2p.tools.p2pd.libp2pj.Stream; import io.netty.buffer.Unpooled; -import io.netty.channel.*; +import io.netty.channel.Channel; import java.nio.ByteBuffer; @@ -30,11 +30,6 @@ public NettyStream(Channel channel, boolean initiator) { this(channel, initiator, null, null); } - @Override - public EventLoop eventLoop() { - return channel.eventLoop(); - } - @Override public void write(ByteBuffer data) { channel.write(Unpooled.wrappedBuffer(data)); diff --git a/libp2p/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Stream.java b/libp2p/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Stream.java index ba4845bd3..623882c11 100644 --- a/libp2p/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Stream.java +++ b/libp2p/src/testFixtures/java/io/libp2p/tools/p2pd/libp2pj/Stream.java @@ -1,7 +1,5 @@ package io.libp2p.tools.p2pd.libp2pj; -import io.netty.channel.*; - import java.nio.ByteBuffer; /** @@ -11,8 +9,6 @@ public interface Stream { boolean isInitiator(); - EventLoop eventLoop(); - void write(ByteBuffer data); void flush(); From 61e630eb73337afb3f9388142cda79beaa1856e5 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 30 May 2023 13:47:03 +0100 Subject: [PATCH 29/37] Remove other eventLoop() impl --- .../kotlin/io/libp2p/simulate/stream/Libp2pStreamImpl.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tools/simulator/src/main/kotlin/io/libp2p/simulate/stream/Libp2pStreamImpl.kt b/tools/simulator/src/main/kotlin/io/libp2p/simulate/stream/Libp2pStreamImpl.kt index 0351535f1..4752ac5e4 100644 --- a/tools/simulator/src/main/kotlin/io/libp2p/simulate/stream/Libp2pStreamImpl.kt +++ b/tools/simulator/src/main/kotlin/io/libp2p/simulate/stream/Libp2pStreamImpl.kt @@ -6,7 +6,6 @@ import io.libp2p.etc.PROTOCOL import io.libp2p.etc.types.toVoidCompletableFuture import io.libp2p.transport.implementation.P2PChannelOverNetty import io.netty.channel.Channel -import io.netty.channel.EventLoop import java.util.concurrent.CompletableFuture class Libp2pStreamImpl( @@ -29,8 +28,4 @@ class Libp2pStreamImpl( override fun closeWrite(): CompletableFuture = nettyChannel.disconnect().toVoidCompletableFuture() - - override fun eventLoop(): EventLoop { - TODO("Not yet implemented") - } } From 8cf6610aa7b469bf6f401e1ad4730fbf214436fe Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 30 May 2023 13:49:56 +0100 Subject: [PATCH 30/37] Revert java17 usage that somehow cherry-picked back in --- .github/workflows/build.yml | 2 +- build.gradle.kts | 8 ++++---- jitpack.yml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1c816f3c1..4991a5968 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/setup-java@v3 with: distribution: temurin - java-version: 17 + java-version: 11 - name: Setup Gradle uses: gradle/gradle-build-action@v2 diff --git a/build.gradle.kts b/build.gradle.kts index 46075ba77..b6e9a7271 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -70,12 +70,12 @@ configure( } java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } tasks.withType { - kotlinOptions.jvmTarget = "17" + kotlinOptions.jvmTarget = "11" kotlinOptions { languageVersion = "1.6" allWarningsAsErrors = true @@ -121,7 +121,7 @@ configure( outputDirectory.set(buildDir.resolve("dokka")) dokkaSourceSets { configureEach { - jdkVersion.set(17) + jdkVersion.set(11) reportUndocumented.set(false) externalDocumentationLink { url.set(URL("https://netty.io/4.1/api/")) diff --git a/jitpack.yml b/jitpack.yml index 1e41e00b7..46c852919 100644 --- a/jitpack.yml +++ b/jitpack.yml @@ -1,2 +1,2 @@ jdk: - - openjdk17 \ No newline at end of file + - openjdk11 \ No newline at end of file From 115dbac97c69a1825d4ac2df6d1bd585a54bd708 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 30 May 2023 14:26:08 +0100 Subject: [PATCH 31/37] Reinstate Java 11 support which somehow got lost --- libp2p/build.gradle.kts | 1 + .../libp2p/security/tls/TLSSecureChannel.kt | 46 +++++++------------ versions.gradle | 3 +- 3 files changed, 19 insertions(+), 31 deletions(-) diff --git a/libp2p/build.gradle.kts b/libp2p/build.gradle.kts index 4012cb2d7..49f8833ae 100644 --- a/libp2p/build.gradle.kts +++ b/libp2p/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { implementation("org.bouncycastle:bcprov-jdk15on") implementation("org.bouncycastle:bcpkix-jdk15on") + implementation("org.bouncycastle:bctls-jdk15on") testImplementation(project(":tools:schedulers")) diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt index 373abc108..2255eb626 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -31,16 +31,18 @@ import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.cert.X509v3CertificateBuilder import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters +import org.bouncycastle.jcajce.interfaces.EdDSAPublicKey +import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder import java.math.BigInteger import java.security.KeyFactory import java.security.PrivateKey import java.security.PublicKey +import java.security.Security import java.security.cert.Certificate import java.security.cert.CertificateException import java.security.cert.X509Certificate import java.security.interfaces.ECPublicKey -import java.security.interfaces.EdECPublicKey import java.security.spec.* import java.time.Instant import java.util.* @@ -48,8 +50,6 @@ import java.util.concurrent.CompletableFuture import java.util.logging.Level import java.util.logging.Logger import javax.net.ssl.X509TrustManager -import kotlin.experimental.and -import kotlin.experimental.or private val log = Logger.getLogger(TlsSecureChannel::class.java.name) private val SetupHandlerName = "TlsSetup" @@ -62,6 +62,10 @@ class TlsSecureChannel(private val localKey: PrivKey, private val muxerIds: List companion object { const val announce = "/tls/1.0.0" + init { + Security.insertProviderAt(Libp2pCrypto.provider, 1) + Security.insertProviderAt(BouncyCastleJsseProvider(), 2) + } } override val protocolDescriptor = ProtocolDescriptor(announce) @@ -101,12 +105,13 @@ fun buildTlsHandler( .ciphers(listOf("TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256")) .clientAuth(ClientAuth.REQUIRE) .trustManager(Libp2pTrustManager(expectedRemotePeer)) + .sslContextProvider(BouncyCastleJsseProvider()) .applicationProtocolConfig( ApplicationProtocolConfig( ApplicationProtocolConfig.Protocol.ALPN, ApplicationProtocolConfig.SelectorFailureBehavior.FATAL_ALERT, ApplicationProtocolConfig.SelectedListenerFailureBehavior.FATAL_ALERT, - muxerIds.plus("libp2p") + muxerIds.plus("libp2p") // early muxer negotiation ) ) .build() @@ -194,7 +199,7 @@ class Libp2pTrustManager(private val expectedRemotePeer: Optional) : X50 fun getJavaKey(priv: PrivKey): PrivateKey { if (priv.keyType == Crypto.KeyType.Ed25519) { - val kf = KeyFactory.getInstance("Ed25519") + val kf = KeyFactory.getInstance("Ed25519", Libp2pCrypto.provider) val privKeyInfo = PrivateKeyInfo(AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), DEROctetString(priv.raw())) val pkcs8KeySpec = PKCS8EncodedKeySpec(privKeyInfo.encoded) @@ -212,39 +217,20 @@ fun getJavaKey(priv: PrivKey): PrivateKey { throw IllegalArgumentException("Unsupported TLS key type:" + priv.keyType) } -fun getJavaPublicKey(pub: PubKey): PublicKey { +fun getAsn1EncodedPublicKey(pub: PubKey): ByteArray { if (pub.keyType == Crypto.KeyType.Ed25519) { - val kf = KeyFactory.getInstance("Ed25519") - - // determine if x was odd. - var pk = pub.raw() - val lastbyteInt = pk[pk.lastIndex].toInt() - var xisodd = lastbyteInt.and(255).shr(7) == 1 - // make sure most significant bit will be 0 - after reversing. - pk[31] = pk[31].and(127) - val y = BigInteger(1, pk.reversedArray()) - - val paramSpec = NamedParameterSpec("Ed25519") - val ep = EdECPoint(xisodd, y) - val pubSpec = EdECPublicKeySpec(paramSpec, ep) - return kf.generatePublic(pubSpec) + return SubjectPublicKeyInfo(AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), pub.raw()).encoded } if (pub.keyType == Crypto.KeyType.ECDSA) { - return (pub as EcdsaPublicKey).javaKey() + return (pub as EcdsaPublicKey).javaKey().encoded } throw IllegalArgumentException("Unsupported TLS key type:" + pub.keyType) } fun getPubKey(pub: PublicKey): PubKey { if (pub.algorithm.equals("EdDSA") || pub.algorithm.equals("Ed25519")) { - // It seems batshit that we have to do this, but haven't found an equivalent library call - val point = (pub as EdECPublicKey).point - var pk = point.y.toByteArray().reversedArray() - if (pk.size == 31) - pk = pk.plus(0) - if (point.isXOdd) - pk[31] = pk[31].or(0x80.toByte()) - return Ed25519PublicKey(Ed25519PublicKeyParameters(pk)) + val raw = (pub as EdDSAPublicKey).pointEncoding + return Ed25519PublicKey(Ed25519PublicKeyParameters(raw)) } if (pub.algorithm.equals("EC")) { return EcdsaPublicKey(pub as ECPublicKey) @@ -295,7 +281,7 @@ fun getPublicKeyFromCert(chain: Array): PubKey { * */ fun buildCert(hostKey: PrivKey, subjectKey: PrivKey): X509Certificate { - val publicKeyAsn1 = getJavaPublicKey(subjectKey.publicKey()).encoded + val publicKeyAsn1 = getAsn1EncodedPublicKey(subjectKey.publicKey()) val subPubKeyInfo = SubjectPublicKeyInfo.getInstance(publicKeyAsn1) val now = Instant.now() diff --git a/versions.gradle b/versions.gradle index 0305071bb..44bf4a254 100644 --- a/versions.gradle +++ b/versions.gradle @@ -27,7 +27,7 @@ dependencyManagement { entry 'protobuf-java' entry 'protoc' } - dependencySet(group: "io.netty", version: "4.1.87.Final") { + dependencySet(group: "io.netty", version: "4.1.90.Final") { entry 'netty-common' entry 'netty-handler' entry 'netty-transport' @@ -40,6 +40,7 @@ dependencyManagement { dependencySet(group: "org.bouncycastle", version: "1.70") { entry 'bcprov-jdk15on' entry 'bcpkix-jdk15on' + entry 'bctls-jdk15on' } } } \ No newline at end of file From 1e4467b225b516b26487aaaa5723bdab35828482 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 31 May 2023 12:55:32 +0400 Subject: [PATCH 32/37] SecureChannel: rename `nextProto` to `muxerProtocol` and make it optional --- .../io/libp2p/core/security/SecureChannel.kt | 10 +++++++--- .../security/noise/NoiseSecureChannelSession.kt | 2 +- .../libp2p/security/noise/NoiseXXSecureChannel.kt | 10 +++++----- .../plaintext/PlaintextInsecureChannel.kt | 9 +++++++-- .../libp2p/security/secio/SecIoSecureChannel.kt | 9 +++++++-- .../io/libp2p/security/tls/TLSSecureChannel.kt | 4 +++- .../io/libp2p/transport/ConnectionUpgrader.kt | 15 +++++++-------- .../transport/implementation/ConnectionBuilder.kt | 6 +++++- .../io/libp2p/core/dsl/BuilderDefaultsTest.kt | 4 ++-- .../kotlin/io/libp2p/pubsub/TestRouter.kt | 2 +- .../kotlin/io/libp2p/tools/HostFactory.kt | 3 ++- .../io/libp2p/transport/NullConnectionUpgrader.kt | 2 +- .../simulate/stream/Libp2pConnectionImpl.kt | 2 +- 13 files changed, 49 insertions(+), 29 deletions(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/core/security/SecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/core/security/SecureChannel.kt index 66fd4ed19..44d4e2dd2 100644 --- a/libp2p/src/main/kotlin/io/libp2p/core/security/SecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/core/security/SecureChannel.kt @@ -3,11 +3,13 @@ package io.libp2p.core.security import io.libp2p.core.PeerId import io.libp2p.core.crypto.PubKey import io.libp2p.core.multistream.ProtocolBinding +import io.libp2p.core.multistream.ProtocolId /** * The SecureChannel interface is implemented by all security channels, such as SecIO, TLS 1.3, Noise, and so on. */ interface SecureChannel : ProtocolBinding { + open class Session( /** * The peer ID of the local peer. @@ -24,9 +26,11 @@ interface SecureChannel : ProtocolBinding { */ val remotePubKey: PubKey, - /** The id of the next protocol, used to select the muxer - * + /** + * Contains muxer [ProtocolId] if security protocol supports + * [Early Multiplexer Negotiation](https://docs.libp2p.io/concepts/multiplex/early-negotiation/) + * and the protocol was successfully negotiated. Else contains `null` */ - val nextProto: String + val muxerProtocol: ProtocolId? ) } diff --git a/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseSecureChannelSession.kt b/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseSecureChannelSession.kt index d1ffc03fa..bdf032a57 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseSecureChannelSession.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseSecureChannelSession.kt @@ -11,4 +11,4 @@ class NoiseSecureChannelSession( remotePubKey: PubKey, val aliceCipher: CipherState, val bobCipher: CipherState -) : SecureChannel.Session(localId, remoteId, remotePubKey, "") +) : SecureChannel.Session(localId, remoteId, remotePubKey, null) diff --git a/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseXXSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseXXSecureChannel.kt index 4c1ccd70b..e8e031e83 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseXXSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseXXSecureChannel.kt @@ -11,6 +11,7 @@ import io.libp2p.core.crypto.PubKey import io.libp2p.core.crypto.marshalPublicKey import io.libp2p.core.crypto.unmarshalPublicKey import io.libp2p.core.multistream.ProtocolDescriptor +import io.libp2p.core.multistream.ProtocolId import io.libp2p.core.security.SecureChannel import io.libp2p.etc.REMOTE_PEER_ID import io.libp2p.etc.types.toByteArray @@ -45,9 +46,12 @@ class UShortLengthCodec : CombinedChannelDuplexHandler) : +class NoiseXXSecureChannel(private val localKey: PrivKey) : SecureChannel { + @Suppress("UNUSED_PARAMETER") + constructor(localKey: PrivKey, muxerProtocols: List) : this(localKey) + companion object { const val protocolName = "Noise_XX_25519_ChaChaPoly_SHA256" const val announce = "/noise" @@ -63,10 +67,6 @@ class NoiseXXSecureChannel(private val localKey: PrivKey, private val muxers: Li override val protocolDescriptor = ProtocolDescriptor(announce) - fun initChannel(ch: P2PChannel): CompletableFuture { - return initChannel(ch, "") - } // initChannel - override fun initChannel( ch: P2PChannel, selectedProtocol: String diff --git a/libp2p/src/main/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannel.kt index 2a5da5173..17ebc0183 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannel.kt @@ -9,6 +9,7 @@ import io.libp2p.core.crypto.PrivKey import io.libp2p.core.crypto.PubKey import io.libp2p.core.crypto.unmarshalPublicKey import io.libp2p.core.multistream.ProtocolDescriptor +import io.libp2p.core.multistream.ProtocolId import io.libp2p.core.security.SecureChannel import io.libp2p.etc.types.toProtobuf import io.libp2p.security.InvalidInitialPacket @@ -22,7 +23,11 @@ import io.netty.handler.codec.LengthFieldPrepender import plaintext.pb.Plaintext import java.util.concurrent.CompletableFuture -class PlaintextInsecureChannel(private val localKey: PrivKey, private val muxerIds: List) : SecureChannel { +class PlaintextInsecureChannel(private val localKey: PrivKey) : SecureChannel { + + @Suppress("UNUSED_PARAMETER") + constructor(localKey: PrivKey, muxerProtocols: List) : this(localKey) + override val protocolDescriptor = ProtocolDescriptor("/plaintext/2.0.0") override fun initChannel(ch: P2PChannel, selectedProtocol: String): CompletableFuture { @@ -108,7 +113,7 @@ class PlaintextHandshakeHandler( localPeerId, remotePeerId, remotePubKey, - "" + null ) handshakeCompleted.complete(session) diff --git a/libp2p/src/main/kotlin/io/libp2p/security/secio/SecIoSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/secio/SecIoSecureChannel.kt index 13379ab9c..d0429b540 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/secio/SecIoSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/secio/SecIoSecureChannel.kt @@ -5,6 +5,7 @@ import io.libp2p.core.P2PChannel import io.libp2p.core.PeerId import io.libp2p.core.crypto.PrivKey import io.libp2p.core.multistream.ProtocolDescriptor +import io.libp2p.core.multistream.ProtocolId import io.libp2p.core.security.SecureChannel import io.libp2p.etc.REMOTE_PEER_ID import io.netty.buffer.ByteBuf @@ -18,7 +19,11 @@ import java.util.concurrent.CompletableFuture private val log = LoggerFactory.getLogger(SecIoSecureChannel::class.java) private val HandshakeHandlerName = "SecIoHandshake" -class SecIoSecureChannel(private val localKey: PrivKey, private val muxerIds: List) : SecureChannel { +class SecIoSecureChannel(private val localKey: PrivKey) : SecureChannel { + + @Suppress("UNUSED_PARAMETER") + constructor(localKey: PrivKey, muxerProtocols: List) : this(localKey) + override val protocolDescriptor = ProtocolDescriptor("/secio/1.0.0") override fun initChannel(ch: P2PChannel, selectedProtocol: String): CompletableFuture { @@ -69,7 +74,7 @@ private class SecIoHandshake( PeerId.fromPubKey(secIoCodec.local.permanentPubKey), PeerId.fromPubKey(secIoCodec.remote.permanentPubKey), secIoCodec.remote.permanentPubKey, - "" + null ) handshakeComplete.complete(session) ctx.channel().pipeline().remove(HandshakeHandlerName) diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt index 2255eb626..70d187ceb 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -127,7 +127,9 @@ fun buildTlsHandler( handshakeComplete.completeExceptionally(cause) } else { val negotiatedProtocols = sslContext.applicationProtocolNegotiator().protocols() - val selectedProtocol = negotiatedProtocols.filter { name -> muxerIds.contains(name) }.getOrElse(0, defaultValue = { _ -> "" }) + val selectedProtocol = negotiatedProtocols + .filter { name -> muxerIds.contains(name) } + .getOrNull(0) handshakeComplete.complete( SecureChannel.Session( PeerId.fromPubKey(localKey.publicKey()), diff --git a/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt b/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt index 487f3cbeb..53d8dadd2 100644 --- a/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt +++ b/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt @@ -4,6 +4,7 @@ import io.libp2p.core.Connection import io.libp2p.core.NoSuchLocalProtocolException import io.libp2p.core.multistream.MultistreamProtocol import io.libp2p.core.multistream.ProtocolBinding +import io.libp2p.core.multistream.ProtocolId import io.libp2p.core.mux.StreamMuxer import io.libp2p.core.security.SecureChannel import io.libp2p.etc.getP2PChannel @@ -37,12 +38,10 @@ open class ConnectionUpgrader( ) } - open fun establishMuxer(muxerId: String, connection: Connection): CompletableFuture { - if (muxerId.isEmpty() || muxerId.equals("libp2p")) { - return establishMuxer(connection) - } - val muxer = muxers.find { m -> m.protocolDescriptor.announceProtocols.contains(muxerId) } - ?: throw NoSuchLocalProtocolException("Early Muxer negotiation selected unsupported muxer: $muxerId") + open fun establishMuxer(muxerId: ProtocolId, connection: Connection): CompletableFuture { + val muxer = muxers.find { m -> + m.protocolDescriptor.matchesAny(listOf( muxerId)) + } ?: throw NoSuchLocalProtocolException("Early Muxer negotiation selected unsupported muxer: $muxerId") val res = CompletableFuture() connection.pushHandler( nettyInitializer { @@ -55,9 +54,9 @@ open class ConnectionUpgrader( private fun , R> establish( multistreamProtocol: MultistreamProtocol, connection: Connection, - channels: List + bindings: List ): CompletableFuture { - val multistream = multistreamProtocol.createMultistream(channels) + val multistream = multistreamProtocol.createMultistream(bindings) return multistream.initChannel(connection) } // establish } // ConnectionUpgrader diff --git a/libp2p/src/main/kotlin/io/libp2p/transport/implementation/ConnectionBuilder.kt b/libp2p/src/main/kotlin/io/libp2p/transport/implementation/ConnectionBuilder.kt index 01ec6eedf..73469be10 100644 --- a/libp2p/src/main/kotlin/io/libp2p/transport/implementation/ConnectionBuilder.kt +++ b/libp2p/src/main/kotlin/io/libp2p/transport/implementation/ConnectionBuilder.kt @@ -32,7 +32,11 @@ class ConnectionBuilder( upgrader.establishSecureChannel(connection) .thenCompose { connection.setSecureSession(it) - upgrader.establishMuxer(it.nextProto, connection) + if (it.muxerProtocol != null) { + upgrader.establishMuxer(it.muxerProtocol, connection) + } else { + upgrader.establishMuxer(connection) + } }.thenApply { connection.setMuxerSession(it) connHandler.handleConnection(connection) diff --git a/libp2p/src/test/kotlin/io/libp2p/core/dsl/BuilderDefaultsTest.kt b/libp2p/src/test/kotlin/io/libp2p/core/dsl/BuilderDefaultsTest.kt index e2609512b..f7bc79b7a 100644 --- a/libp2p/src/test/kotlin/io/libp2p/core/dsl/BuilderDefaultsTest.kt +++ b/libp2p/src/test/kotlin/io/libp2p/core/dsl/BuilderDefaultsTest.kt @@ -46,7 +46,7 @@ class BuilderDefaultsTest { host(Builder.Defaults.None) { identity { random() } transports { +::TcpTransport } - secureChannels { +::SecIoSecureChannel } + secureChannels { add(::SecIoSecureChannel) } } } } @@ -56,7 +56,7 @@ class BuilderDefaultsTest { val host = host(Builder.Defaults.None) { identity { random() } transports { +::TcpTransport } - secureChannels { +::SecIoSecureChannel } + secureChannels { add(::SecIoSecureChannel) } muxers { + StreamMuxerProtocol.Mplex } } diff --git a/libp2p/src/testFixtures/kotlin/io/libp2p/pubsub/TestRouter.kt b/libp2p/src/testFixtures/kotlin/io/libp2p/pubsub/TestRouter.kt index f62b2a475..a90a9f45e 100644 --- a/libp2p/src/testFixtures/kotlin/io/libp2p/pubsub/TestRouter.kt +++ b/libp2p/src/testFixtures/kotlin/io/libp2p/pubsub/TestRouter.kt @@ -68,7 +68,7 @@ class TestRouter( ConnectionOverNetty(parentChannel, NullTransport(), initiator) connection.setSecureSession( SecureChannel.Session( - peerId, remoteRouter.peerId, remoteRouter.keyPair.second, "" + peerId, remoteRouter.peerId, remoteRouter.keyPair.second, null ) ) diff --git a/libp2p/src/testFixtures/kotlin/io/libp2p/tools/HostFactory.kt b/libp2p/src/testFixtures/kotlin/io/libp2p/tools/HostFactory.kt index 2af4b035f..08d3be9e3 100644 --- a/libp2p/src/testFixtures/kotlin/io/libp2p/tools/HostFactory.kt +++ b/libp2p/src/testFixtures/kotlin/io/libp2p/tools/HostFactory.kt @@ -7,6 +7,7 @@ import io.libp2p.core.crypto.PrivKey import io.libp2p.core.crypto.PubKey import io.libp2p.core.crypto.generateKeyPair import io.libp2p.core.dsl.Builder +import io.libp2p.core.dsl.SecureChannelCtor import io.libp2p.core.dsl.host import io.libp2p.core.multiformats.Multiaddr import io.libp2p.core.multiformats.Protocol @@ -26,7 +27,7 @@ class HostFactory { var keyType = KEY_TYPE.ECDSA var tcpPort = 5000 var transportCtor = ::TcpTransport - var secureCtor = ::NoiseXXSecureChannel + var secureCtor: SecureChannelCtor = ::NoiseXXSecureChannel var mplexCtor = ::MplexStreamMuxer var muxLogLevel: LogLevel? = LogLevel.DEBUG diff --git a/libp2p/src/testFixtures/kotlin/io/libp2p/transport/NullConnectionUpgrader.kt b/libp2p/src/testFixtures/kotlin/io/libp2p/transport/NullConnectionUpgrader.kt index 2d24866d6..62f1b6c2c 100644 --- a/libp2p/src/testFixtures/kotlin/io/libp2p/transport/NullConnectionUpgrader.kt +++ b/libp2p/src/testFixtures/kotlin/io/libp2p/transport/NullConnectionUpgrader.kt @@ -26,7 +26,7 @@ class NullConnectionUpgrader : PeerId.random(), PeerId.random(), generateKeyPair(KEY_TYPE.RSA).second, - "" + null ) return CompletableFuture.completedFuture(nonsenseSession) } // establishSecureChannel diff --git a/tools/simulator/src/main/kotlin/io/libp2p/simulate/stream/Libp2pConnectionImpl.kt b/tools/simulator/src/main/kotlin/io/libp2p/simulate/stream/Libp2pConnectionImpl.kt index f23d66285..9c17a0050 100644 --- a/tools/simulator/src/main/kotlin/io/libp2p/simulate/stream/Libp2pConnectionImpl.kt +++ b/tools/simulator/src/main/kotlin/io/libp2p/simulate/stream/Libp2pConnectionImpl.kt @@ -25,7 +25,7 @@ class Libp2pConnectionImpl( PeerId.fromPubKey(localPubkey), PeerId.fromPubKey(remotePubkey), remotePubkey, - "" + null ) ) } From d5ba11b1241d906276b29e588d220cde56fc7278 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 31 May 2023 15:11:20 +0400 Subject: [PATCH 33/37] Add more type safety: since SecureChannel may early negotiate just a puxer protocol, pass a list of StreamMuxers to constructor. SecureChannel.Session contains NegotiatedStreamMuxer instance instead of string protocol representation --- .../java/io/libp2p/core/dsl/HostBuilder.java | 6 +-- .../kotlin/io/libp2p/core/dsl/Builders.kt | 4 +- .../core/multistream/NegotiatedProtocol.kt | 15 ++++++ .../core/multistream/ProtocolBinding.kt | 7 +-- .../libp2p/core/mux/NegotiatedStreamMuxer.kt | 5 ++ .../kotlin/io/libp2p/core/mux/StreamMuxer.kt | 5 +- .../io/libp2p/core/security/SecureChannel.kt | 6 +-- .../security/noise/NoiseXXSecureChannel.kt | 3 +- .../plaintext/PlaintextInsecureChannel.kt | 3 +- .../security/secio/SecIoSecureChannel.kt | 3 +- .../libp2p/security/tls/TLSSecureChannel.kt | 47 ++++++++++++++----- .../io/libp2p/transport/ConnectionUpgrader.kt | 28 +++++------ .../implementation/ConnectionBuilder.kt | 4 +- .../security/CipherSecureChannelTest.kt | 5 +- .../libp2p/security/SecureChannelTestBase.kt | 7 +-- .../security/tls/TlsSecureChannelTest.kt | 2 +- 16 files changed, 98 insertions(+), 52 deletions(-) create mode 100644 libp2p/src/main/kotlin/io/libp2p/core/multistream/NegotiatedProtocol.kt create mode 100644 libp2p/src/main/kotlin/io/libp2p/core/mux/NegotiatedStreamMuxer.kt diff --git a/libp2p/src/main/java/io/libp2p/core/dsl/HostBuilder.java b/libp2p/src/main/java/io/libp2p/core/dsl/HostBuilder.java index e5e25b761..069d5701a 100644 --- a/libp2p/src/main/java/io/libp2p/core/dsl/HostBuilder.java +++ b/libp2p/src/main/java/io/libp2p/core/dsl/HostBuilder.java @@ -40,7 +40,7 @@ public final HostBuilder transport( @SafeVarargs public final HostBuilder secureChannel( - BiFunction, SecureChannel>... secureChannels) { + BiFunction, SecureChannel>... secureChannels) { secureChannels_.addAll(Arrays.asList(secureChannels)); return this; } @@ -75,7 +75,7 @@ public Host build() { b.getTransports().add(t::apply) ); secureChannels_.forEach(sc -> - b.getSecureChannels().add((k, m) -> sc.apply(k, (List)m)) + b.getSecureChannels().add((k, m) -> sc.apply(k, (List)m)) ); muxers_.forEach(m -> b.getMuxers().add(m.get()) @@ -90,7 +90,7 @@ public Host build() { private DefaultMode defaultMode_; private List> transports_ = new ArrayList<>(); - private List, SecureChannel>> secureChannels_ = new ArrayList<>(); + private List, SecureChannel>> secureChannels_ = new ArrayList<>(); private List> muxers_ = new ArrayList<>(); private List> protocols_ = new ArrayList<>(); private List listenAddresses_ = new ArrayList<>(); diff --git a/libp2p/src/main/kotlin/io/libp2p/core/dsl/Builders.kt b/libp2p/src/main/kotlin/io/libp2p/core/dsl/Builders.kt index 9a9df2905..5482b5e8e 100644 --- a/libp2p/src/main/kotlin/io/libp2p/core/dsl/Builders.kt +++ b/libp2p/src/main/kotlin/io/libp2p/core/dsl/Builders.kt @@ -35,7 +35,7 @@ import io.netty.handler.logging.LogLevel import io.netty.handler.logging.LoggingHandler typealias TransportCtor = (ConnectionUpgrader) -> Transport -typealias SecureChannelCtor = (PrivKey, List) -> SecureChannel +typealias SecureChannelCtor = (PrivKey, List) -> SecureChannel typealias IdentityFactory = () -> PrivKey class HostConfigurationException(message: String) : RuntimeException(message) @@ -175,7 +175,7 @@ open class Builder { val muxers = muxers.map { it.createMuxer(streamMultistreamProtocol, protocols.values) } - val secureChannels = secureChannels.values.map { it(privKey, muxers.flatMap { it.protocolDescriptor.announceProtocols }) } + val secureChannels = secureChannels.values.map { it(privKey, muxers) } if (debug.muxFramesHandler.handlers.isNotEmpty()) { val broadcast = ChannelVisitor.createBroadcast(*debug.muxFramesHandler.handlers.toTypedArray()) diff --git a/libp2p/src/main/kotlin/io/libp2p/core/multistream/NegotiatedProtocol.kt b/libp2p/src/main/kotlin/io/libp2p/core/multistream/NegotiatedProtocol.kt new file mode 100644 index 000000000..a1557b95c --- /dev/null +++ b/libp2p/src/main/kotlin/io/libp2p/core/multistream/NegotiatedProtocol.kt @@ -0,0 +1,15 @@ +package io.libp2p.core.multistream + +import io.libp2p.core.P2PChannel +import java.util.concurrent.CompletableFuture + +/** + * Represents [ProtocolBinding] with exact protocol version which was agreed on + */ +open class NegotiatedProtocol> ( + val binding: TBinding, + val protocol: ProtocolId +) { + open fun initChannel(ch: P2PChannel): CompletableFuture = + binding.initChannel(ch, protocol) +} \ No newline at end of file diff --git a/libp2p/src/main/kotlin/io/libp2p/core/multistream/ProtocolBinding.kt b/libp2p/src/main/kotlin/io/libp2p/core/multistream/ProtocolBinding.kt index 054b3c0f3..dd355c15a 100644 --- a/libp2p/src/main/kotlin/io/libp2p/core/multistream/ProtocolBinding.kt +++ b/libp2p/src/main/kotlin/io/libp2p/core/multistream/ProtocolBinding.kt @@ -44,7 +44,7 @@ interface ProtocolBinding { /** * Returns initializer for this protocol on the provided channel, together with an optional controller object. */ - fun initChannel(ch: P2PChannel, selectedProtocol: String): CompletableFuture + fun initChannel(ch: P2PChannel, selectedProtocol: ProtocolId): CompletableFuture /** * If the [matcher] of this binding is not [Mode.STRICT] then it can't play initiator role since @@ -56,7 +56,7 @@ interface ProtocolBinding { val srcBinding = this return object : ProtocolBinding { override val protocolDescriptor = ProtocolDescriptor(protocols, srcBinding.protocolDescriptor.protocolMatcher) - override fun initChannel(ch: P2PChannel, selectedProtocol: String): CompletableFuture = + override fun initChannel(ch: P2PChannel, selectedProtocol: ProtocolId): CompletableFuture = srcBinding.initChannel(ch, selectedProtocol) } } @@ -68,10 +68,11 @@ interface ProtocolBinding { fun createSimple(protocolName: ProtocolId, handler: P2PChannelHandler): ProtocolBinding { return object : ProtocolBinding { override val protocolDescriptor = ProtocolDescriptor(protocolName) - override fun initChannel(ch: P2PChannel, selectedProtocol: String): CompletableFuture { + override fun initChannel(ch: P2PChannel, selectedProtocol: ProtocolId): CompletableFuture { return handler.initChannel(ch) } } } } } + diff --git a/libp2p/src/main/kotlin/io/libp2p/core/mux/NegotiatedStreamMuxer.kt b/libp2p/src/main/kotlin/io/libp2p/core/mux/NegotiatedStreamMuxer.kt new file mode 100644 index 000000000..21a606828 --- /dev/null +++ b/libp2p/src/main/kotlin/io/libp2p/core/mux/NegotiatedStreamMuxer.kt @@ -0,0 +1,5 @@ +package io.libp2p.core.mux + +import io.libp2p.core.multistream.NegotiatedProtocol + +typealias NegotiatedStreamMuxer = NegotiatedProtocol \ No newline at end of file diff --git a/libp2p/src/main/kotlin/io/libp2p/core/mux/StreamMuxer.kt b/libp2p/src/main/kotlin/io/libp2p/core/mux/StreamMuxer.kt index 4113e427e..b753c44a0 100644 --- a/libp2p/src/main/kotlin/io/libp2p/core/mux/StreamMuxer.kt +++ b/libp2p/src/main/kotlin/io/libp2p/core/mux/StreamMuxer.kt @@ -1,9 +1,6 @@ package io.libp2p.core.mux -import io.libp2p.core.ChannelVisitor -import io.libp2p.core.Connection -import io.libp2p.core.StreamHandler -import io.libp2p.core.StreamPromise +import io.libp2p.core.* import io.libp2p.core.multistream.ProtocolBinding /** diff --git a/libp2p/src/main/kotlin/io/libp2p/core/security/SecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/core/security/SecureChannel.kt index 44d4e2dd2..abd0b69cd 100644 --- a/libp2p/src/main/kotlin/io/libp2p/core/security/SecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/core/security/SecureChannel.kt @@ -3,7 +3,7 @@ package io.libp2p.core.security import io.libp2p.core.PeerId import io.libp2p.core.crypto.PubKey import io.libp2p.core.multistream.ProtocolBinding -import io.libp2p.core.multistream.ProtocolId +import io.libp2p.core.mux.NegotiatedStreamMuxer /** * The SecureChannel interface is implemented by all security channels, such as SecIO, TLS 1.3, Noise, and so on. @@ -27,10 +27,10 @@ interface SecureChannel : ProtocolBinding { val remotePubKey: PubKey, /** - * Contains muxer [ProtocolId] if security protocol supports + * Contains muxer if security protocol supports * [Early Multiplexer Negotiation](https://docs.libp2p.io/concepts/multiplex/early-negotiation/) * and the protocol was successfully negotiated. Else contains `null` */ - val muxerProtocol: ProtocolId? + val earlyMuxer: NegotiatedStreamMuxer? ) } diff --git a/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseXXSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseXXSecureChannel.kt index e8e031e83..01e2a5c9e 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseXXSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseXXSecureChannel.kt @@ -12,6 +12,7 @@ import io.libp2p.core.crypto.marshalPublicKey import io.libp2p.core.crypto.unmarshalPublicKey import io.libp2p.core.multistream.ProtocolDescriptor import io.libp2p.core.multistream.ProtocolId +import io.libp2p.core.mux.StreamMuxer import io.libp2p.core.security.SecureChannel import io.libp2p.etc.REMOTE_PEER_ID import io.libp2p.etc.types.toByteArray @@ -50,7 +51,7 @@ class NoiseXXSecureChannel(private val localKey: PrivKey) : SecureChannel { @Suppress("UNUSED_PARAMETER") - constructor(localKey: PrivKey, muxerProtocols: List) : this(localKey) + constructor(localKey: PrivKey, muxerProtocols: List) : this(localKey) companion object { const val protocolName = "Noise_XX_25519_ChaChaPoly_SHA256" diff --git a/libp2p/src/main/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannel.kt index 17ebc0183..c6c6dbd1f 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannel.kt @@ -10,6 +10,7 @@ import io.libp2p.core.crypto.PubKey import io.libp2p.core.crypto.unmarshalPublicKey import io.libp2p.core.multistream.ProtocolDescriptor import io.libp2p.core.multistream.ProtocolId +import io.libp2p.core.mux.StreamMuxer import io.libp2p.core.security.SecureChannel import io.libp2p.etc.types.toProtobuf import io.libp2p.security.InvalidInitialPacket @@ -26,7 +27,7 @@ import java.util.concurrent.CompletableFuture class PlaintextInsecureChannel(private val localKey: PrivKey) : SecureChannel { @Suppress("UNUSED_PARAMETER") - constructor(localKey: PrivKey, muxerProtocols: List) : this(localKey) + constructor(localKey: PrivKey, muxerProtocols: List) : this(localKey) override val protocolDescriptor = ProtocolDescriptor("/plaintext/2.0.0") diff --git a/libp2p/src/main/kotlin/io/libp2p/security/secio/SecIoSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/secio/SecIoSecureChannel.kt index d0429b540..18c93d068 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/secio/SecIoSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/secio/SecIoSecureChannel.kt @@ -6,6 +6,7 @@ import io.libp2p.core.PeerId import io.libp2p.core.crypto.PrivKey import io.libp2p.core.multistream.ProtocolDescriptor import io.libp2p.core.multistream.ProtocolId +import io.libp2p.core.mux.StreamMuxer import io.libp2p.core.security.SecureChannel import io.libp2p.etc.REMOTE_PEER_ID import io.netty.buffer.ByteBuf @@ -22,7 +23,7 @@ private val HandshakeHandlerName = "SecIoHandshake" class SecIoSecureChannel(private val localKey: PrivKey) : SecureChannel { @Suppress("UNUSED_PARAMETER") - constructor(localKey: PrivKey, muxerProtocols: List) : this(localKey) + constructor(localKey: PrivKey, muxerProtocols: List) : this(localKey) override val protocolDescriptor = ProtocolDescriptor("/secio/1.0.0") diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt index 70d187ceb..ae4fbf129 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -5,7 +5,11 @@ import io.libp2p.core.* import io.libp2p.core.crypto.PrivKey import io.libp2p.core.crypto.PubKey import io.libp2p.core.crypto.unmarshalPublicKey +import io.libp2p.core.multistream.ProtocolBinding import io.libp2p.core.multistream.ProtocolDescriptor +import io.libp2p.core.multistream.ProtocolId +import io.libp2p.core.mux.StreamMuxer +import io.libp2p.core.mux.NegotiatedStreamMuxer import io.libp2p.core.security.SecureChannel import io.libp2p.crypto.Libp2pCrypto import io.libp2p.crypto.keys.EcdsaPublicKey @@ -55,10 +59,10 @@ private val log = Logger.getLogger(TlsSecureChannel::class.java.name) private val SetupHandlerName = "TlsSetup" val certificatePrefix = "libp2p-tls-handshake:".encodeToByteArray() -class TlsSecureChannel(private val localKey: PrivKey, private val muxerIds: List, private val certAlgorithm: String) : +class TlsSecureChannel(private val localKey: PrivKey, private val muxers: List, private val certAlgorithm: String) : SecureChannel { - constructor(localKey: PrivKey, muxerIds: List) : this(localKey, muxerIds, "Ed25519") {} + constructor(localKey: PrivKey, muxerIds: List) : this(localKey, muxerIds, "Ed25519") {} companion object { const val announce = "/tls/1.0.0" @@ -79,7 +83,7 @@ class TlsSecureChannel(private val localKey: PrivKey, private val muxerIds: List selectedProtocol: String ): CompletableFuture { val handshakeComplete = CompletableFuture() - ch.pushHandler(SetupHandlerName, ChannelSetup(localKey, muxerIds, certAlgorithm, ch, handshakeComplete)) + ch.pushHandler(SetupHandlerName, ChannelSetup(localKey, muxers, certAlgorithm, ch, handshakeComplete)) return handshakeComplete } } @@ -87,7 +91,7 @@ class TlsSecureChannel(private val localKey: PrivKey, private val muxerIds: List fun buildTlsHandler( localKey: PrivKey, expectedRemotePeer: Optional, - muxerIds: List, + muxers: List, certAlgorithm: String, ch: P2PChannel, handshakeComplete: CompletableFuture, @@ -111,7 +115,7 @@ fun buildTlsHandler( ApplicationProtocolConfig.Protocol.ALPN, ApplicationProtocolConfig.SelectorFailureBehavior.FATAL_ALERT, ApplicationProtocolConfig.SelectedListenerFailureBehavior.FATAL_ALERT, - muxerIds.plus("libp2p") // early muxer negotiation + muxers.allProtocols // early muxer negotiation ) ) .build() @@ -127,15 +131,13 @@ fun buildTlsHandler( handshakeComplete.completeExceptionally(cause) } else { val negotiatedProtocols = sslContext.applicationProtocolNegotiator().protocols() - val selectedProtocol = negotiatedProtocols - .filter { name -> muxerIds.contains(name) } - .getOrNull(0) + val selectedMuxer = muxers.findBestMatch(negotiatedProtocols) handshakeComplete.complete( SecureChannel.Session( PeerId.fromPubKey(localKey.publicKey()), verifyAndExtractPeerId(engine.session.peerCertificates), getPublicKeyFromCert(engine.session.peerCertificates), - selectedProtocol + selectedMuxer ) ) ctx.fireChannelActive() @@ -144,9 +146,22 @@ fun buildTlsHandler( return handler } +private val > List.allProtocols: List get() = + this.flatMap { it.protocolDescriptor.announceProtocols } + +private fun List.findBestMatch(remoteProtocols: List): NegotiatedStreamMuxer? = + this.firstNotNullOfOrNull { muxer -> + remoteProtocols.firstOrNull { remoteProtocol -> + muxer.protocolDescriptor.protocolMatcher.matches(remoteProtocol) + }?.let { negotiatedProtocol -> + NegotiatedStreamMuxer(muxer, negotiatedProtocol) + } + } + + private class ChannelSetup( private val localKey: PrivKey, - private val muxerIds: List, + private val muxers: List, private val certAlgorithm: String, private val ch: P2PChannel, private val handshakeComplete: CompletableFuture @@ -157,7 +172,17 @@ private class ChannelSetup( if (! activated) { activated = true val expectedRemotePeerId = ctx.channel().attr(REMOTE_PEER_ID).get() - ctx.channel().pipeline().addLast(buildTlsHandler(localKey, Optional.ofNullable(expectedRemotePeerId), muxerIds, certAlgorithm, ch, handshakeComplete, ctx)) + ctx.channel().pipeline().addLast( + buildTlsHandler( + localKey, + Optional.ofNullable(expectedRemotePeerId), + muxers, + certAlgorithm, + ch, + handshakeComplete, + ctx + ) + ) ctx.channel().pipeline().remove(SetupHandlerName) } } diff --git a/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt b/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt index 53d8dadd2..17b7d7c54 100644 --- a/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt +++ b/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt @@ -1,11 +1,10 @@ package io.libp2p.transport import io.libp2p.core.Connection -import io.libp2p.core.NoSuchLocalProtocolException import io.libp2p.core.multistream.MultistreamProtocol import io.libp2p.core.multistream.ProtocolBinding -import io.libp2p.core.multistream.ProtocolId import io.libp2p.core.mux.StreamMuxer +import io.libp2p.core.mux.NegotiatedStreamMuxer import io.libp2p.core.security.SecureChannel import io.libp2p.etc.getP2PChannel import io.libp2p.etc.types.forward @@ -38,19 +37,6 @@ open class ConnectionUpgrader( ) } - open fun establishMuxer(muxerId: ProtocolId, connection: Connection): CompletableFuture { - val muxer = muxers.find { m -> - m.protocolDescriptor.matchesAny(listOf( muxerId)) - } ?: throw NoSuchLocalProtocolException("Early Muxer negotiation selected unsupported muxer: $muxerId") - val res = CompletableFuture() - connection.pushHandler( - nettyInitializer { - muxer.initChannel(it.channel.getP2PChannel(), muxerId).forward(res) - } - ) - return res - } - private fun , R> establish( multistreamProtocol: MultistreamProtocol, connection: Connection, @@ -59,4 +45,16 @@ open class ConnectionUpgrader( val multistream = multistreamProtocol.createMultistream(bindings) return multistream.initChannel(connection) } // establish + + companion object { + fun establishMuxer(muxer: NegotiatedStreamMuxer, connection: Connection): CompletableFuture { + val res = CompletableFuture() + connection.pushHandler( + nettyInitializer { + muxer.initChannel(it.channel.getP2PChannel()).forward(res) + } + ) + return res + } + } } // ConnectionUpgrader diff --git a/libp2p/src/main/kotlin/io/libp2p/transport/implementation/ConnectionBuilder.kt b/libp2p/src/main/kotlin/io/libp2p/transport/implementation/ConnectionBuilder.kt index 73469be10..960c94f33 100644 --- a/libp2p/src/main/kotlin/io/libp2p/transport/implementation/ConnectionBuilder.kt +++ b/libp2p/src/main/kotlin/io/libp2p/transport/implementation/ConnectionBuilder.kt @@ -32,8 +32,8 @@ class ConnectionBuilder( upgrader.establishSecureChannel(connection) .thenCompose { connection.setSecureSession(it) - if (it.muxerProtocol != null) { - upgrader.establishMuxer(it.muxerProtocol, connection) + if (it.earlyMuxer != null) { + ConnectionUpgrader.establishMuxer(it.earlyMuxer, connection) } else { upgrader.establishMuxer(connection) } diff --git a/libp2p/src/test/kotlin/io/libp2p/security/CipherSecureChannelTest.kt b/libp2p/src/test/kotlin/io/libp2p/security/CipherSecureChannelTest.kt index 6362bc717..b466f04e6 100644 --- a/libp2p/src/test/kotlin/io/libp2p/security/CipherSecureChannelTest.kt +++ b/libp2p/src/test/kotlin/io/libp2p/security/CipherSecureChannelTest.kt @@ -3,6 +3,7 @@ package io.libp2p.security import io.libp2p.core.PeerId import io.libp2p.core.crypto.KEY_TYPE import io.libp2p.core.crypto.generateKeyPair +import io.libp2p.core.mux.StreamMuxer import io.libp2p.tools.TestChannel import io.libp2p.tools.TestLogAppender import io.netty.buffer.Unpooled @@ -12,8 +13,8 @@ import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.jupiter.api.Test import java.util.concurrent.TimeUnit.SECONDS -abstract class CipherSecureChannelTest(secureChannelCtor: SecureChannelCtor, muxerIds: List, announce: String) : - SecureChannelTestBase(secureChannelCtor, muxerIds, announce) { +abstract class CipherSecureChannelTest(secureChannelCtor: SecureChannelCtor, muxers: List, announce: String) : + SecureChannelTestBase(secureChannelCtor, muxers, announce) { @Test fun `incorrect initiator remote PeerId should throw`() { diff --git a/libp2p/src/test/kotlin/io/libp2p/security/SecureChannelTestBase.kt b/libp2p/src/test/kotlin/io/libp2p/security/SecureChannelTestBase.kt index 4dd736876..a91651a75 100644 --- a/libp2p/src/test/kotlin/io/libp2p/security/SecureChannelTestBase.kt +++ b/libp2p/src/test/kotlin/io/libp2p/security/SecureChannelTestBase.kt @@ -5,6 +5,7 @@ import io.libp2p.core.crypto.KEY_TYPE import io.libp2p.core.crypto.PrivKey import io.libp2p.core.crypto.generateKeyPair import io.libp2p.core.multistream.ProtocolMatcher +import io.libp2p.core.mux.StreamMuxer import io.libp2p.core.security.SecureChannel import io.libp2p.etc.types.seconds import io.libp2p.etc.types.toByteArray @@ -26,13 +27,13 @@ import java.nio.charset.StandardCharsets import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.TimeUnit -typealias SecureChannelCtor = (PrivKey, List) -> SecureChannel +typealias SecureChannelCtor = (PrivKey, List) -> SecureChannel val logger = LoggerFactory.getLogger(SecureChannelTestBase::class.java) abstract class SecureChannelTestBase( val secureChannelCtor: SecureChannelCtor, - val muxerIds: List, + val muxerIds: List, val announce: String ) { init { @@ -118,7 +119,7 @@ abstract class SecureChannelTestBase( } } // secureInterconnect - protected fun makeSelector(key: PrivKey, muxers: List) = ProtocolSelect(listOf(secureChannelCtor(key, muxers))) + protected fun makeSelector(key: PrivKey, muxers: List) = ProtocolSelect(listOf(secureChannelCtor(key, muxers))) protected fun makeDialChannel( name: String, diff --git a/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt b/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt index f32d6778b..1d5fe5ed5 100644 --- a/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt +++ b/libp2p/src/test/kotlin/io/libp2p/security/tls/TlsSecureChannelTest.kt @@ -19,7 +19,7 @@ val MultistreamProtocolV1: MultistreamProtocolDebug = MultistreamProtocolDebugV1 @Tag("secure-channel") class TlsSecureChannelTest : SecureChannelTestBase( ::TlsSecureChannel, - listOf(StreamMuxerProtocol.Yamux.createMuxer(MultistreamProtocolV1, listOf()).protocolDescriptor.announceProtocols.get(0)), + listOf(StreamMuxerProtocol.Yamux.createMuxer(MultistreamProtocolV1, listOf())), TlsSecureChannel.announce ) { @Test From 7390327815640d14613ff35d52410f0a746060f9 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 31 May 2023 15:57:12 +0400 Subject: [PATCH 34/37] Remove unused variable --- libp2p/src/main/kotlin/io/libp2p/multistream/ProtocolSelect.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/multistream/ProtocolSelect.kt b/libp2p/src/main/kotlin/io/libp2p/multistream/ProtocolSelect.kt index 434216fa0..332d8f2b6 100644 --- a/libp2p/src/main/kotlin/io/libp2p/multistream/ProtocolSelect.kt +++ b/libp2p/src/main/kotlin/io/libp2p/multistream/ProtocolSelect.kt @@ -23,7 +23,6 @@ class ProtocolSelect(val protocols: List() var activeFired = false - var userEvent = false override fun channelRead(ctx: ChannelHandlerContext, msg: Any) { // when protocol data immediately follows protocol id in the same packet @@ -43,7 +42,6 @@ class ProtocolSelect(val protocols: List { val protocolBinding = protocols.find { it.protocolDescriptor.protocolMatcher.matches(evt.proto) } From e74723abcd3679628c69446afbc2355b8318a174 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 31 May 2023 16:01:58 +0400 Subject: [PATCH 35/37] Remove accident star import optimization --- libp2p/src/main/kotlin/io/libp2p/core/mux/StreamMuxer.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/core/mux/StreamMuxer.kt b/libp2p/src/main/kotlin/io/libp2p/core/mux/StreamMuxer.kt index b753c44a0..4113e427e 100644 --- a/libp2p/src/main/kotlin/io/libp2p/core/mux/StreamMuxer.kt +++ b/libp2p/src/main/kotlin/io/libp2p/core/mux/StreamMuxer.kt @@ -1,6 +1,9 @@ package io.libp2p.core.mux -import io.libp2p.core.* +import io.libp2p.core.ChannelVisitor +import io.libp2p.core.Connection +import io.libp2p.core.StreamHandler +import io.libp2p.core.StreamPromise import io.libp2p.core.multistream.ProtocolBinding /** From 3071e674feb0c4e38e46149fc497070ce2b18a48 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 31 May 2023 16:12:44 +0400 Subject: [PATCH 36/37] Return back the "libp2p" entry for the TLS ALPN --- .../main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt index ae4fbf129..6a62a2323 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -56,7 +56,9 @@ import java.util.logging.Logger import javax.net.ssl.X509TrustManager private val log = Logger.getLogger(TlsSecureChannel::class.java.name) -private val SetupHandlerName = "TlsSetup" + +const val NoEarlyMuxerNegotiationEntry = "libp2p" +const val SetupHandlerName = "TlsSetup" val certificatePrefix = "libp2p-tls-handshake:".encodeToByteArray() class TlsSecureChannel(private val localKey: PrivKey, private val muxers: List, private val certAlgorithm: String) : @@ -115,7 +117,7 @@ fun buildTlsHandler( ApplicationProtocolConfig.Protocol.ALPN, ApplicationProtocolConfig.SelectorFailureBehavior.FATAL_ALERT, ApplicationProtocolConfig.SelectedListenerFailureBehavior.FATAL_ALERT, - muxers.allProtocols // early muxer negotiation + muxers.allProtocols + NoEarlyMuxerNegotiationEntry// early muxer negotiation ) ) .build() From 2ee67b0e5b60d27c6ddbaf2b3e9d6786e96ed292 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 31 May 2023 16:22:15 +0400 Subject: [PATCH 37/37] Formatting --- .../kotlin/io/libp2p/core/multistream/NegotiatedProtocol.kt | 2 +- .../kotlin/io/libp2p/core/multistream/ProtocolBinding.kt | 1 - .../main/kotlin/io/libp2p/core/mux/NegotiatedStreamMuxer.kt | 2 +- .../kotlin/io/libp2p/security/noise/NoiseXXSecureChannel.kt | 1 - .../io/libp2p/security/plaintext/PlaintextInsecureChannel.kt | 1 - .../kotlin/io/libp2p/security/secio/SecIoSecureChannel.kt | 1 - .../main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt | 5 ++--- .../main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt | 2 +- 8 files changed, 5 insertions(+), 10 deletions(-) diff --git a/libp2p/src/main/kotlin/io/libp2p/core/multistream/NegotiatedProtocol.kt b/libp2p/src/main/kotlin/io/libp2p/core/multistream/NegotiatedProtocol.kt index a1557b95c..e0b04d655 100644 --- a/libp2p/src/main/kotlin/io/libp2p/core/multistream/NegotiatedProtocol.kt +++ b/libp2p/src/main/kotlin/io/libp2p/core/multistream/NegotiatedProtocol.kt @@ -12,4 +12,4 @@ open class NegotiatedProtocol = binding.initChannel(ch, protocol) -} \ No newline at end of file +} diff --git a/libp2p/src/main/kotlin/io/libp2p/core/multistream/ProtocolBinding.kt b/libp2p/src/main/kotlin/io/libp2p/core/multistream/ProtocolBinding.kt index dd355c15a..6e0b1224a 100644 --- a/libp2p/src/main/kotlin/io/libp2p/core/multistream/ProtocolBinding.kt +++ b/libp2p/src/main/kotlin/io/libp2p/core/multistream/ProtocolBinding.kt @@ -75,4 +75,3 @@ interface ProtocolBinding { } } } - diff --git a/libp2p/src/main/kotlin/io/libp2p/core/mux/NegotiatedStreamMuxer.kt b/libp2p/src/main/kotlin/io/libp2p/core/mux/NegotiatedStreamMuxer.kt index 21a606828..4053b16df 100644 --- a/libp2p/src/main/kotlin/io/libp2p/core/mux/NegotiatedStreamMuxer.kt +++ b/libp2p/src/main/kotlin/io/libp2p/core/mux/NegotiatedStreamMuxer.kt @@ -2,4 +2,4 @@ package io.libp2p.core.mux import io.libp2p.core.multistream.NegotiatedProtocol -typealias NegotiatedStreamMuxer = NegotiatedProtocol \ No newline at end of file +typealias NegotiatedStreamMuxer = NegotiatedProtocol diff --git a/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseXXSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseXXSecureChannel.kt index 01e2a5c9e..9bb407ef6 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseXXSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseXXSecureChannel.kt @@ -11,7 +11,6 @@ import io.libp2p.core.crypto.PubKey import io.libp2p.core.crypto.marshalPublicKey import io.libp2p.core.crypto.unmarshalPublicKey import io.libp2p.core.multistream.ProtocolDescriptor -import io.libp2p.core.multistream.ProtocolId import io.libp2p.core.mux.StreamMuxer import io.libp2p.core.security.SecureChannel import io.libp2p.etc.REMOTE_PEER_ID diff --git a/libp2p/src/main/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannel.kt index c6c6dbd1f..a75c71ce3 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/plaintext/PlaintextInsecureChannel.kt @@ -9,7 +9,6 @@ import io.libp2p.core.crypto.PrivKey import io.libp2p.core.crypto.PubKey import io.libp2p.core.crypto.unmarshalPublicKey import io.libp2p.core.multistream.ProtocolDescriptor -import io.libp2p.core.multistream.ProtocolId import io.libp2p.core.mux.StreamMuxer import io.libp2p.core.security.SecureChannel import io.libp2p.etc.types.toProtobuf diff --git a/libp2p/src/main/kotlin/io/libp2p/security/secio/SecIoSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/secio/SecIoSecureChannel.kt index 18c93d068..ae049e9bf 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/secio/SecIoSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/secio/SecIoSecureChannel.kt @@ -5,7 +5,6 @@ import io.libp2p.core.P2PChannel import io.libp2p.core.PeerId import io.libp2p.core.crypto.PrivKey import io.libp2p.core.multistream.ProtocolDescriptor -import io.libp2p.core.multistream.ProtocolId import io.libp2p.core.mux.StreamMuxer import io.libp2p.core.security.SecureChannel import io.libp2p.etc.REMOTE_PEER_ID diff --git a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt index 6a62a2323..4d162fc9e 100644 --- a/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt +++ b/libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt @@ -8,8 +8,8 @@ import io.libp2p.core.crypto.unmarshalPublicKey import io.libp2p.core.multistream.ProtocolBinding import io.libp2p.core.multistream.ProtocolDescriptor import io.libp2p.core.multistream.ProtocolId -import io.libp2p.core.mux.StreamMuxer import io.libp2p.core.mux.NegotiatedStreamMuxer +import io.libp2p.core.mux.StreamMuxer import io.libp2p.core.security.SecureChannel import io.libp2p.crypto.Libp2pCrypto import io.libp2p.crypto.keys.EcdsaPublicKey @@ -117,7 +117,7 @@ fun buildTlsHandler( ApplicationProtocolConfig.Protocol.ALPN, ApplicationProtocolConfig.SelectorFailureBehavior.FATAL_ALERT, ApplicationProtocolConfig.SelectedListenerFailureBehavior.FATAL_ALERT, - muxers.allProtocols + NoEarlyMuxerNegotiationEntry// early muxer negotiation + muxers.allProtocols + NoEarlyMuxerNegotiationEntry // early muxer negotiation ) ) .build() @@ -160,7 +160,6 @@ private fun List.findBestMatch(remoteProtocols: List): } } - private class ChannelSetup( private val localKey: PrivKey, private val muxers: List, diff --git a/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt b/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt index 17b7d7c54..4df94b197 100644 --- a/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt +++ b/libp2p/src/main/kotlin/io/libp2p/transport/ConnectionUpgrader.kt @@ -3,8 +3,8 @@ package io.libp2p.transport import io.libp2p.core.Connection import io.libp2p.core.multistream.MultistreamProtocol import io.libp2p.core.multistream.ProtocolBinding -import io.libp2p.core.mux.StreamMuxer import io.libp2p.core.mux.NegotiatedStreamMuxer +import io.libp2p.core.mux.StreamMuxer import io.libp2p.core.security.SecureChannel import io.libp2p.etc.getP2PChannel import io.libp2p.etc.types.forward