Mac, KeyRing and request signing (3.2) - #104
Merged
Merged
Conversation
A SharedKey could only encrypt. Mac authenticates a message with it, never using the key directly: the MAC key is HKDF-SHA256(key, info = Iridium|Mac|V1| + context), and the context is required, so one shared key can serve Crypt and several MAC purposes without one forging for another. The golden vectors were computed outside PHP. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
fromPairs() reads pairs from configuration and skips a slot that is not filled instead of holding an empty key, under which anyone could forge a MAC. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Authenticates method, path, timestamp and body without sending the secret. Plain strings in and out: no framework types, no fixed header names. The verifier checks the signature before the age of the timestamp, so a stale timestamp always describes a correctly signed request, and reports its reason through an enum meant for the log only. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.
What
Three additive modules, so a
SharedKeycan authenticate as well as encrypt. Nothing existing changes: 3.2.0, not 4.0.Mac— HMAC-SHA256, URL-safe Base64. The shared key is never used directly: the MAC key isHKDF-SHA256(raw key, empty salt, 32 bytes, info = "Iridium|Mac|V1|" + context), and the context is required. One key can serve Crypt and several MAC purposes without one being able to forge for another.Key\KeyRing— key ID →SharedKey, for rotation.fromPairs()reads pairs straight from configuration and skips a slot that is not filled instead of holding an empty key, under which anyone could forge a MAC.Request\RequestSigner/RequestVerifier— authenticate method, path, timestamp and body without ever sending the secret. Plain strings in and out: no framework types, no fixed header names.RequestVerificationFailure(enum) says why a request was refused, for the log only.Why
Split tokens are bearer tokens with a hashed verifier: right for many revocable per-user credentials, and unable by construction to verify an HMAC (the server never holds the secret). A credential both sides hold — a secret built into an app, or shared between two services — wants the opposite shape. Tessera's desktop API (plan 020) is the first consumer; its
ApiRequestAuthenticatorwas the reference implementation this was extracted from.Decisions worth a look
-v2context separates a future format at both layers.StaleTimestampalways means a correctly signed request from a skewed clock.0|[1-9]\d{0,9}): the text is signed as sent, so there is exactly one spelling of each instant.NonceStorageInterfacebesideTokenStorageInterfacewould be a natural 3.3.Base64::decode()is lenient about alphabet and padding, so a signature in standard Base64 that decodes to the right 32 bytes is accepted. Harmless (the bytes are what is compared), but noted.Wire contract
Specified in the README ("The Signed String") and pinned by
tests/fixtures/request-signing-vectors.json, computed outside PHP (Pythonhmac/hashlibwith a hand-written RFC 5869 HKDF, itself checked against RFC 5869 test case 3), so the PHP tests pit two implementations against each other.CLAUDE.mdrecords that the info prefix, the canonical string and the vectors are never edited in place.Checks
PHPUnit 131 tests green (62 new), Psalm level 1 clean, php-cs-fixer clean.
After merging: tag
v3.2.0.🤖 Generated with Claude Code