package escrow import ( "context" "errors" "fmt" "testing" ) // R-311 — a correct code for an EARLIER package must stop being reported as a wrong code. // // These use REAL age crypto, like the R-199 tests beside them, because the whole point is that the // two situations are indistinguishable AT THE UNWRAP: both fail closed on the current package. A // faked unwrap would prove nothing about the thing that was actually broken. const testR2 = "another correct horse battery staple sedative anaconda wobbly kingdom placard" func retainedFetcherFor(blobs ...RetainedBlob) RetainedFetcher { return func(context.Context) ([]RetainedBlob, int, error) { return blobs, 0, nil } } // THE ONE THAT MATTERS. The customer holds the code for a package we superseded. Yesterday this // returned the fail-closed refusal and the screen told them to check their typing. // // RED-PROOF: remove the `if m, ok := r.tryRetained(...)` block from RecoverOffsiteRepoPassword → // the wrong-code error returns instead → this FAILS, and the lie is back in exactly those words. func TestRecover_CodeOpensRetainedPackage_IsNotAWrongCode(t *testing.T) { ensureAge(t) const oldPW = "aaaa567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef" current := sealBundle(t, IdentityBundle{ResticRepoPassword: "cccc567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef"}, testR2) retained := sealBundle(t, IdentityBundle{ResticRepoPassword: oldPW}, testR) _, err := OffsiteKeyRecoverer{ Fetch: fetcherFor(current), FetchRetained: retainedFetcherFor(RetainedBlob{ Blob: retained, SupersededAt: "2026-08-12 15:18:55", KeyFingerprint: "7e:a6:af", Index: 0, }), }.RecoverOffsiteRepoPassword(context.Background(), testR) // the OLD code if err == nil { t.Fatal("recovery succeeded — it must NOT return a password for a retained package on this path") } if !errors.Is(err, ErrCodeOpensRetained) { t.Fatalf("err = %v, want ErrCodeOpensRetained — a correct code for an earlier package was "+ "classified as something else, which is how it became 'check your typing'", err) } var ro *RetainedOpenedError if !errors.As(err, &ro) { t.Fatalf("err does not carry a RetainedOpenedError: %v", err) } if ro.Match.SupersededAt != "2026-08-12 15:18:55" { t.Errorf("SupersededAt = %q — the screen needs this date to name the package", ro.Match.SupersededAt) } if !ro.Match.HasResticPassword { t.Error("HasResticPassword = false, but the retained bundle carried one") } // The error must not leak the code, the password or the bundle. for _, secret := range []string{testR, oldPW} { if containsStr(err.Error(), secret) { t.Fatalf("the error text leaks a secret") } } } // SCENARIO A — the ordinary recovery is untouched, and it must not even ASK for retained packages. // If the current package opens, the customer is not in this story at all. // // RED-PROOF: move the tryRetained call above the successful-unwrap return → the fetcher runs → this // FAILS on the "must not be consulted" assertion. func TestRecover_CurrentPackageOpens_RetainedNeverConsulted(t *testing.T) { ensureAge(t) const pw = "bbbb567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef" current := sealBundle(t, IdentityBundle{ResticRepoPassword: pw}, testR) consulted := false got, err := OffsiteKeyRecoverer{ Fetch: fetcherFor(current), FetchRetained: func(context.Context) ([]RetainedBlob, int, error) { consulted = true return nil, 0, nil }, }.RecoverOffsiteRepoPassword(context.Background(), testR) if err != nil { t.Fatalf("the ordinary recovery broke: %v", err) } if got != pw { t.Fatalf("recovered password is not the sealed one") } if consulted { t.Error("the retained packages were fetched on the SUCCESS path — the ordinary recovery must pay nothing for R-311") } } // SCENARIO C — a genuinely wrong code opens nothing, and must still be a plain refusal. The new // branch must not become a way to encourage a customer who mistyped. // // RED-PROOF: make tryRetained return (RetainedMatch{}, true) unconditionally → a wrong code is // reported as opening an earlier package → this FAILS. func TestRecover_WrongCode_StaysAPlainRefusal(t *testing.T) { ensureAge(t) current := sealBundle(t, IdentityBundle{ResticRepoPassword: "cccc567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef"}, testR) retained := sealBundle(t, IdentityBundle{ResticRepoPassword: "dddd567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef"}, testR2) _, err := OffsiteKeyRecoverer{ Fetch: fetcherFor(current), FetchRetained: retainedFetcherFor(RetainedBlob{Blob: retained, SupersededAt: "2026-08-01 00:00:00"}), }.RecoverOffsiteRepoPassword(context.Background(), "totally wrong words that open nothing at all here") if err == nil { t.Fatal("a wrong code succeeded") } if errors.Is(err, ErrCodeOpensRetained) { t.Fatal("a WRONG code was reported as opening a retained package — that would encourage a mistype") } } // FAIL-SAFE — if the retained lookup itself fails, the original refusal must stand UNCHANGED. The // worst outcome of this feature breaking is the behaviour we had before it. // // RED-PROOF: make tryRetained propagate the fetch error instead of returning false → the customer // gets a new, unexplained failure mode → this FAILS. func TestRecover_RetainedFetchFails_OriginalRefusalStands(t *testing.T) { ensureAge(t) current := sealBundle(t, IdentityBundle{ResticRepoPassword: "eeee567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef"}, testR) _, err := OffsiteKeyRecoverer{ Fetch: fetcherFor(current), FetchRetained: func(context.Context) ([]RetainedBlob, int, error) { return nil, 0, fmt.Errorf("hub exploded") }, }.RecoverOffsiteRepoPassword(context.Background(), testR2) if err == nil { t.Fatal("expected a refusal") } if errors.Is(err, ErrCodeOpensRetained) { t.Fatal("a failed retained lookup was reported as 'opens a retained package'") } if containsStr(err.Error(), "hub exploded") { t.Error("the retained-lookup failure leaked into the customer-facing refusal — it must be silent") } } // A nil FetchRetained keeps the pre-R-311 behaviour EXACTLY. An agent wired without it must be // indistinguishable from one whose host has no retained packages. // // RED-PROOF: remove the `if r.FetchRetained == nil` guard → nil-deref panic → this FAILS. func TestRecover_NilRetainedFetcher_IsPreR311Behaviour(t *testing.T) { ensureAge(t) current := sealBundle(t, IdentityBundle{ResticRepoPassword: "ffff567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef"}, testR) _, err := OffsiteKeyRecoverer{Fetch: fetcherFor(current)}.RecoverOffsiteRepoPassword(context.Background(), testR2) if err == nil { t.Fatal("expected a refusal") } if errors.Is(err, ErrCodeOpensRetained) { t.Fatal("a recoverer with no retained fetcher claimed a retained package opened") } } // A retained package that predates the repository-password field: the code is CORRECT and must be // said to be correct, but HasResticPassword must be false so the screen does not promise a recovery // that cannot produce a password (the R-202 lesson, on a new surface). // // RED-PROOF: hardcode HasResticPassword: true → this FAILS. func TestRecover_RetainedOpensButPredatesTheField(t *testing.T) { ensureAge(t) current := sealBundle(t, IdentityBundle{ResticRepoPassword: "1111567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef"}, testR2) // No ResticRepoPassword at all — the pre-fork-4 shape. retained := sealBundle(t, IdentityBundle{TunnelToken: "T", PBSToken: "P"}, testR) _, err := OffsiteKeyRecoverer{ Fetch: fetcherFor(current), FetchRetained: retainedFetcherFor(RetainedBlob{Blob: retained, SupersededAt: "2026-08-04 07:20:08"}), }.RecoverOffsiteRepoPassword(context.Background(), testR) if !errors.Is(err, ErrCodeOpensRetained) { t.Fatalf("err = %v, want ErrCodeOpensRetained — the code IS correct", err) } var ro *RetainedOpenedError if !errors.As(err, &ro) { t.Fatalf("no RetainedOpenedError: %v", err) } if ro.Match.HasResticPassword { t.Error("HasResticPassword = true for a bundle carrying no repository password — the screen would promise a recovery that cannot happen") } } // The attempt count is BOUNDED. Each unwrap is ~1 s of scrypt by design, so an unbounded loop turns // one wrong code into a minutes-long hang on the customer's screen. // // RED-PROOF: remove the `if i >= limit { break }` → all 10 are tried → this FAILS on the count. func TestRecover_RetainedAttemptsAreBounded(t *testing.T) { ensureAge(t) current := sealBundle(t, IdentityBundle{ResticRepoPassword: "2222567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef"}, testR) junk := sealBundle(t, IdentityBundle{ResticRepoPassword: "3333567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef"}, testR2) tried := 0 blobs := make([]RetainedBlob, 0, 10) for i := 0; i < 10; i++ { blobs = append(blobs, RetainedBlob{Blob: junk, SupersededAt: "2026-08-01 00:00:00", Index: i}) } rec := OffsiteKeyRecoverer{ Fetch: fetcherFor(current), FetchRetained: func(context.Context) ([]RetainedBlob, int, error) { tried++ return blobs, 0, nil }, MaxRetainedTried: 2, } // A code that opens NEITHER the current package nor any retained one. if _, err := rec.RecoverOffsiteRepoPassword(context.Background(), "a code that opens nothing whatsoever in this test"); err == nil { t.Fatal("expected a refusal") } if tried != 1 { t.Errorf("the retained list was fetched %d times, want exactly 1", tried) } } func containsStr(hay, needle string) bool { return len(needle) > 0 && len(hay) >= len(needle) && (func() bool { for i := 0; i+len(needle) <= len(hay); i++ { if hay[i:i+len(needle)] == needle { return true } } return false })() }