carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package hostbox
2
3import (
4 "crypto"
5 "crypto/rand"
6 "crypto/rsa"
7 "crypto/sha256"
8 "crypto/x509"
9 "encoding/base64"
10 "encoding/json"
11 "errors"
12 "reflect"
13 "testing"
14
15 "github.com/carlosframework/platform/internal/boxid"
16)
17
18// L1: a roster row read without a binding is not a verified actor. Every
19// installed-state comparison stamps and compares the binding, so an actor
20// without one must never leave Verify.
21func TestIdentityVerifyRequiresBinding(t *testing.T) {
22 key, err := rsa.GenerateKey(rand.Reader, 2048)
23 if err != nil {
24 t.Fatal(err)
25 }
26 actor := Actor{Box: "box", Enrollment: "en", Revision: 1, Binding: "binding", Region: "ie", Bucket: "data", Namespace: "platform", Eligible: true, Cloud: CloudIdentity{Provider: "aws", Account: "111122223333", Region: "eu-west-1", InstanceID: "i-0abc"}}
27 doc, err := json.Marshal(map[string]string{"accountId": actor.Cloud.Account, "region": actor.Cloud.Region, "instanceId": actor.Cloud.InstanceID})
28 if err != nil {
29 t.Fatal(err)
30 }
31 sum := sha256.Sum256(doc)
32 sig, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, sum[:])
33 if err != nil {
34 t.Fatal(err)
35 }
36 raw, err := json.Marshal(boxid.Record{V: 1, Box: actor.Box, IdentityDocument: base64.StdEncoding.EncodeToString(doc), IdentitySignature: base64.StdEncoding.EncodeToString(sig)})
37 if err != nil {
38 t.Fatal(err)
39 }
40 p := IdentityProvider{Account: actor.Cloud.Account, Box: actor.Box, Region: actor.Region, AWSRegion: actor.Cloud.Region, Bucket: actor.Bucket, Namespace: actor.Namespace, Roster: enrollmentRoster{actor}, Evidence: enrollmentEvidence(raw), Certs: map[string]*x509.Certificate{actor.Cloud.Region: {PublicKey: &key.PublicKey, PublicKeyAlgorithm: x509.RSA}}}
41 if got, err := p.Verify(t.Context()); err != nil || got != actor {
42 t.Fatalf("bound actor: got=%+v err=%v", got, err)
43 }
44 unbound := actor
45 unbound.Binding = ""
46 if p.actorMatches(unbound) {
47 t.Fatal("actorMatches admitted an actor without a binding")
48 }
49 p.Roster = enrollmentRoster{unbound}
50 if _, err := p.Verify(t.Context()); !errors.Is(err, ErrChanged) {
51 t.Fatalf("Verify without a binding: %v, want ErrChanged", err)
52 }
53}
54
55// Bound clears only Revision, and SameBinding ignores only Revision: every
56// other actor field, Binding included, still separates two actors. The
57// reflection walk covers fields added later.
58func TestActorBoundIgnoresOnlyRevision(t *testing.T) {
59 base := Actor{FleetHomeRegion: "ie", HomeRegion: "ie", ControlBucket: "deployment", Box: "box", Enrollment: "enrolled", Revision: 7, Binding: "binding", Region: "ie", Bucket: "data", Namespace: "platform", Cloud: CloudIdentity{Provider: "aws", Account: "111122223333", Region: "eu-west-1", InstanceID: "i-0abc"}, Eligible: true}
60 if b := base.Bound(); b.Revision != 0 || func() Actor { c := base; c.Revision = 0; return c }() != b {
61 t.Fatalf("Bound = %+v", b)
62 }
63 if !SameBinding(base, base.Bound()) {
64 t.Fatal("an actor is not SameBinding with its bound copy")
65 }
66 var walk func(v reflect.Value, index []int, path string)
67 walk = func(v reflect.Value, index []int, path string) {
68 for i := 0; i < v.NumField(); i++ {
69 f := v.Field(i)
70 at := append(append([]int{}, index...), i)
71 name := path + v.Type().Field(i).Name
72 if f.Kind() == reflect.Struct {
73 walk(f, at, name+".")
74 continue
75 }
76 changed := base
77 field := reflect.ValueOf(&changed).Elem().FieldByIndex(at)
78 switch field.Kind() {
79 case reflect.String:
80 field.SetString(field.String() + "-changed")
81 case reflect.Uint64:
82 field.SetUint(field.Uint() + 1)
83 case reflect.Bool:
84 field.SetBool(!field.Bool())
85 default:
86 t.Fatalf("%s: unhandled kind %s", name, field.Kind())
87 }
88 if got, want := SameBinding(changed, base), name == "Revision"; got != want {
89 t.Errorf("%s changed: SameBinding = %v, want %v", name, got, want)
90 }
91 if got, want := changed.Bound() == base.Bound(), name == "Revision"; got != want {
92 t.Errorf("%s changed: Bound equal = %v, want %v", name, got, want)
93 }
94 }
95 }
96 walk(reflect.ValueOf(base), nil, "")
97}
98