Files
rdp-proxy/backend/internal/platform/clusterauth/authority_test.go
T

128 lines
4.1 KiB
Go

package clusterauth
import (
"encoding/json"
"errors"
"fmt"
"testing"
"time"
)
func TestSignAndVerifyRawPayload(t *testing.T) {
keys, err := GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair: %v", err)
}
payload := json.RawMessage(`{"cluster_id":"cluster-1","schema_version":"test.v1","value":1}`)
signature, err := SignRaw(keys.PrivateKeyB64, payload, time.Date(2026, 4, 28, 12, 0, 0, 0, time.UTC))
if err != nil {
t.Fatalf("SignRaw: %v", err)
}
if signature.KeyFingerprint != keys.Fingerprint {
t.Fatalf("fingerprint = %q, want %q", signature.KeyFingerprint, keys.Fingerprint)
}
if err := VerifyRaw(keys.PublicKeyB64, payload, signature); err != nil {
t.Fatalf("VerifyRaw: %v", err)
}
}
func TestVerifyRawRejectsTamperedPayload(t *testing.T) {
keys, err := GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair: %v", err)
}
payload := json.RawMessage(`{"cluster_id":"cluster-1","schema_version":"test.v1","value":1}`)
signature, err := SignRaw(keys.PrivateKeyB64, payload, time.Date(2026, 4, 28, 12, 0, 0, 0, time.UTC))
if err != nil {
t.Fatalf("SignRaw: %v", err)
}
tampered := json.RawMessage(`{"cluster_id":"cluster-1","schema_version":"test.v1","value":2}`)
if err := VerifyRaw(keys.PublicKeyB64, tampered, signature); !errors.Is(err, ErrInvalidSignature) {
t.Fatalf("err = %v, want ErrInvalidSignature", err)
}
}
func TestVerifyQuorumRawAcceptsThreshold(t *testing.T) {
payload := json.RawMessage(`{"cluster_id":"cluster-1","schema_version":"rap.node_update_plan_authority.v1","action":"update"}`)
descriptor, keys := testQuorum(t, 3, 2)
payloadHash, err := HashRaw(payload)
if err != nil {
t.Fatalf("HashRaw: %v", err)
}
quorumHash, err := QuorumDescriptorHash(descriptor)
if err != nil {
t.Fatalf("QuorumDescriptorHash: %v", err)
}
signatureA, err := SignRaw(keys[0].PrivateKeyB64, payload, time.Date(2026, 5, 16, 12, 0, 0, 0, time.UTC))
if err != nil {
t.Fatalf("SignRaw A: %v", err)
}
signatureB, err := SignRaw(keys[1].PrivateKeyB64, payload, time.Date(2026, 5, 16, 12, 0, 1, 0, time.UTC))
if err != nil {
t.Fatalf("SignRaw B: %v", err)
}
envelope := QuorumEnvelope{
SchemaVersion: QuorumEnvelopeVersion,
ClusterID: "cluster-1",
Epoch: "epoch-1",
Threshold: 2,
PayloadSHA256: payloadHash,
QuorumSHA256: quorumHash,
Signatures: []Signature{signatureA, signatureB},
}
if err := VerifyQuorumRaw(descriptor, payload, envelope, "update-authority"); err != nil {
t.Fatalf("VerifyQuorumRaw: %v", err)
}
}
func TestVerifyQuorumRawRejectsBelowThreshold(t *testing.T) {
payload := json.RawMessage(`{"cluster_id":"cluster-1","schema_version":"rap.node_update_plan_authority.v1","action":"update"}`)
descriptor, keys := testQuorum(t, 3, 2)
payloadHash, _ := HashRaw(payload)
quorumHash, _ := QuorumDescriptorHash(descriptor)
signature, err := SignRaw(keys[0].PrivateKeyB64, payload, time.Date(2026, 5, 16, 12, 0, 0, 0, time.UTC))
if err != nil {
t.Fatalf("SignRaw: %v", err)
}
envelope := QuorumEnvelope{
SchemaVersion: QuorumEnvelopeVersion,
ClusterID: "cluster-1",
Epoch: "epoch-1",
Threshold: 2,
PayloadSHA256: payloadHash,
QuorumSHA256: quorumHash,
Signatures: []Signature{signature},
}
if err := VerifyQuorumRaw(descriptor, payload, envelope, "update-authority"); !errors.Is(err, ErrInvalidSignature) {
t.Fatalf("err = %v, want ErrInvalidSignature", err)
}
}
func testQuorum(t *testing.T, count int, threshold int) (QuorumDescriptor, []KeyPair) {
t.Helper()
descriptor := QuorumDescriptor{
SchemaVersion: QuorumSchemaVersion,
ClusterID: "cluster-1",
Epoch: "epoch-1",
Threshold: threshold,
}
keys := make([]KeyPair, 0, count)
for i := 0; i < count; i++ {
keyPair, err := GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair: %v", err)
}
descriptor.Members = append(descriptor.Members, QuorumMember{
NodeID: fmt.Sprintf("authority-%d", i+1),
Role: "update-authority",
PublicKey: keyPair.PublicKeyB64,
PublicKeyFingerprint: keyPair.Fingerprint,
Scopes: []string{"update-authority"},
})
keys = append(keys, keyPair)
}
return descriptor, keys
}