package host import ( "bytes" "crypto/ed25519" "errors" "strings" "testing" "time" "github.com/carlosframework/platform/internal/hostactors" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostops" "github.com/carlosframework/platform/internal/release" ) func TestClaimEvidenceProviderConfiguresOnlyRootSigner(t *testing.T) { root := ed25519.NewKeyFromSeed(bytes.Repeat([]byte{1}, ed25519.SeedSize)) private := ed25519.NewKeyFromSeed(bytes.Repeat([]byte{2}, ed25519.SeedSize)) store := release.NewDirStore(t.TempDir()) actor := hostbox.Actor{Box: "box", Enrollment: strings.Repeat("a", 32), ControlBucket: "bucket", Bucket: "bucket"} roster := hostactors.Roster{V: 1, DeploymentBucket: actor.Bucket, Revision: 3, RequestLimitNS: int64(hostbox.RequestLimit), Regions: map[string]hostactors.Region{"ie": {}}, Boxes: map[string]hostactors.Box{ actor.Box: {CensusPublicKey: private.Public().(ed25519.PublicKey), Enrollment: actor.Enrollment, Namespace: "platform", BoxID: actor.Box, Label: actor.Box, Region: "ie", Pool: "default", Cloud: hostactors.Cloud{Provider: "aws", Account: "123", Region: "ie", InstanceID: "i-box"}, EvidenceSHA256: strings.Repeat("c", 64), State: "active"}, }, Consoles: map[string]hostactors.Console{}, Holds: map[string]hostactors.Reservation{}, Activations: map[string]hostactors.Reservation{}} body, err := release.SignRootEnvelope(release.RootSigner(root), roster) if err != nil { t.Fatal(err) } if _, err := store.Put(t.Context(), hostactors.Key, body, ""); err != nil { t.Fatal(err) } authority := &hostops.ProductionAuthority{Store: store, Directory: hostactors.Directory{Store: store, PublicKey: root.Public().(ed25519.PublicKey), Bucket: actor.Bucket}, Now: time.Now} owners := ClaimOwnerBindings{RetirementAuthority: authority, EvidenceKey: private} stop, err := owners.claimEvidenceCoordinator(t.Context(), store, "instance-control", actor) if err != nil || stop.RequestLimit != hostbox.RequestLimit || !bytes.Equal(stop.EvidenceSigner, private) { t.Fatalf("root signer: %+v, %v", stop, err) } private[0] ^= 1 if bytes.Equal(stop.EvidenceSigner, private) { t.Fatal("coordinator retained mutable provider key") } private[0] ^= 1 for _, mode := range []string{"agent", "adopt"} { if _, err := owners.claimEvidenceCoordinator(t.Context(), store, mode, actor); err == nil { t.Fatalf("%s accepted a private key", mode) } withoutKey := owners withoutKey.EvidenceKey = nil stop, err := withoutKey.claimEvidenceCoordinator(t.Context(), store, mode, actor) if err != nil || len(stop.EvidenceSigner) != 0 { t.Fatalf("%s needs or retains root key: %+v, %v", mode, stop, err) } } for _, bad := range []ClaimOwnerBindings{ {}, {RetirementAuthority: authority}, {RetirementAuthority: authority, EvidenceKey: root}, } { if _, err := bad.claimEvidenceCoordinator(t.Context(), store, "instance-control", actor); err == nil { t.Fatal("root accepted missing authority or unenrolled key") } } for _, wrongDirectory := range []bool{false, true} { wrong := *authority if wrongDirectory { wrong.Directory.Store = release.NewDirStore(t.TempDir()) } else { wrong.Store = release.NewDirStore(t.TempDir()) } miswired := owners miswired.RetirementAuthority = &wrong if _, err := miswired.claimEvidenceCoordinator(t.Context(), store, "instance-control", actor); err == nil { t.Fatal("authority accepted another store") } } otherActor := actor otherActor.Enrollment = strings.Repeat("b", 32) if _, err := owners.claimEvidenceCoordinator(t.Context(), store, "instance-control", otherActor); !errors.Is(err, ErrClaimStopIdentity) { t.Fatalf("wrong enrollment accepted: %v", err) } otherActor = actor otherActor.ControlBucket = "other-deployment" if _, err := owners.claimEvidenceCoordinator(t.Context(), store, "instance-control", otherActor); err == nil { t.Fatal("wrong deployment accepted") } }