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

TLS x509 for Websocket #145

Closed
wants to merge 3 commits into from
Closed
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ runtime/
*.env
__debug*

*.exe
*.exe
.b7s_*
44 changes: 42 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ build-manager:
cd cmd/manager && go build -o ../../dist/b7s-manager
@echo "\n✅ Done.\n"


.PHONY: clean
clean:
@echo "\n🧹 Cleaning...\n"
Expand All @@ -37,6 +36,47 @@ clean:
setup:
@echo "\n📥 Downloading and extracting runtime...\n"
mkdir -p /tmp/runtime
wget -O /tmp/blockless-runtime.tar.gz https://github.com/blocklessnetwork/runtime/releases/download/v0.3.1/blockless-runtime.linux-latest.x86_64.tar.gz
@UNAME_S=$$(uname -s); \
UNAME_M=$$(uname -m); \
if [ "$$UNAME_S" = "Darwin" -a "$$UNAME_M" = "arm64" ]; then \
echo "Detected MacOS (arm64). Downloading appropriate version..."; \
wget -O /tmp/blockless-runtime.tar.gz https://github.com/blocklessnetwork/runtime/releases/download/v0.3.2/blockless-runtime.macos-latest.aarch64.tar.gz; \
elif [ "$$UNAME_S" = "Darwin" -a "$$UNAME_M" = "x86_64" ]; then \
echo "Detected MacOS (x86_64). Downloading appropriate version..."; \
wget -O /tmp/blockless-runtime.tar.gz https://github.com/blocklessnetwork/runtime/releases/download/v0.3.2/blockless-runtime.macos-latest.x86_64.tar.gz; \
elif [ "$$UNAME_S" = "Linux" -a "$$UNAME_M" = "arm64" ]; then \
echo "Detected Linux (arm64). Downloading appropriate version..."; \
wget -O /tmp/blockless-runtime.tar.gz https://github.com/blocklessnetwork/runtime/releases/download/v0.3.2/blockless-runtime.linux-latest.arm64.tar.gz; \
elif [ "$$UNAME_S" = "Linux" -a "$$UNAME_M" = "x86_64" ]; then \
echo "Detected Linux (x86_64). Downloading appropriate version..."; \
wget -O /tmp/blockless-runtime.tar.gz https://github.com/blocklessnetwork/runtime/releases/download/v0.3.2/blockless-runtime.linux-latest.x86_64.tar.gz; \
else \
echo "No compatible runtime found. Please check your OS and architecture."; \
fi
tar -xzf /tmp/blockless-runtime.tar.gz -C /tmp/runtime
@echo "\n✅ Done.\n"


.PHONY: run-head
run-head:
@echo "\n🚀 Launching Head Node...\n"
./dist/b7s \
--log-level debug \
--port 9527 \
--role head \
--private-key ./configs/testkeys/ident1/priv.bin \
--rest-api :8081
@echo "\n✅ Head Node is running!\n"

