carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package release
2
3import (
4 "context"
5 "crypto/ed25519"
6 "encoding/json"
7 "errors"
8 "os"
9 "path/filepath"
10 "strings"
11 "testing"
12 "time"
13)
14
15func proverKey(t *testing.T) (ed25519.PrivateKey, ed25519.PublicKey, string) {
16 t.Helper()
17 seed, pubB64, err := GenerateKey()
18 if err != nil {
19 t.Fatal(err)
20 }
21 priv, err := ParseSigningKey(seed, "test")
22 if err != nil {
23 t.Fatal(err)
24 }
25 pub, err := ParsePublicKey(pubB64, "test")
26 if err != nil {
27 t.Fatal(err)
28 }
29 return priv, pub, pubB64
30}
31
32var testSHA = strings.Repeat("ab", 32)
33
34func sampleProof() Proof {
35 return Proof{App: "console", Platform: "linux-arm64", SHA256: testSHA, Version: "f4478b15", Prover: "dev",
36 ActiveSince: time.Date(2026, 10, 6, 9, 0, 0, 0, time.UTC),
37 ProvenAt: time.Date(2026, 10, 6, 9, 15, 0, 0, time.UTC)}
38}
39
40func TestProofSignVerifyRoundTrip(t *testing.T) {
41 priv, pub, _ := proverKey(t)
42 p, err := SignProof(priv, sampleProof())
43 if err != nil {
44 t.Fatal(err)
45 }
46 b, err := json.MarshalIndent(p, "", " ")
47 if err != nil {
48 t.Fatal(err)
49 }
50 got, err := ParseProof(b)
51 if err != nil {
52 t.Fatalf("ParseProof: %v", err)
53 }
54 if got.Schema != 1 {
55 t.Fatalf("schema = %d", got.Schema)
56 }
57 if err := got.Check(pub, "console", "linux-arm64", testSHA, "dev"); err != nil {
58 t.Fatalf("Check: %v", err)
59 }
60 // The signature is over the canonical form, not the file's bytes, so a
61 // proof re-encoded in transit still verifies.
62 compact, err := json.Marshal(got)
63 if err != nil {
64 t.Fatal(err)
65 }
66 again, err := ParseProof(compact)
67 if err != nil || again.Verify(pub) != nil {
68 t.Fatalf("a compact re-encoding does not verify: %v", err)
69 }
70}
71
72func TestProofEditedFieldDoesNotVerify(t *testing.T) {
73 priv, pub, _ := proverKey(t)
74 p, err := SignProof(priv, sampleProof())
75 if err != nil {
76 t.Fatal(err)
77 }
78 for name, edit := range map[string]func(*Proof){
79 "sha256": func(p *Proof) { p.SHA256 = strings.Repeat("cd", 32) },
80 "app": func(p *Proof) { p.App = "other" },
81 "platform": func(p *Proof) { p.Platform = "linux-amd64" },
82 "version": func(p *Proof) { p.Version = "v9" },
83 "prover": func(p *Proof) { p.Prover = "staging" },
84 "proven_at": func(p *Proof) { p.ProvenAt = p.ProvenAt.Add(time.Second) },
85 } {
86 q := *p
87 edit(&q)
88 if err := q.Verify(pub); err == nil {
89 t.Fatalf("%s edited and the proof still verifies", name)
90 }
91 }
92 _, other, _ := proverKey(t)
93 if err := p.Verify(other); err == nil {
94 t.Fatal("the proof verifies against another prover's key")
95 }
96}
97
98func TestProofCheckNamesTheMismatch(t *testing.T) {
99 priv, pub, _ := proverKey(t)
100 p, err := SignProof(priv, sampleProof())
101 if err != nil {
102 t.Fatal(err)
103 }
104 for _, c := range []struct{ app, plat, sha, prover, want string }{
105 {"other", "linux-arm64", testSHA, "dev", "app"},
106 {"console", "linux-amd64", testSHA, "dev", "platform"},
107 {"console", "linux-arm64", strings.Repeat("cd", 32), "dev", "sha256"},
108 {"console", "linux-arm64", testSHA, "staging", "prover"},
109 } {
110 err := p.Check(pub, c.app, c.plat, c.sha, c.prover)
111 if err == nil || !strings.Contains(err.Error(), c.want) {
112 t.Fatalf("Check(%s,%s,%s,%s) = %v, want an error naming %s", c.app, c.plat, c.sha, c.prover, err, c.want)
113 }
114 }
115}
116
117func TestParseProofRejectsBadShape(t *testing.T) {
118 priv, _, _ := proverKey(t)
119 good, err := SignProof(priv, sampleProof())
120 if err != nil {
121 t.Fatal(err)
122 }
123 for name, edit := range map[string]func(map[string]any){
124 "schema": func(m map[string]any) { m["schema"] = 2 },
125 "sha256": func(m map[string]any) { m["sha256"] = "../../etc" },
126 "prover": func(m map[string]any) { m["prover"] = "Dev Box" },
127 "app": func(m map[string]any) { m["app"] = "" },
128 "sig": func(m map[string]any) { m["sig"] = "not base64!" },
129 "proven_at": func(m map[string]any) { delete(m, "proven_at") },
130 } {
131 b, _ := json.Marshal(good)
132 var m map[string]any
133 if err := json.Unmarshal(b, &m); err != nil {
134 t.Fatal(err)
135 }
136 edit(m)
137 b, _ = json.Marshal(m)
138 if _, err := ParseProof(b); err == nil {
139 t.Fatalf("%s: a malformed proof parsed", name)
140 }
141 }
142 if _, err := ParseProof([]byte("{")); err == nil {
143 t.Fatal("torn JSON parsed")
144 }
145}
146
147func TestProofStoreRoundTrip(t *testing.T) {
148 s := NewDirStore(t.TempDir())
149 ctx := context.Background()
150 priv, pub, _ := proverKey(t)
151 p, err := SignProof(priv, sampleProof())
152 if err != nil {
153 t.Fatal(err)
154 }
155 if got, want := ProofKey("bab", "console", testSHA, "dev"), "apps/bab/console/proofs/"+testSHA+"/dev.json"; got != want {
156 t.Fatalf("ProofKey = %s, want %s", got, want)
157 }
158 if _, err := GetProof(ctx, s, "bab", "console", testSHA, "dev"); !errors.Is(err, os.ErrNotExist) {
159 t.Fatalf("absent proof = %v, want os.ErrNotExist", err)
160 }
161 if err := PutProof(ctx, s, "bab", p); err != nil {
162 t.Fatal(err)
163 }
164 if err := PutProof(ctx, s, "bab", p); err != nil {
165 t.Fatalf("carrying the same proof twice: %v", err)
166 }
167 got, err := GetProof(ctx, s, "bab", "console", testSHA, "dev")
168 if err != nil || got.Verify(pub) != nil {
169 t.Fatalf("GetProof = %+v, %v", got, err)
170 }
171 if _, err := GetProof(ctx, s, "bab", "console", "../x", "dev"); err == nil {
172 t.Fatal("a path in the sha256 reached the store")
173 }
174}
175
176func TestProofPendingIsCreateOnly(t *testing.T) {
177 s := NewDirStore(t.TempDir())
178 ctx := context.Background()
179 first := time.Date(2026, 10, 6, 9, 30, 30, 0, time.UTC)
180 rec := ProofPending{App: "console", Platform: "linux-arm64", SHA256: testSHA, Version: "v2", Prover: "dev", LiveSince: first, After: "15m"}
181 if err := PutProofPending(ctx, s, "bab", rec); err != nil {
182 t.Fatal(err)
183 }
184 rec.LiveSince = first.Add(time.Hour)
185 if err := PutProofPending(ctx, s, "bab", rec); err != nil {
186 t.Fatalf("a second sighting: %v", err)
187 }
188 got, err := GetProofPending(ctx, s, "bab", "console", testSHA, "dev")
189 if err != nil || !got.LiveSince.Equal(first) {
190 t.Fatalf("pending = %+v, %v; want the first sighting kept", got, err)
191 }
192}
193
194func TestFileSHA256(t *testing.T) {
195 p := filepath.Join(t.TempDir(), "f")
196 if err := os.WriteFile(p, []byte("bad build"), 0o644); err != nil {
197 t.Fatal(err)
198 }
199 got, err := FileSHA256(p)
200 if err != nil || got != etagOf([]byte("bad build")) {
201 t.Fatalf("FileSHA256 = %q, %v", got, err)
202 }
203}
204