Separate the window sentence from the wrapped error so a bad certificate file is not reported as a key mismatch.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-07 15:01:38 +02:00
co-authored by Cursor
parent a405048f23
commit e642b24be0
17 changed files with 842 additions and 742 deletions
+29 -64
View File
@@ -4,8 +4,6 @@ import (
"bytes"
"context"
"crypto"
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/rand"
"crypto/rsa"
@@ -16,9 +14,10 @@ import (
"crypto/x509/pkix"
"encoding/asn1"
"encoding/binary"
"encoding/json"
jsonv2 "encoding/json/v2"
"encoding/pem"
"errors"
"io/fs"
"math/big"
"net"
"os"
@@ -26,9 +25,7 @@ import (
"testing"
"time"
jsonv2 "encoding/json/v2"
"golang.org/x/sys/unix"
"gitea.neitzel.de/konrad/funknetz/internal/gcm64"
"gitea.neitzel.de/konrad/funknetz/internal/opus"
"gitea.neitzel.de/konrad/funknetz/station"
)
@@ -194,7 +191,10 @@ func startV3(t *testing.T, certDir string) *v3 {
}
ctx, cancel := context.WithCancel(t.Context())
t.Cleanup(cancel)
go func() { _ = st.Run(ctx) }()
go func() {
// Run's error is observed through State, which is the station seam.
_ = st.Run(ctx)
}()
pki := newPKI(t, "", net.ParseIP("127.0.0.1"))
return &v3{st: st, clock: clock, refl: refl, pki: pki, certDir: certDir, cancel: cancel}
}
@@ -276,21 +276,17 @@ func (v *v3) serverAndKey(t *testing.T, codecs []string) {
t.Fatalf("iv %d key %d rest %d", len(iv), len(key), len(rest))
}
var got map[string]string
if err := json.Unmarshal([]byte(raw), &got); err != nil {
if err := jsonv2.Unmarshal([]byte(raw), &got); err != nil {
t.Fatal(err)
}
if got["sw"] != station.SoftwareName || got["swVer"] != station.SoftwareVersion || got["projVer"] != station.SoftwareVersion {
t.Fatalf("node json %#v", got)
}
var u unix.Utsname
if err := unix.Uname(&u); err != nil {
t.Fatal(err)
name, ok := station.MachineArch()
if !ok {
t.Fatal("machine arch")
}
name := u.Machine[:]
if i := bytes.IndexByte(name, 0); i >= 0 {
name = name[:i]
}
if got["machineArch"] != string(name) {
if got["machineArch"] != name {
t.Fatalf("machineArch %q, want %q", got["machineArch"], name)
}
v.iv = iv
@@ -403,7 +399,7 @@ func openReg(t *testing.T, key, iv []byte, id uint16, pkt []byte) []byte {
func openGCM(t *testing.T, key, iv []byte, id uint16, counter uint32, aad, tag, ct []byte) []byte {
t.Helper()
plain, err := openTruncTest(key, nonceFor(iv, id, counter), tag, ct, aad)
plain, err := gcm64.Open(key, gcm64.IV(iv, id, counter), tag, ct, aad)
if err != nil {
t.Fatal(err)
}
@@ -414,7 +410,7 @@ func sealToStation(t *testing.T, key, iv []byte, counter uint32, plain []byte) [
t.Helper()
var aad [4]byte
binary.BigEndian.PutUint32(aad[:], counter)
tag, ct, err := sealTruncTest(key, nonceFor(iv, 0, counter), plain, aad[:])
tag, ct, err := gcm64.Seal(key, gcm64.IV(iv, 0, counter), plain, aad[:])
if err != nil {
t.Fatal(err)
}
@@ -423,48 +419,6 @@ func sealToStation(t *testing.T, key, iv []byte, counter uint32, plain []byte) [
return append(wire, ct...)
}
func nonceFor(iv []byte, id uint16, counter uint32) []byte {
nonce := make([]byte, 12)
copy(nonce, iv)
binary.BigEndian.PutUint16(nonce[6:8], id)
binary.BigEndian.PutUint32(nonce[8:12], counter)
return nonce
}
func sealTruncTest(key, nonce, plain, aad []byte) (tag, ct []byte, err error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, nil, err
}
out := gcm.Seal(nil, nonce, plain, aad)
return append([]byte(nil), out[len(plain):len(plain)+8]...), append([]byte(nil), out[:len(plain)]...), nil
}
func openTruncTest(key, nonce, tag, ct, aad []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
stream := gcm.Seal(nil, nonce, make([]byte, len(ct)), aad)
plain := make([]byte, len(ct))
for i := range plain {
plain[i] = ct[i] ^ stream[i]
}
check := gcm.Seal(nil, nonce, plain, aad)
if !bytes.Equal(check[len(plain):len(plain)+8], tag) {
return nil, errors.New("tag")
}
return plain, nil
}
func openFromStation(t *testing.T, key, iv []byte, id uint16, pkt []byte) (uint32, []byte) {
t.Helper()
if len(pkt) < 12 {
@@ -505,7 +459,7 @@ func TestBadCASignatureRetriesOn3(t *testing.T) {
if got.Connection != "Disconnected" {
t.Fatalf("Connection %s", got.Connection)
}
if _, err := os.Stat(filepath.Join(v.certDir, "ca.pem")); !os.IsNotExist(err) {
if _, err := os.Stat(filepath.Join(v.certDir, "ca.pem")); !errors.Is(err, fs.ErrNotExist) {
t.Fatalf("ca.pem err = %v, want absent", err)
}
v.clock.BlockUntilWaiting(t)
@@ -568,7 +522,10 @@ func TestCADigestMismatchIsLoggedAndKept(t *testing.T) {
}
ctx, cancel := context.WithCancel(t.Context())
defer cancel()
go func() { _ = st.Run(ctx) }()
go func() {
// Run's error is observed through State, which is the station seam.
_ = st.Run(ctx)
}()
pki := newPKI(t, "", net.ParseIP("127.0.0.1"))
refl.accept(t)
refl.readFrame(t)
@@ -607,7 +564,10 @@ func TestCannotCreateCertificateDirStops(t *testing.T) {
}
ctx, cancel := context.WithCancel(t.Context())
defer cancel()
go func() { _ = st.Run(ctx) }()
go func() {
// Run's error is observed through State, which is the station seam.
_ = st.Run(ctx)
}()
pki := newPKI(t, "", net.ParseIP("127.0.0.1"))
refl.accept(t)
refl.readFrame(t)
@@ -645,7 +605,10 @@ func TestRelativeCertificateDirStaysRelative(t *testing.T) {
}
ctx, cancel := context.WithCancel(t.Context())
defer cancel()
go func() { _ = st.Run(ctx) }()
go func() {
// Run's error is observed through State, which is the station seam.
_ = st.Run(ctx)
}()
pki := newPKI(t, "", net.ParseIP("127.0.0.1"))
refl.accept(t)
refl.readFrame(t)
@@ -777,6 +740,7 @@ func TestHostMismatchRetriesOn3(t *testing.T) {
v.pki = newPKI(t, "example.com", nil)
v.throughBundle(t)
v.refl.writeFrame(t, 15, nil)
// The station records this handshake failure; its State is the result.
_ = v.refl.handshake(t, v.pki.serverConfig())
got, err := v.st.Wait(context.Background(), func(s station.State) bool {
return s.Detail == "Reflector certificate does not match the host"
@@ -853,6 +817,7 @@ func TestTLSHandshakeFailureRetriesOn3(t *testing.T) {
cfg.MinVersion = tls.VersionTLS10
cfg.MaxVersion = tls.VersionTLS11
v.refl.writeFrame(t, 15, nil)
// The station records this handshake failure; its State is the result.
_ = v.refl.handshake(t, cfg)
if _, err := v.st.Wait(context.Background(), func(s station.State) bool { return s.Detail == "TLS handshake failed" }); err != nil {
t.Fatal(err)