Files
Privatae e4c9b69b46
ci / personal-data (push) Successful in 5s
ci / test (push) Successful in 3m4s
ci / fuzz (push) Successful in 3m45s
bastion 0.53.2
2026-10-20 04:42:14 +00:00

75 lines
3.1 KiB
Go

package main
// Fuzz targets for the signed inputs that need no box state: release
// manifests and the box's own owner and anchor claims. The ones that verify
// against keys pinned on an anchored box are in fuzz_grants_test.go.
import (
"bytes"
"encoding/base64"
"strings"
"testing"
)
// A release manifest is read only if its signature verifies for its kind
// (release_sign.go). Whatever bytes arrive as the manifest and the signature,
// verification never panics, and it succeeds only for the exact manifest that
// was signed, with the exact signature, for the kind it was signed for.
func FuzzReleaseSignatureIsBoundToItsManifestAndKind(f *testing.F) {
signed := []byte(`{"version":"1.2.3","binaries":{"linux-amd64":{"sha256":"00"}}}`)
sig := testReleaseSig(releaseKindBastion, signed)
f.Add(signed, sig, false)
f.Add(signed, sig, true)
f.Add([]byte(`{"version":"1.2.3\n","binaries":{}}`), sig, false)
f.Add([]byte(`{"version":"1.2.4","binaries":{"linux-amd64":{"sha256":"00"}}}`), sig, false)
f.Add(signed, []byte(" "+string(bytes.TrimSpace(sig))+"\r\n"), false)
f.Add([]byte(`{"version":1}`), []byte("x"), false)
f.Add([]byte{}, []byte{}, false)
f.Fuzz(func(t *testing.T, body, s []byte, agent bool) {
kind := releaseKindBastion
if agent {
kind = releaseKindAgent
}
err := verifyReleaseSignature(kind, body, s)
if err != nil {
return
}
// The same signature can be spelled more than one way in base64
// (line breaks, unused low bits); what must match is its bytes.
got, _ := base64.StdEncoding.DecodeString(strings.TrimSpace(string(s)))
want, _ := base64.StdEncoding.DecodeString(strings.TrimSpace(string(sig)))
if kind != releaseKindBastion || !bytes.Equal(body, signed) || !bytes.Equal(got, want) {
t.Fatalf("a %s manifest verified that is not the one signed for bastion:\n%q\n%q", kind, body, s)
}
})
}
// The box signs its owner claim and its anchor claim with the same key, so the
// two signing inputs must never be the same bytes, and two different anchor
// claims whose names pass claimNameOK must never be the same bytes either.
func FuzzOwnerAndAnchorClaimsNeverShareSignedBytes(f *testing.F) {
f.Add("Acme", "host.a", "2026-01-01T00:00:00Z", "Acme", "host.a", "2026-01-01T00:00:00Z")
f.Add("x\nhost", "h", "t", "x", "host\nh", "t")
f.Add("anchor\nx", "h", "t", "x", "h", "t")
f.Add("a", "b\nc", "t", "a\nb", "c", "t")
f.Fuzz(func(t *testing.T, o1, h1, a1, o2, h2, a2 string) {
if bytes.Equal(anchorSigningInput(o1, h1, a1), ownerSigningInput(o2, a2)) {
t.Fatalf("an anchor claim (%q, %q, %q) and an owner claim (%q, %q) sign the same bytes", o1, h1, a1, o2, a2)
}
for _, n := range []string{o1, h1, o2, h2} {
if claimNameOK(n) != nil {
return
}
}
same := o1 == o2 && h1 == h2 && a1 == a2
if !same && bytes.Equal(anchorSigningInput(o1, h1, a1), anchorSigningInput(o2, h2, a2)) {
t.Fatalf("two different anchor claims sign the same bytes: (%q, %q, %q) and (%q, %q, %q)", o1, h1, a1, o2, h2, a2)
}
if o1 != o2 || a1 != a2 {
if bytes.Equal(ownerSigningInput(o1, a1), ownerSigningInput(o2, a2)) {
t.Fatalf("two different owner claims sign the same bytes: (%q, %q) and (%q, %q)", o1, a1, o2, a2)
}
}
})
}