Skip to content

Commit

Permalink
Add WIP auth support
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcoPolo committed Aug 2, 2023
1 parent 1113c1f commit 8a648d9
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 2 deletions.
62 changes: 62 additions & 0 deletions p2p/http/auth_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package libp2phttp

import (
"errors"
"fmt"
"net/http"

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

const AuthHandlerProtocolID = "/http-noise-auth/1.0.0"

type AuthHandler struct {
hostKey crypto.PrivKey
}

var _ http.Handler = (*AuthHandler)(nil)

// ServeHTTP implements http.Handler.
func (h *AuthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
clientID, err := AuthenticateClient(h.hostKey, w.Header(), r)

if err != nil || clientID == "" {
w.Header().Set("WWW-Authenticate", "Libp2p-Noise-IX")
w.WriteHeader(http.StatusUnauthorized)
return
}

w.WriteHeader(http.StatusOK)
}

// DoAuth sends an auth request over HTTP. The provided client should be
// namespaced to the AuthHandler protocol.
//
// Returns the server's peer id.
func DoAuth(client http.Client, clientKey crypto.PrivKey) (peer.ID, error) {
req, err := http.NewRequest("POST", "/", nil)
if err != nil {
return "", err
}
a, err := WithNoiseAuthentication(clientKey, req.Header)
if err != nil {
return "", err
}

resp, err := client.Do(req)
if err != nil {
return "", err
}

if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}

if resp.TLS == nil {
return "", errors.New("expected TLS connection")
}

expectedHost := resp.TLS.ServerName
return a.AuthenticateServer(expectedHost, resp.Header)
}
128 changes: 126 additions & 2 deletions p2p/http/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,27 @@ package libp2phttp

import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"errors"
"fmt"
"io"
"math/big"
"net"
"net/http"
"testing"
"time"

"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/test"
"github.com/multiformats/go-multiaddr"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -54,7 +66,8 @@ func TestServerHandlerRequiresAuth(t *testing.T) {
require.NoError(t, err)

// Start the server
server := New()
server, err := New()
require.NoError(t, err)
l, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
defer l.Close()
Expand Down Expand Up @@ -143,4 +156,115 @@ func TestServerHandlerRequiresAuth(t *testing.T) {
}
}

// TODO test with TLS + sni
func TestSNIIsUsed(t *testing.T) {
serverKey, _, err := test.RandTestKeyPair(crypto.Ed25519, 256)
require.NoError(t, err)
serverID, err := peer.IDFromPrivateKey(serverKey)
require.NoError(t, err)

serverHTTPHost, err := New()
require.NoError(t, err)

l, err := net.Listen("tcp", "127.0.0.1:0")
serverHTTPHost.SetHttpHandler(AuthHandlerProtocolID, &AuthHandler{serverKey})
require.NoError(t, err)
defer l.Close()
tlsConf := getTLSConf(t, net.IPv4(127, 0, 0, 1), time.Now(), time.Now().Add(time.Hour), "example.com")
go serverHTTPHost.ServeTLS(l, tlsConf)

serverMaSNI, err := manet.FromNetAddr(l.Addr())
require.NoError(t, err)
serverMaWrongSNI := ma.Join(serverMaSNI, multiaddr.StringCast("/tls/sni/wrong.com/http"))
serverMaSNI = ma.Join(serverMaSNI, multiaddr.StringCast("/tls/sni/example.com/http"))
serverMaDNS := multiaddr.StringCast("/dns4/example.com/tcp/443/tls/http")

testCases := []struct {
name string
ma ma.Multiaddr
shouldFail bool
}{
{
name: "With sni: " + serverMaSNI.String(),
ma: serverMaSNI,
shouldFail: false,
},
{
name: "With wrong sni: " + serverMaWrongSNI.String(),
ma: serverMaWrongSNI,
shouldFail: true,
},
{
name: "With dns: " + serverMaDNS.String(),
ma: serverMaDNS,
shouldFail: false,
},
}

for _, tc := range testCases {
t.Run("With multiaddr "+tc.name, func(t *testing.T) {
clientHttpHost, err := New(WithTLSClientConfig(&tls.Config{InsecureSkipVerify: true}))
require.NoError(t, err)

tr := clientHttpHost.httpRoundTripper.Clone()
// Resolve example.com to the server's IP in our test
clientHttpHost.httpRoundTripper.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
// Returns our listener's addr
return tr.DialContext(ctx, l.Addr().Network(), l.Addr().String())
}
client, err := clientHttpHost.NamespacedClient(nil, AuthHandlerProtocolID, peer.AddrInfo{ID: serverID, Addrs: []multiaddr.Multiaddr{tc.ma}})
if tc.shouldFail {
require.Error(t, err)
} else {

require.NoError(t, err)
}

clientKey, _, err := test.RandTestKeyPair(crypto.Ed25519, 256)
require.NoError(t, err)

observedServerID, err := DoAuth(client, clientKey)
if tc.shouldFail {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, serverID, observedServerID)
}
})
}
}

func getTLSConf(t *testing.T, ip net.IP, start, end time.Time, expectSNI string) *tls.Config {
t.Helper()
certTempl := &x509.Certificate{
DNSNames: []string{expectSNI},
SerialNumber: big.NewInt(1234),
Subject: pkix.Name{Organization: []string{"https-test"}},
NotBefore: start,
NotAfter: end,
IsCA: true,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
}
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
caBytes, err := x509.CreateCertificate(rand.Reader, certTempl, certTempl, &priv.PublicKey, priv)
require.NoError(t, err)
cert, err := x509.ParseCertificate(caBytes)
require.NoError(t, err)
var c *tls.Config
c = &tls.Config{
Certificates: []tls.Certificate{{
Certificate: [][]byte{cert.Raw},
PrivateKey: priv,
Leaf: cert,
}},
GetConfigForClient: func(hello *tls.ClientHelloInfo) (*tls.Config, error) {
if hello.ServerName != expectSNI {
return nil, errors.New("unexpected SNI")
}
return c, nil
},
}
return c
}

0 comments on commit 8a648d9

Please sign in to comment.