Skip to content

Fix/review findings - #1

Merged
grokify merged 5 commits into
mainfrom
fix/review-findings
Oct 25, 2026
Merged

grokify merged 5 commits into
mainfrom
fix/review-findings

Conversation

@grokify

@grokify grokify commented Oct 25, 2026

Copy link
Copy Markdown
Member

No description provided.

grokify and others added 5 commits October 24, 2026 07:28
The highest keyring generation seen was kept in plain JSON beside the
keyring, and a missing file was read as "nothing seen". Anyone able to
write the keys directory could put back an older, still validly sealed
keyring and delete or edit that file, and Open accepted the older
keyring without AcceptRollback, bringing back key versions that had
been destroyed or undoing a rotation.

The backup state file is now authenticated with HMAC-SHA-256 under a
key derived from the root key with HKDF, and bound to the keyring's
file name. Open refuses:

- a state file that does not authenticate (ErrCorruptBackupState);
- a missing state file beside a keyring past generation 1
  (ErrBackupStateMissing); generation 1 is still accepted, since a
  crash during Create leaves exactly that;
- a state file in the previous unauthenticated format
  (ErrUnauthenticatedBackupState).

Each can be accepted with AcceptRollback, which rewrites the state
authenticated and keeps any existing high-water mark and backup
acknowledgement. Root key rotation authenticates the state under both
the old and the new root key before replacing the keyring, so a crash
on either side leaves state the surviving keyring's root key opens.

BREAKING CHANGE: the first Open after upgrading fails with
ErrUnauthenticatedBackupState until it is done once with
AcceptRollback. Opening a keyring past generation 1 without its
backup state file, for example after moving it to a new host, also
needs AcceptRollback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Policy validation accepted a retained-versions floor of 1, which let a
deployment retire the previous key version and lose the rollback margin.
Validate now rejects RetainVersions below 2, for the default and for every
per-purpose override, so the active version and the previous one are always
kept and only N-2 and older can be retired.

A compromised version is still not counted by the floor and can be retired
and destroyed at once. Tests that retired version 1 under a floor of 1 now
rotate once more so that version 1 is N-2.

BREAKING CHANGE: a policy with RetainVersions below 2, in the default or in a
per-purpose override, is now rejected by Policy.Validate and Set.Validate, and
so by rotate.New. Set RetainVersions to 2 or more, or leave it zero to inherit
the default of 2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@grokify
grokify merged commit 779ed4a into main Oct 25, 2026
7 checks passed
@coderabbitai

coderabbitai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Caution

Review failed

The pull request is closed.

Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant