Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add quic support #403

Draft
wants to merge 14 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ jobs:
with:
distribution: temurin
java-version: 11
- name: Install and run ipfs
run: ./install-run-ipfs.sh

- name: Setup Gradle
uses: gradle/gradle-build-action@v2
Expand Down
6 changes: 6 additions & 0 deletions install-run-ipfs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#! /bin/sh
wget https://dist.ipfs.io/kubo/v0.18.1/kubo_v0.18.1_linux-amd64.tar.gz -O /tmp/kubo_linux-amd64.tar.gz
tar -xvf /tmp/kubo_linux-amd64.tar.gz
export PATH=$PATH:$PWD/kubo/
ipfs init
ipfs daemon --routing=dhtserver &
8 changes: 8 additions & 0 deletions libp2p/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ dependencies {
api("io.netty:netty-transport")
implementation("io.netty:netty-handler")
implementation("io.netty:netty-codec-http")
implementation("io.netty:netty-transport-classes-epoll:4.1.90.Final")
implementation("io.netty.incubator:netty-incubator-codec-native-quic")

api("com.google.protobuf:protobuf-java")

Expand All @@ -26,6 +28,12 @@ dependencies {

testImplementation(project(":tools:schedulers"))

testImplementation("io.netty.incubator:netty-incubator-codec-native-quic::linux-x86_64")
testImplementation("io.netty.incubator:netty-incubator-codec-native-quic::linux-aarch_64")
testImplementation("io.netty.incubator:netty-incubator-codec-native-quic::osx-x86_64")
testImplementation("io.netty.incubator:netty-incubator-codec-native-quic::osx-aarch_64")
testImplementation("io.netty.incubator:netty-incubator-codec-native-quic::windows-x86_64")

testFixturesApi("org.apache.logging.log4j:log4j-core")
testFixturesImplementation(project(":tools:schedulers"))
testFixturesImplementation("io.netty:netty-transport-classes-epoll")
Expand Down
24 changes: 21 additions & 3 deletions libp2p/src/main/java/io/libp2p/core/dsl/HostBuilder.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.libp2p.core.dsl;

import io.libp2p.core.Host;
import io.libp2p.core.crypto.KeyType;
import io.libp2p.core.crypto.PrivKey;
import io.libp2p.core.multistream.ProtocolBinding;
import io.libp2p.core.mux.*;
Expand Down Expand Up @@ -39,6 +40,13 @@ public final HostBuilder transport(Function<ConnectionUpgrader, Transport>... tr
return this;
}

@SafeVarargs
public final HostBuilder secureTransport(
BiFunction<PrivKey, List<ProtocolBinding<?>>, Transport>... transports) {
secureTransports_.addAll(Arrays.asList(transports));
return this;
}

@SafeVarargs
public final HostBuilder secureChannel(
BiFunction<PrivKey, List<StreamMuxer>, SecureChannel>... secureChannels) {
Expand Down Expand Up @@ -72,9 +80,17 @@ public Host build() {
return BuilderJKt.hostJ(
defaultMode_.asBuilderDefault(),
b -> {
b.getIdentity().random();
IdentityBuilder identity = b.getIdentity();
identity.random(KeyType.ED25519);
PrivKey peerId = identity.getFactory().invoke();
identity.setFactory(() -> peerId);

transports_.forEach(t -> b.getTransports().add(t::apply));
secureTransports_.forEach(t ->
b.getTransports().add(c ->
t.apply(identity.getFactory().invoke(), protocols_))
);
transports_.forEach(t ->
b.getTransports().add(t::apply));
secureChannels_.forEach(
sc -> b.getSecureChannels().add((k, m) -> sc.apply(k, (List<StreamMuxer>) m)));
muxers_.forEach(m -> b.getMuxers().add(m.get()));
Expand All @@ -85,7 +101,9 @@ public Host build() {
} // build

private DefaultMode defaultMode_;
private List<Function<ConnectionUpgrader, Transport>> transports_ = new ArrayList<>();
private List<BiFunction<PrivKey, List<ProtocolBinding<?>>, Transport>> secureTransports_ = new ArrayList<>();

private List<Function<ConnectionUpgrader, Transport>> transports_ = new ArrayList<>();
private List<BiFunction<PrivKey, List<StreamMuxer>, SecureChannel>> secureChannels_ =
new ArrayList<>();
private List<Supplier<StreamMuxerProtocol>> muxers_ = new ArrayList<>();
Expand Down
40 changes: 24 additions & 16 deletions libp2p/src/main/kotlin/io/libp2p/security/tls/TLSSecureChannel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,14 @@ fun buildTlsHandler(
expectedRemotePeer: Optional<PeerId>,
muxers: List<StreamMuxer>,
certAlgorithm: String,
ch: P2PChannel,
isInitiator: Boolean,
handshakeComplete: CompletableFuture<SecureChannel.Session>,
ctx: ChannelHandlerContext
): SslHandler {
val connectionKeys = if (certAlgorithm.equals("ECDSA")) generateEcdsaKeyPair() else generateEd25519KeyPair()
val javaPrivateKey = getJavaKey(connectionKeys.first)
val sslContext = (
if (ch.isInitiator) {
if (isInitiator) {
SslContextBuilder.forClient().keyManager(javaPrivateKey, listOf(buildCert(localKey, connectionKeys.first)))
} else {
SslContextBuilder.forServer(javaPrivateKey, listOf(buildCert(localKey, connectionKeys.first)))
Expand All @@ -130,7 +130,6 @@ fun buildTlsHandler(
)
.build()
val handler = sslContext.newHandler(ctx.alloc())
handler.sslCloseFuture().addListener { _ -> ctx.close() }
val handshake = handler.handshakeFuture()
val engine = handler.engine()
handshake.addListener { fut ->
Expand Down Expand Up @@ -180,17 +179,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),
muxers,
certAlgorithm,
ch,
handshakeComplete,
ctx
)
val handler = buildTlsHandler(
localKey,
Optional.ofNullable(expectedRemotePeerId),
muxers,
certAlgorithm,
ch.isInitiator,
handshakeComplete,
ctx
)
ctx.channel().pipeline().addLast(handler)
handler.sslCloseFuture().addListener { _ -> ctx.close() }
ctx.channel().pipeline().remove(SetupHandlerName)
}
}
Expand All @@ -215,18 +214,26 @@ private class ChannelSetup(
}

class Libp2pTrustManager(private val expectedRemotePeer: Optional<PeerId>) : X509TrustManager {
var remoteCert: Certificate?

init {
remoteCert = null
}
override fun checkClientTrusted(certs: Array<out X509Certificate>?, authType: String?) {
if (certs?.size != 1) {
throw CertificateException()
}
val claimedPeerId = verifyAndExtractPeerId(arrayOf(certs.get(0)))
val cert = certs.get(0)
remoteCert = cert
val claimedPeerId = verifyAndExtractPeerId(arrayOf(cert))
if (expectedRemotePeer.map { ex -> !ex.equals(claimedPeerId) }.orElse(false)) {
throw InvalidRemotePubKey()
}
}

override fun checkServerTrusted(certs: Array<out X509Certificate>?, authType: String?) {
return checkClientTrusted(certs, authType)
println("Checking server cert...")
checkClientTrusted(certs, authType)
}

override fun getAcceptedIssuers(): Array<X509Certificate> {
Expand Down Expand Up @@ -298,7 +305,8 @@ fun verifyAndExtractPeerId(chain: Array<Certificate>): PeerId {
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)) {
val pubKeyAsn1 = bcCert.subjectPublicKeyInfo.encoded
if (!pubKey.verify(certificatePrefix.plus(pubKeyAsn1), signature)) {
throw IllegalStateException("Invalid signature on TLS certificate extension!")
}

Expand Down
Loading
Loading