75939a192c
client/tls_test: mints a throwaway CA + server cert in-memory; a client pinning the CA completes the handshake and operates (golden), a client without the CA fails the handshake (error path). busauth/tls_test: golden load of a CA PEM and a server keypair, plus error paths (missing file, non-PEM). Harness body extracted to bootHarness(ctrlMode, natsAuth, natsTLS). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
96 lines
3.0 KiB
Go
96 lines
3.0 KiB
Go
package busauth
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/pem"
|
|
"math/big"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// writeSelfSigned writes a self-signed cert + key PEM pair to dir and returns
|
|
// their paths. It is enough to exercise both LoadCATLSConfig (reads the cert as
|
|
// a CA) and ServerTLSConfig (reads the cert+key as a server keypair).
|
|
func writeSelfSigned(t *testing.T, dir string) (certPath, keyPath string) {
|
|
t.Helper()
|
|
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
if err != nil {
|
|
t.Fatalf("key: %v", err)
|
|
}
|
|
tmpl := &x509.Certificate{
|
|
SerialNumber: big.NewInt(1),
|
|
Subject: pkix.Name{CommonName: "unibus-tls-test"},
|
|
NotBefore: time.Now().Add(-time.Hour),
|
|
NotAfter: time.Now().Add(time.Hour),
|
|
IsCA: true,
|
|
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature,
|
|
BasicConstraintsValid: true,
|
|
}
|
|
der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &key.PublicKey, key)
|
|
if err != nil {
|
|
t.Fatalf("cert: %v", err)
|
|
}
|
|
certPath = filepath.Join(dir, "cert.pem")
|
|
keyPath = filepath.Join(dir, "key.pem")
|
|
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})
|
|
if err := os.WriteFile(certPath, certPEM, 0o644); err != nil {
|
|
t.Fatalf("write cert: %v", err)
|
|
}
|
|
keyDER, err := x509.MarshalECPrivateKey(key)
|
|
if err != nil {
|
|
t.Fatalf("marshal key: %v", err)
|
|
}
|
|
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyDER})
|
|
if err := os.WriteFile(keyPath, keyPEM, 0o600); err != nil {
|
|
t.Fatalf("write key: %v", err)
|
|
}
|
|
return certPath, keyPath
|
|
}
|
|
|
|
// Golden: a valid CA PEM loads into a config with a non-empty RootCAs pool, and
|
|
// a valid keypair loads into a config presenting one certificate.
|
|
func TestLoadTLSConfigsGolden(t *testing.T) {
|
|
dir := t.TempDir()
|
|
certPath, keyPath := writeSelfSigned(t, dir)
|
|
|
|
caCfg, err := LoadCATLSConfig(certPath)
|
|
if err != nil {
|
|
t.Fatalf("LoadCATLSConfig: %v", err)
|
|
}
|
|
if caCfg.RootCAs == nil {
|
|
t.Fatalf("expected a populated RootCAs pool")
|
|
}
|
|
|
|
srvCfg, err := ServerTLSConfig(certPath, keyPath)
|
|
if err != nil {
|
|
t.Fatalf("ServerTLSConfig: %v", err)
|
|
}
|
|
if len(srvCfg.Certificates) != 1 {
|
|
t.Fatalf("expected exactly one server certificate, got %d", len(srvCfg.Certificates))
|
|
}
|
|
}
|
|
|
|
// Error path: missing file, and a file that is not valid PEM.
|
|
func TestLoadTLSConfigsErrors(t *testing.T) {
|
|
if _, err := LoadCATLSConfig("/no/such/ca.crt"); err == nil {
|
|
t.Fatalf("expected error for missing CA file")
|
|
}
|
|
dir := t.TempDir()
|
|
junk := filepath.Join(dir, "junk.crt")
|
|
if err := os.WriteFile(junk, []byte("not a pem"), 0o644); err != nil {
|
|
t.Fatalf("write junk: %v", err)
|
|
}
|
|
if _, err := LoadCATLSConfig(junk); err == nil {
|
|
t.Fatalf("expected error for non-PEM CA file")
|
|
}
|
|
if _, err := ServerTLSConfig("/no/such/server.crt", "/no/such/server.key"); err == nil {
|
|
t.Fatalf("expected error for missing server keypair")
|
|
}
|
|
}
|