package runtime import ( "crypto/ed25519" "errors" "testing" "github.com/tegwick/fluid-core/internal/contract" "github.com/tegwick/fluid-core/internal/signing" ) // TestVerifiedRegistryRefusesUnsigned is the router-side half of Blueprint 35. // The pipeline signs; this is what makes the signature mean something. func TestVerifiedRegistryRefusesUnsigned(t *testing.T) { signer, pub, err := signing.GenerateKey("key-1") if err != nil { t.Fatal(err) } reg := NewVerifiedRegistry(testInterface, signing.NewVerifier(map[string]ed25519.PublicKey{"key-1": pub})) unsigned := descriptor("R-1", contract.RevisionStateStable) if err := reg.PutRevision(unsigned); !errors.Is(err, signing.ErrUnsigned) { t.Fatalf("unsigned descriptor accepted: %v", err) } sig, err := signer.Sign(contract.RevisionDescriptorDocument{Revision: unsigned}) if err != nil { t.Fatal(err) } signed := unsigned signed.Signature = &contract.RevisionSignature{ Algorithm: contract.RevisionSignatureAlgorithm(sig.Algorithm), KeyID: sig.KeyID, Value: sig.Value, } if err := reg.PutRevision(signed); err != nil { t.Fatalf("correctly signed descriptor refused: %v", err) } } // TestVerifiedRegistryRefusesTamperedDescriptor covers the case that matters // most: an attacker promoting a signed experiment to stable, or repointing it // at their own adapter. func TestVerifiedRegistryRefusesTamperedDescriptor(t *testing.T) { signer, pub, _ := signing.GenerateKey("key-1") reg := NewVerifiedRegistry(testInterface, signing.NewVerifier(map[string]ed25519.PublicKey{"key-1": pub})) original := descriptor("R-1", contract.RevisionStateExperiment) sig, _ := signer.Sign(contract.RevisionDescriptorDocument{Revision: original}) attach := func(d contract.Revision) contract.Revision { d.Signature = &contract.RevisionSignature{ Algorithm: contract.RevisionSignatureAlgorithm(sig.Algorithm), KeyID: sig.KeyID, Value: sig.Value, } return d } promoted := original promoted.State = contract.RevisionStateStable if err := reg.PutRevision(attach(promoted)); !errors.Is(err, signing.ErrBadSignature) { t.Errorf("a promoted descriptor was accepted: %v", err) } redirected := original redirected.Runtime.Upstream = "http://attacker:8080" if err := reg.PutRevision(attach(redirected)); !errors.Is(err, signing.ErrBadSignature) { t.Errorf("a redirected descriptor was accepted: %v", err) } } func TestVerifiedRegistryRefusesUntrustedKey(t *testing.T) { rogue, _, _ := signing.GenerateKey("rogue") _, trusted, _ := signing.GenerateKey("key-1") reg := NewVerifiedRegistry(testInterface, signing.NewVerifier(map[string]ed25519.PublicKey{"key-1": trusted})) d := descriptor("R-1", contract.RevisionStateStable) sig, _ := rogue.Sign(contract.RevisionDescriptorDocument{Revision: d}) d.Signature = &contract.RevisionSignature{ Algorithm: contract.RevisionSignatureAlgorithm(sig.Algorithm), KeyID: sig.KeyID, Value: sig.Value, } if err := reg.PutRevision(d); !errors.Is(err, signing.ErrUnknownKey) { t.Errorf("a descriptor signed by an untrusted key was accepted: %v", err) } }