Files
mesh-host/internal/link/pinned_test.go
T
jschoubben 4f1e92160a
mesh/delivery merged: source.main moved to novox/mesh-host@89475bb0cfec
Round 1: the pin tests merged (test budget), KVWatch's doc says what folds
The review of fe9533c1: a new test function raised a count already over the
budget, so two broker-pin tests are one; KVWatch no longer yields every
change, and its doc says so (issue 1966).

Claude-Session: https://claude.ai/code/session_01NcbgfFfQ4Z9UE5jK32PmE5
2026-11-08 00:38:36 +01:00

164 lines
4.9 KiB
Go

package link
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"errors"
"math/big"
"net"
"strings"
"testing"
"time"
)
// A real TLS server with a real self-signed certificate. Not a fake: what is being tested is that
// Go's TLS stack calls this verification before the handshake completes and that a wrong
// certificate is refused there — a fake would assert that the fake refuses it
// (novox/hq ADR 0017).
func server(t *testing.T) (address string, fingerprint string) {
t.Helper()
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
template := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: "mesh-broker"},
NotBefore: time.Now().Add(-time.Hour),
NotAfter: time.Now().Add(time.Hour),
}
der, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
if err != nil {
t.Fatal(err)
}
listener, err := tls.Listen("tcp", "127.0.0.1:0", &tls.Config{
Certificates: []tls.Certificate{{Certificate: [][]byte{der}, PrivateKey: key}},
MinVersion: tls.VersionTLS12,
})
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { listener.Close() })
go func() {
for {
conn, err := listener.Accept()
if err != nil {
return
}
go func() {
// Complete the handshake, then close. Enough for a client to have checked.
_ = conn.(*tls.Conn).Handshake()
conn.Close()
}()
}
}()
return listener.Addr().String(), Fingerprint(der)
}
// The right broker is accepted, a different one refused, and a malformed pin is caught before connecting, rather than at
// the handshake, so a mistyped token fails while a person is looking at it (three tests merged).
func TestTheRightBrokerIsAcceptedAndAMalformedPinRefused(t *testing.T) {
address, pin := server(t)
conn, err := dialPinned(address, pin, 5*time.Second)
if err != nil {
t.Fatalf("the broker its token describes was refused: %v", err)
}
conn.Close()
for _, bad := range []string{"", "sha256:short", strings.Repeat("a", 64),
"sha256:" + strings.Repeat("z", 64), "md5:" + strings.Repeat("a", 64)} {
if _, err := PinnedConfig(bad); err == nil {
t.Errorf("%q was accepted as a fingerprint", bad)
}
}
// The case the pin exists for: something else answering at that address. Since the host
// applies whatever the link delivers, connecting to the wrong mesh is the whole machine.
_, other := server(t)
_, err = dialPinned(address, other, 5*time.Second)
if err == nil {
t.Fatal("a broker presenting a different certificate was accepted")
}
if !errors.Is(err, ErrWrongCertificate) {
t.Fatalf("refused, but not as a wrong certificate: %v", err)
}
if !strings.Contains(err.Error(), "retrying will not help") {
t.Error("the error reads like a connection problem; this one must not be retried")
}
}
func TestNothingIsSentToTheWrongBroker(t *testing.T) {
// ADR 0004 requires the check to happen *before* anything is sent. Asserted by counting what
// the wrong server received: a handshake, and no application bytes.
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
template := x509.Certificate{
SerialNumber: big.NewInt(2),
Subject: pkix.Name{CommonName: "impostor"},
NotBefore: time.Now().Add(-time.Hour),
NotAfter: time.Now().Add(time.Hour),
}
der, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
if err != nil {
t.Fatal(err)
}
listener, err := tls.Listen("tcp", "127.0.0.1:0", &tls.Config{
Certificates: []tls.Certificate{{Certificate: [][]byte{der}, PrivateKey: key}},
MinVersion: tls.VersionTLS12,
})
if err != nil {
t.Fatal(err)
}
defer listener.Close()
received := make(chan int, 1)
go func() {
conn, err := listener.Accept()
if err != nil {
received <- -1
return
}
defer conn.Close()
_ = conn.SetReadDeadline(time.Now().Add(2 * time.Second))
buf := make([]byte, 512)
n, _ := conn.Read(buf)
received <- n
}()
// A pin for a certificate this server does not have.
_, elsewhere := server(t)
if _, err := dialPinned(listener.Addr().String(), elsewhere, 5*time.Second); err == nil {
t.Fatal("the impostor was accepted")
}
if n := <-received; n > 0 {
t.Errorf("%d application byte(s) reached a broker that failed the pin", n)
}
}
func TestAnUnreachableBrokerIsAnOrdinaryFailure(t *testing.T) {
// Must not read as a wrong certificate: one is a network problem worth retrying, the other
// means the mesh was substituted.
_, pin := server(t)
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
address := listener.Addr().String()
listener.Close()
_, err = dialPinned(address, pin, 2*time.Second)
if err == nil {
t.Fatal("dialling a closed port succeeded")
}
if errors.Is(err, ErrWrongCertificate) {
t.Error("an unreachable broker was reported as presenting the wrong certificate")
}
}