feat: replace HS256 with EdDSA keystore signing (ROC-161) - #37
Open
Arminek wants to merge 1 commit into
Open
Conversation
Tokens were signed with a shared HS256 secret, so consumers could not verify them without holding it. Keys now live in the database sealed with AES-256-GCM under KEY_ENCRYPTION_KEY, are auto-generated on first boot (race-safe across replicas via a unique-active-key constraint), and every JWT carries an RFC 7638 kid used to select the verification key. Retired keys surface only as verifying keys, so signing with one is unrepresentable. SECRET is removed from code, env files, compose, CI, and terraform.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
KEY_ENCRYPTION_KEY(base64, 32 bytes); public keys stored in the clear for later JWKS publication (ROC-162)kid(RFC 7638 JWK thumbprint); verification selects the key bykid; unknownkidis rejectedSigningKey, retired records surface only asVerifyingKeySECRETremoved from code, env files, docker-compose, CI workflows, README, and terraform; missingKEY_ENCRYPTION_KEYfails startup, wrong KEK fails on first key accessScope note
The issue called for squashing migrations to a fresh baseline. Decided during implementation to keep migration history and append
signing_keysinstead (recorded in a ROC-161 comment).Testing
cargo testin Docker: 178/178 on MySQL, 178/178 on SQLite (16 new tests: keystore unit, envelope crypto, token kid/unknown-kid, keystore init incl. concurrent-boot convergence and wrong-KEK failure)