Files
go-cart-actor/internal/ucp/signing_test.go
T
mats 9aa587b9cf
Build and Publish / BuildAndDeployArm64 (push) Failing after 6s
Build and Publish / BuildAndDeployAmd64 (push) Has been cancelled
mcp and ucp
2026-06-24 10:50:32 +02:00

174 lines
4.9 KiB
Go

package ucp
import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestSigningConfig_FromPEM(t *testing.T) {
// PEM from the generated key pair (openssl ecparam -genkey -name prime256v1)
pemData := `-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIDTssEctnrEqym80fx2jEVolTW+tWrHyo8fmbi8hMFWmoAoGCCqGSM49
AwEHoUQDQgAEpnPksDSSMcNgUYT4++Z6XuS0V2p6TNMSjWBsgXOu7mo5Esoux+Cm
y+C84ty5VUAdEpoDyNfWMeaAlHKGo+FjiQ==
-----END EC PRIVATE KEY-----`
cfg, err := NewSigningConfigFromPEM([]byte(pemData), "k6n-ecdsa-2026")
if err != nil {
t.Fatalf("NewSigningConfigFromPEM failed: %v", err)
}
if cfg.KeyID != "k6n-ecdsa-2026" {
t.Fatalf("expected key ID 'k6n-ecdsa-2026', got %q", cfg.KeyID)
}
if cfg.PrivateKey == nil {
t.Fatal("expected non-nil private key")
}
if cfg.PrivateKey.Curve.Params().Name != "P-256" {
t.Fatalf("expected P-256 curve, got %s", cfg.PrivateKey.Curve.Params().Name)
}
}
func TestSigningConfig_InvalidPEM(t *testing.T) {
_, err := NewSigningConfigFromPEM([]byte("not a pem"), "test")
if err == nil {
t.Fatal("expected error for invalid PEM")
}
}
func TestWithSigning_AddsHeaders(t *testing.T) {
// Load the test key.
cfg := mustLoadTestKey(t)
// A simple handler that returns a JSON response.
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
})
wrapped := WithSigning(handler, cfg)
req := httptest.NewRequest("GET", "/test", nil)
rec := httptest.NewRecorder()
wrapped.ServeHTTP(rec, req)
// Check the response body.
if rec.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", rec.Code)
}
// Check that signature headers are present.
sigInput := rec.Header().Get("Signature-Input")
sig := rec.Header().Get("Signature")
ts := rec.Header().Get("x-ucp-timestamp")
if sigInput == "" {
t.Fatal("expected Signature-Input header")
}
if sig == "" {
t.Fatal("expected Signature header")
}
if ts == "" {
t.Fatal("expected x-ucp-timestamp header")
}
// Verify the signature input format.
if !strings.HasPrefix(sigInput, `sig1=(@status "content-type" "x-ucp-timestamp");`) {
t.Fatalf("unexpected Signature-Input format: %q", sigInput)
}
if !strings.Contains(sigInput, `keyid="k6n-ecdsa-2026"`) {
t.Fatalf("Signature-Input missing keyid: %q", sigInput)
}
if !strings.Contains(sigInput, `alg="ecdsa-p256"`) {
t.Fatalf("Signature-Input missing alg: %q", sigInput)
}
// Verify the signature format.
if !strings.HasPrefix(sig, "sig1=:") || !strings.HasSuffix(sig, ":") {
t.Fatalf("unexpected Signature format: %q", sig)
}
}
func TestWithSigning_NilConfig(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
})
wrapped := WithSigning(handler, nil)
req := httptest.NewRequest("GET", "/test", nil)
rec := httptest.NewRecorder()
wrapped.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", rec.Code)
}
if rec.Header().Get("Signature-Input") != "" {
t.Fatal("expected no Signature-Input when signing is nil")
}
}
func TestWithSigning_DifferentStatuses(t *testing.T) {
cfg := mustLoadTestKey(t)
for _, status := range []int{200, 201, 400, 404, 500} {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(status)
w.Write([]byte(`{}`))
})
wrapped := WithSigning(handler, cfg)
req := httptest.NewRequest("GET", "/test", nil)
rec := httptest.NewRecorder()
wrapped.ServeHTTP(rec, req)
if rec.Code != status {
t.Fatalf("expected status %d, got %d", status, rec.Code)
}
if rec.Header().Get("Signature-Input") == "" {
t.Fatalf("expected Signature-Input for status %d", status)
}
}
}
func TestWithSigning_WithUCPCartHandler(t *testing.T) {
cfg := mustLoadTestKey(t)
applier := newTestApplier()
handler := CartHandler(applier)
wrapped := WithSigning(handler, cfg)
// Create a cart through the wrapped handler.
req := httptest.NewRequest("POST", "/", nil)
rec := httptest.NewRecorder()
wrapped.ServeHTTP(rec, req)
if rec.Code != http.StatusCreated {
t.Fatalf("expected 201, got %d", rec.Code)
}
// Verify signed.
if rec.Header().Get("Signature-Input") == "" {
t.Fatal("expected Signature-Input on UCP cart response")
}
if rec.Header().Get("x-ucp-timestamp") == "" {
t.Fatal("expected x-ucp-timestamp on UCP cart response")
}
}
// mustLoadTestKey loads the test PEM for signing tests.
func mustLoadTestKey(t testing.TB) *SigningConfig {
t.Helper()
pemData := `-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIDTssEctnrEqym80fx2jEVolTW+tWrHyo8fmbi8hMFWmoAoGCCqGSM49
AwEHoUQDQgAEpnPksDSSMcNgUYT4++Z6XuS0V2p6TNMSjWBsgXOu7mo5Esoux+Cm
y+C84ty5VUAdEpoDyNfWMeaAlHKGo+FjiQ==
-----END EC PRIVATE KEY-----`
cfg, err := NewSigningConfigFromPEM([]byte(pemData), "k6n-ecdsa-2026")
if err != nil {
t.Fatalf("mustLoadTestKey: %v", err)
}
return cfg
}