Artifact 8b3a9d1f400149aea0c3ca19e3592b2fdf29b6cb84f13196a5457890949912e8:
- File
signing_test.go
— part of check-in
[451d368554]
at
2026-10-24 13:16:26
on branch main
— feat(horizon): bind each seed to its horizon and sign seeds apart
Every seed an origin mints now carries T +horizon-seed <C> <rule> (draft-fossil-horizon-00 §5.2.1), inside the signed bytes. The card is not counted as a tag in force, Mint never derives a horizon-seed row into a seed, and Apply skips the card.
Before a clone holds a seed, signed or not, it checks the seed against its store cone (§5.2.4). The seed must carry exactly one binding card naming the store's C and rule, and its D must be no earlier than the held seed's. A seed that fails is refused as a wrongly signed one and the clone falls back to the content horizon. A deepen discards the held seed first, so the floor restarts at the new C. A held seed that is no longer bound is discarded when it is next applied.
Seeds are signed with their own horizon-seed-command and are no longer signed by clearsign and pgp-command. SSH seed signatures use the fossil-horizon-seed namespace. clearsign.Key.Verify now takes the namespace, and check-in verification keeps fossilscm.
Adds 9 conformance cases and flips HZ-5.2-8, HZ-5.2-11, HZ-5.2-12 and HZ-5.2-13 to covered. The zit interop gate moves to zit 0.7.1, whose seeds are byte-identical with ours. (user: [email protected], size: 2226) [annotate] [blame] [check-ins using] [more...]
//go:build !js && !windows package libfossil import ( "bytes" "context" "testing" "github.com/danmestas/go-libfossil/internal/clearsign" "github.com/danmestas/go-libfossil/internal/clearsign/signtest" "github.com/danmestas/go-libfossil/internal/content" "github.com/danmestas/go-libfossil/internal/fsltype" ) // TestCommitSigning drives signing through the public API: with // SettingClearsign on, Commit stores the manifest signed by // SettingPGPCommand and NoSign stores it plain. func TestCommitSigning(t *testing.T) { r := newTestRepo(t) command, pub := signtest.SSHKey(t, "ed25519", clearsign.SSHNamespace) key, err := clearsign.ParseKey(pub) if err != nil { t.Fatal(err) } if err := r.SetConfig(SettingClearsign, "on"); err != nil { t.Fatal(err) } if err := r.SetConfig(SettingPGPCommand, command); err != nil { t.Fatal(err) } files := []FileToCommit{{Name: "a.txt", Content: []byte("a\n")}} signed, _, err := r.Commit(CommitOpts{Files: files, Comment: "signed", User: "test"}) if err != nil { t.Fatal(err) } plain, _, err := r.Commit(CommitOpts{ Files: files, Comment: "plain", User: "test", ParentID: signed, NoSign: true, }) if err != nil { t.Fatal(err) } stored := func(rid int64) []byte { data, err := content.Expand(r.Inner().DB(), fsltype.FslID(rid)) if err != nil { t.Fatal(err) } return data } if err := key.Verify(context.Background(), clearsign.SSHNamespace, stored(signed)); err != nil { t.Errorf("signed check-in: %v", err) } if data := stored(plain); !bytes.HasPrefix(data, []byte("C plain")) { t.Errorf("NoSign check-in is not plain:\n%s", data) } } // TestSetConfigSeedKey: SetConfig keeps an SSH key or an OpenPGP // fingerprint as SettingHorizonSeedKey and refuses anything else. func TestSetConfigSeedKey(t *testing.T) { r := newTestRepo(t) _, pub := signtest.SSHKey(t, "ed25519", clearsign.SSHNamespace) for _, ok := range []string{pub, "0123456789ABCDEF0123456789ABCDEF01234567", ""} { if err := r.SetConfig(SettingHorizonSeedKey, ok); err != nil { t.Errorf("SetConfig(%q): %v", ok, err) } } if err := r.SetConfig(SettingHorizonSeedKey, "not a key"); err == nil { t.Error("SetConfig accepted a value that is no key") } }