carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package hostfloor
2
3import (
4 "bytes"
5 "crypto/ed25519"
6 "crypto/rand"
7 "encoding/json"
8 "testing"
9
10 "github.com/carlosframework/platform/internal/release"
11)
12
13// The armed policy signed offline (claims run sign-policy) carries exactly
14// the floor bytes and authorization that Publish later writes for the same
15// floor. A box armed with it reports that floor's digest, and guard-exec
16// compares its local policy with the published pair, so any difference
17// would refuse every guarded launch once the floor is published.
18func TestArmedPolicyIsThePublishedAuthorization(t *testing.T) {
19 pub, key, _ := ed25519.GenerateKey(rand.Reader)
20 sg := release.RootSigner(key)
21 floor := Floor{Min: 1, MinRelease: "v0.28.2"}
22 p, err := ArmedPolicy(sg, "bucket", floor)
23 if err != nil {
24 t.Fatal(err)
25 }
26 if p.Schema != 1 || p.DeploymentBucket != "bucket" || p.Mode != "armed" {
27 t.Fatalf("policy header %+v", p)
28 }
29 verified, err := p.Verify(pub, "bucket")
30 if err != nil || verified.Floor != floor {
31 t.Fatal("armed policy does not verify", verified, err)
32 }
33 if _, err = p.Verify(pub, "other"); err == nil {
34 t.Fatal("armed policy verified for another deployment")
35 }
36 // The policy survives the file round trip guard-arm makes.
37 wire, err := json.Marshal(p)
38 if err != nil {
39 t.Fatal(err)
40 }
41 var read Policy
42 if err = json.Unmarshal(wire, &read); err != nil {
43 t.Fatal(err)
44 }
45 if _, err = read.Verify(pub, "bucket"); err != nil || !bytes.Equal(read.Floor, p.Floor) || !bytes.Equal(read.Authorization, p.Authorization) {
46 t.Fatal("policy file round trip changed the signed bytes", err)
47 }
48
49 s := release.NewDirStore(t.TempDir())
50 if err = Publish(t.Context(), s, sg, "bucket", floor); err != nil {
51 t.Fatal(err)
52 }
53 raw, _, err := s.Get(t.Context(), Key)
54 if err != nil {
55 t.Fatal(err)
56 }
57 auth, _, err := s.Get(t.Context(), AuthKey)
58 if err != nil {
59 t.Fatal(err)
60 }
61 if !bytes.Equal(raw, p.Floor) || !bytes.Equal(auth, p.Authorization) {
62 t.Fatalf("published pair differs from the armed policy:\nfloor %s\npolicy %s\nauth %s\npolicy %s", raw, p.Floor, auth, p.Authorization)
63 }
64 published, err := Read(t.Context(), s, pub, "bucket")
65 if err != nil || published.Digest != verified.Digest {
66 t.Fatal("published digest differs", published, verified, err)
67 }
68 // Signing again is the same bytes: ed25519 is deterministic and the
69 // floor marshals one way.
70 again, err := ArmedPolicy(sg, "bucket", floor)
71 if err != nil || !bytes.Equal(again.Floor, p.Floor) || !bytes.Equal(again.Authorization, p.Authorization) {
72 t.Fatal("second signature differs", err)
73 }
74}
75
76func TestArmedPolicyRefusesAnInvalidFloor(t *testing.T) {
77 _, key, _ := ed25519.GenerateKey(rand.Reader)
78 sg := release.RootSigner(key)
79 for name, tc := range map[string]struct {
80 bucket string
81 floor Floor
82 }{
83 "no bucket": {"", Floor{Min: 1, MinRelease: "v1.0.0"}},
84 "protocol zero": {"bucket", Floor{Min: 0, MinRelease: "v1.0.0"}},
85 "no release": {"bucket", Floor{Min: 1}},
86 "not a tag": {"bucket", Floor{Min: 1, MinRelease: "1.0.0"}},
87 "build, not tag": {"bucket", Floor{Min: 1, MinRelease: "26065378"}},
88 } {
89 t.Run(name, func(t *testing.T) {
90 if _, err := ArmedPolicy(sg, tc.bucket, tc.floor); err == nil {
91 t.Fatal("signed an invalid floor")
92 }
93 if _, _, err := Authorize(sg, tc.bucket, tc.floor); err == nil {
94 t.Fatal("authorized an invalid floor")
95 }
96 })
97 }
98}
99