.PHONY: run-worker
run-worker:
@echo "\n🚀 Launching Worker Node...\n"
./dist/b7s \
--log-level debug \
--port 0 \
--role worker \
--runtime-path /tmp/runtime \
--runtime-cli bls-runtime \
--private-key ./configs/testkeys/ident2/priv.bin \
--boot-nodes /ip4/0.0.0.0/tcp/9527/p2p/12D3KooWH9GerdSEroL2nqjpd2GuE5dwmqNi7uHX7FoywBdKcP4q
@echo "\n✅ Worker Node is running!\n"
9 changes: 9 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ require (
require (
github.com/armon/go-metrics v0.4.1 // indirect
github.com/boltdb/bolt v1.3.1 // indirect
github.com/btcsuite/btcd v0.22.1 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.1.3 // indirect
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
github.com/cilium/ebpf v0.12.3 // indirect
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
Expand All @@ -62,10 +65,14 @@ require (
github.com/knadh/koanf/maps v0.1.1 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/libp2p/go-libp2p-consensus v0.0.1 // indirect
github.com/libp2p/go-libp2p-core v0.20.1 // indirect
github.com/libp2p/go-libp2p-gostream v0.6.0 // indirect
github.com/libp2p/go-libp2p-routing-helpers v0.7.3 // indirect
github.com/libp2p/go-openssl v0.1.0 // indirect
github.com/libp2p/go-ws-transport v0.7.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-pointer v0.0.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
Expand All @@ -76,6 +83,7 @@ require (
github.com/quic-go/quic-go v0.42.0 // indirect
github.com/quic-go/webtransport-go v0.7.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
Expand Down Expand Up @@ -137,6 +145,7 @@ require (
github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect
github.com/libp2p/go-libp2p-kbucket v0.6.3 // indirect
github.com/libp2p/go-libp2p-record v0.2.0 // indirect
github.com/libp2p/go-libp2p-tls v0.5.0
github.com/libp2p/go-msgio v0.3.0 // indirect
github.com/libp2p/go-nat v0.2.0 // indirect
github.com/libp2p/go-netroute v0.2.1 // indirect
Expand Down
20 changes: 20 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ github.com/blocklessnetwork/b7s-attributes v0.0.0/go.mod h1:0c+ZemB4kfylI14IERH4
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c=
github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y=
github.com/btcsuite/btcd/btcec/v2 v2.1.3 h1:xM/n3yIhHAhHy04z4i43C8p4ehixJZMsnrVJkgl+MTE=
github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/cavaliergopher/grab/v3 v3.0.1 h1:4z7TkBfmPjmLAAmkkAZNX/6QJ1nNFdv3SdIHXju0Fr4=
github.com/cavaliergopher/grab/v3 v3.0.1/go.mod h1:1U/KNnD+Ft6JJiYoYBAimKH2XrYptb8Kl3DFGmsjpq4=
Expand Down Expand Up @@ -323,6 +329,10 @@ github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl9
github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8=
github.com/libp2p/go-libp2p-consensus v0.0.1 h1:jcVbHRZLwTXU9iT/mPi+Lx4/OrIzq3bU1TbZNhYFCV8=
github.com/libp2p/go-libp2p-consensus v0.0.1/go.mod h1:+9Wrfhc5QOqWB0gXI0m6ARlkHfdJpcFXmRU0WoHz4Mo=
github.com/libp2p/go-libp2p-core v0.16.1 h1:bWoiEBqVkpJ13hbv/f69tHODp86t6mvc4fBN4DkK73M=
github.com/libp2p/go-libp2p-core v0.16.1/go.mod h1:O3i/7y+LqUb0N+qhzXjBjjpchgptWAVMG1Voegk7b4c=
github.com/libp2p/go-libp2p-core v0.20.1 h1:fQz4BJyIFmSZAiTbKV8qoYhEH5Dtv/cVhZbG3Ib/+Cw=
github.com/libp2p/go-libp2p-core v0.20.1/go.mod h1:6zR8H7CvQWgYLsbG4on6oLNSGcyKaYFSEYyDt51+bIY=
github.com/libp2p/go-libp2p-gostream v0.6.0 h1:QfAiWeQRce6pqnYfmIVWJFXNdDyfiR/qkCnjyaZUPYU=
github.com/libp2p/go-libp2p-gostream v0.6.0/go.mod h1:Nywu0gYZwfj7Jc91PQvbGU8dIpqbQQkjWgDuOrFaRdA=
github.com/libp2p/go-libp2p-kad-dht v0.25.2 h1:FOIk9gHoe4YRWXTu8SY9Z1d0RILol0TrtApsMDPjAVQ=
Expand All @@ -339,14 +349,20 @@ github.com/libp2p/go-libp2p-routing-helpers v0.7.3 h1:u1LGzAMVRK9Nqq5aYDVOiq/HaB
github.com/libp2p/go-libp2p-routing-helpers v0.7.3/go.mod h1:cN4mJAD/7zfPKXBcs9ze31JGYAZgzdABEm+q/hkswb8=
github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=
github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg=
github.com/libp2p/go-libp2p-tls v0.5.0 h1:aRNTeOI8Ljm1r4L2uMGxkMsVnyZoPwaqQqMw23qAsQs=
github.com/libp2p/go-libp2p-tls v0.5.0/go.mod h1:1a4tq0xQSZ0kAkDkZVAppuP3SAIUHcnzi2djJ/2EN4I=
github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0=
github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM=
github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk=
github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk=
github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU=
github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ=
github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo=
github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc=
github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s=
github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU=
github.com/libp2p/go-ws-transport v0.7.0 h1:TJYvBgWpPYAE5sN3V5y0cu7ivxGxzwvF/FhyYFnzkNs=
github.com/libp2p/go-ws-transport v0.7.0/go.mod h1:ZgC5TYCKUIUJTjgd5Mb17Tu3K0vFYB409nzRYGaJu0I=
github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ=
github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
Expand All @@ -365,6 +381,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0=
github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
Expand Down Expand Up @@ -528,6 +546,8 @@ github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hg
github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU=
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
Expand Down
78 changes: 78 additions & 0 deletions host/cert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package host

import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"math/big"
"time"

libp2pcrypto "github.com/libp2p/go-libp2p/core/crypto"
)

// Convert a libp2p PrivKey to a crypto.PrivateKey
func convertLibp2pPrivKeyToCryptoPrivKey(privKey libp2pcrypto.PrivKey) (crypto.PrivateKey, error) {
rawKey, err := privKey.Raw()
if err != nil {
return nil, err
}

switch privKey.Type() {
case libp2pcrypto.RSA:
return x509.ParsePKCS1PrivateKey(rawKey)
case libp2pcrypto.ECDSA:
return x509.ParseECPrivateKey(rawKey)
case libp2pcrypto.Ed25519:
return ed25519.PrivateKey(rawKey), nil
default:
return nil, fmt.Errorf("unsupported key type for X.509 conversion")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be useful to know which key it was:

Suggested change
return nil, fmt.Errorf("unsupported key type for X.509 conversion")
return nil, fmt.Errorf("unsupported key type for X.509 conversion: %s", privKey.Type())

}
}

func generateX509Certificate(privKey crypto.PrivateKey) (tls.Certificate, error) {
// Define certificate template
template := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
Organization: []string{"b7s"},
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(365 * 24 * time.Hour), // 1 year validity
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps for visibility we define this in params.go:

var (
	DefaultCertificateDuration = 365 * 24 * time.Hour // 1 year
)

Then here we do:

Suggested change
NotAfter: time.Now().Add(365 * 24 * time.Hour), // 1 year validity
NotAfter: time.Now().Add(DefaultCertificateDuration),

I'd do the same for Organization.

KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
}

pubKey := publicKey(privKey)

// Create the certificate
derBytes, err := x509.CreateCertificate(rand.Reader, template, template, pubKey, privKey)
if err != nil {
return tls.Certificate{}, err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we wrap the error here? Especially if we change the above to return public key and an error - we'd have two paths that return an error - it's useful to have context

}

// Encode the certificate and private key
cert := tls.Certificate{
Certificate: [][]byte{derBytes},
PrivateKey: privKey,
}

return cert, nil
}

func publicKey(priv crypto.PrivateKey) crypto.PublicKey {
switch key := priv.(type) {
case *rsa.PrivateKey:
return &key.PublicKey
case *ecdsa.PrivateKey:
return &key.PublicKey
case ed25519.PrivateKey:
return key.Public().(ed25519.PublicKey)
default:
panic("unsupported key type")
}
Comment on lines +67 to +77
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to have this return crypto.PublicKey and an error than have this panic.

Instead in default I'd do return nil, fmt.Errorf("unsupported key type: %T", key)

}
35 changes: 35 additions & 0 deletions host/cert_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package host

import (
"crypto/rand"
"testing"

libp2pcrypto "github.com/libp2p/go-libp2p/core/crypto"
"github.com/stretchr/testify/assert"
)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd change these asserts to requires. That way it's consistent with the codebase and it'll rarely be useful to continue the test after the first error.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to have tests for other types of keys too - but I can add that in a separate PR.

func TestConvertLibp2pPrivKeyToCryptoPrivKey(t *testing.T) {
// Generate a libp2p ECDSA key pair for testing
priv, _, err := libp2pcrypto.GenerateECDSAKeyPair(rand.Reader)
assert.NoError(t, err, "failed to generate libp2p ECDSA key pair")

// Convert the libp2p private key to a crypto.PrivateKey
cryptoPriv, err := convertLibp2pPrivKeyToCryptoPrivKey(priv)
assert.NoError(t, err, "failed to convert libp2p private key to crypto private key")
assert.NotNil(t, cryptoPriv, "converted crypto private key should not be nil")
}

func TestGenerateX509Certificate(t *testing.T) {
// Generate a libp2p ECDSA key pair for testing
priv, _, err := libp2pcrypto.GenerateECDSAKeyPair(rand.Reader)
assert.NoError(t, err, "failed to generate libp2p ECDSA key pair")

// Convert the libp2p private key to a crypto.PrivateKey
cryptoPriv, err := convertLibp2pPrivKeyToCryptoPrivKey(priv)
assert.NoError(t, err, "failed to convert libp2p private key")

// Generate an X.509 certificate
cert, err := generateX509Certificate(cryptoPriv)
assert.NoError(t, err, "failed to generate X.509 certificate")
assert.NotEmpty(t, cert.Certificate, "certificate should contain at least one DER encoded block")
}
66 changes: 49 additions & 17 deletions host/host.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package host

import (
"crypto/tls"
"errors"
"fmt"
"os"
Expand All @@ -12,6 +13,10 @@ import (
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/host"
quic "github.com/libp2p/go-libp2p/p2p/transport/quic"
"github.com/libp2p/go-libp2p/p2p/transport/tcp"
ws "github.com/libp2p/go-libp2p/p2p/transport/websocket"
webtransport "github.com/libp2p/go-libp2p/p2p/transport/webtransport"
ma "github.com/multiformats/go-multiaddr"
)

Expand All @@ -27,46 +32,73 @@ type Host struct {

// New creates a new Host.
func New(log zerolog.Logger, address string, port uint, options ...func(*Config)) (*Host, error) {

cfg := defaultConfig
for _, option := range options {
option(&cfg)
}

hostAddress := fmt.Sprintf("/ip4/%v/tcp/%v", address, port)
addresses := []string{
hostAddress,
}

if cfg.Websocket {

// If the TCP and websocket port are explicitly chosen and set to the same value, one of the two listens will silently fail.
if port == cfg.WebsocketPort && cfg.WebsocketPort != 0 {
return nil, fmt.Errorf("TCP and websocket ports cannot be the same (TCP: %v, Websocket: %v)", port, cfg.WebsocketPort)
}
addresses := []string{hostAddress}

wsAddr := fmt.Sprintf("/ip4/%v/tcp/%v/ws", address, cfg.WebsocketPort)
addresses = append(addresses, wsAddr)
}
// define a subset of the default transports, so that we can offer a x509 certificate for the websocket transport
DefaultTransports := libp2p.ChainOptions(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
DefaultTransports := libp2p.ChainOptions(
transports := libp2p.ChainOptions(

libp2p.Transport(tcp.NewTCPTransport),
libp2p.Transport(quic.NewTransport),
libp2p.Transport(webtransport.New),
)

opts := []libp2p.Option{
libp2p.ListenAddrStrings(addresses...),
libp2p.DefaultTransports,
DefaultTransports,
libp2p.DefaultMuxers,
libp2p.DefaultSecurity,
libp2p.NATPortMap(),
}

// Read private key, if provided.
var key crypto.PrivKey
var err error

if cfg.PrivateKey != "" {
key, err := readPrivateKey(cfg.PrivateKey)
key, err = readPrivateKey(cfg.PrivateKey)
if err != nil {
return nil, fmt.Errorf("could not read private key: %w", err)
}

opts = append(opts, libp2p.Identity(key))
}

var tlsConfig *tls.Config
if cfg.Websocket {

// If the TCP and websocket port are explicitly chosen and set to the same value, one of the two listens will silently fail.
if port == cfg.WebsocketPort && cfg.WebsocketPort != 0 {
return nil, fmt.Errorf("TCP and websocket ports cannot be the same (TCP: %v, Websocket: %v)", port, cfg.WebsocketPort)
}

// Convert libp2p private key to crypto.PrivateKey
cryptoPrivKey, err := convertLibp2pPrivKeyToCryptoPrivKey(key)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This here line relies on us having a key. However the key is optional - we could generate a libp2p identity on the fly.

If we start the node with node --websocket true (without --private-key) - this will blow up.

What I'm thinking:

  1. We change how private key config works for the host. Instead of this:
// WithPrivateKey specifies the private key for the Host.
func WithPrivateKey(filepath string) func(*Config) 

we do:

// WithPrivateKey specifies the private key for the Host.
func WithPrivateKey(key crypto.PrivKey) func(*Config)
  1. We make the key a hard requirement - host.New fails if it does not have a key.
  2. In the main() function of the node, we take care of a) reading the key from disk - if the private key is specified, or b) we generate an in-memory key that we will use for the current invocation. In either case we pass the key to the host. Then we can rely on having the key.

What do you think?

if err != nil {
return nil, fmt.Errorf("failed to convert libp2p private key: %v", err)
}

// Generate the X.509 certificate
tlsCert, err := generateX509Certificate(cryptoPrivKey)
if err != nil {
return nil, fmt.Errorf("failed to generate TLS certificate: %v", err)
}

tlsConfig = &tls.Config{
Certificates: []tls.Certificate{tlsCert},
MinVersion: tls.VersionTLS12,
}

wsAddr := fmt.Sprintf("/ip4/%v/tcp/%v/wss", address, cfg.WebsocketPort)
addresses = append(addresses, wsAddr)
opts = append(opts, libp2p.Transport(ws.New, ws.WithTLSConfig(tlsConfig)))
}

opts = append(opts, libp2p.ListenAddrStrings(addresses...))

if cfg.DialBackAddress != "" && cfg.DialBackPort != 0 {

protocol, dialbackAddress, err := determineAddressProtocol(cfg.DialBackAddress)
Expand Down
Loading