package store import "testing" // R-366 — a reinstall mints a NEW whole-guest backup key K (`felhom-pbs-apply`: `--encryption-key // autogen`) while, since R-241's mint guard, the box keeps its restic password. The escrow PUT then // carries the SAME restic sha and a NEW key fingerprint. Until this fix the supersession rule looked // at the restic sha only, so the row holding the OLD K (wrapped under the customer's recovery code) // was OVERWRITTEN — every whole-guest archive written before the reinstall became unopenable for // good, not only for the new box. The old row must be RETAINED when K changes. // // COMPANION RED-PROOF (observed, `audits/night-burndown-2026-10-06/r366/red-key-change.txt`): on the // pre-fix rule (`curSHA != resticPwSHA256` alone) this fails with "a new backup key with the same // restic password must supersede (retain the old K)". Restored. func TestSaveHostEscrow_R366_NewBackupKeySameResticPasswordRetainsOldKey(t *testing.T) { st := newTestStore(t) const h = "h1" if _, _, err := st.SaveHostEscrow(h, []byte("K-old-wrapped"), "3f:4f:65:c0", "zk", "2026-08-18T00:00:00Z", "SHA_SAME"); err != nil { t.Fatal(err) } sup, _, err := st.SaveHostEscrow(h, []byte("K-new-wrapped"), "dd:d1:d8:53", "zk", "2026-08-21T00:00:00Z", "SHA_SAME") if err != nil { t.Fatal(err) } if !sup { t.Fatal("a new backup key with the same restic password must supersede (retain the old K)") } old, err := st.ListSupersededEscrow(h) if err != nil { t.Fatal(err) } if len(old) != 1 || string(old[0].Blob) != "K-old-wrapped" || old[0].KeyFingerprint != "3f:4f:65:c0" { t.Fatalf("the old K is not retained: %+v", old) } cur, _ := st.GetHostEscrow(h) if cur == nil || string(cur.Blob) != "K-new-wrapped" || cur.KeyFingerprint != "dd:d1:d8:53" { t.Fatalf("the current row must be the new K: %+v", cur) } } // The same key in another letter case, or a legacy row with no fingerprint, is NOT a key change: // a re-upload of the same K must stay idempotent (no retained row per re-ceremony). func TestSaveHostEscrow_R366_SameKeyOrUnknownFingerprintDoesNotSupersede(t *testing.T) { st := newTestStore(t) const h = "h1" if _, _, err := st.SaveHostEscrow(h, []byte("a"), "AB:CD", "zk", "2026-08-18T00:00:00Z", "SHA"); err != nil { t.Fatal(err) } for i, fp := range []string{"ab:cd", " AB:CD ", ""} { sup, _, err := st.SaveHostEscrow(h, []byte("b"), fp, "zk", "2026-08-19T00:00:00Z", "SHA") if err != nil { t.Fatal(err) } if sup { t.Fatalf("case %d (%q): the same or an unknown fingerprint must not supersede", i, fp) } // keep the stored fingerprint known for the next case if _, _, err := st.SaveHostEscrow(h, []byte("a"), "AB:CD", "zk", "2026-08-18T00:00:00Z", "SHA"); err != nil { t.Fatal(err) } } if n, _ := st.CountSupersededEscrow(h); n != 0 { t.Fatalf("no key change happened, yet %d rows were retained", n) } }