KMS: implement MessageType=DIGEST for Sign and Verify - #10202
Open
the-gabe wants to merge 4 commits into
Open
Conversation
RSAPrivateKey and ECDSAPrivateKey (and the AbstractPrivateKey contract) gain a message_type parameter. With "DIGEST" the message is treated as the already-computed hash and signed/verified via cryptography's Prehashed, after validating that its length matches the signing algorithm's hash — "the length of the Message value must match the length of hashed messages for the specified signing algorithm" (API_Sign.html), failing with AWS's wording: "Digest is invalid length for algorithm ...". The Verify path applies the same validation rather than quietly reporting an invalid signature. Defaults to "RAW", so behaviour is unchanged until callers pass the new parameter.
KmsBackend.sign/verify accept message_type and hand it to the key wrapper. Also drops the "MessageType-parameter DIGEST is not yet implemented" note from the Verify docstring, since the wrappers now implement it. Default stays "RAW"; the responses layer does not pass the parameter yet, so behaviour is still unchanged.
Both operations parsed MessageType and ignored it, so a DIGEST request had its digest hashed AGAIN — the signature verified against sha256(digest) instead of the digest, and any client signing with MessageType=DIGEST (the documented way to sign large messages) produced signatures that fail external verification against GetPublicKey while identical code works on AWS. The responses layer now validates the parameter against the real enum (RAW | DIGEST | EXTERNAL_MU per API_Sign.html — EXTERNAL_MU is a valid value but only meaningful for ML-DSA keys, which moto does not implement, and is rejected with a message that says so) and passes it through to the backend. The existing digest tests encoded the old behaviour and are corrected in the same commit: they signed DIGEST and verified RAW with the same bytes (which only passes when both sides re-hash), and paired SHA-256-sized digests with SHA_384/512 algorithms (which real KMS rejects on length). They now compute the digest with the signing algorithm's own hash and verify as DIGEST.
The contracts from API_Sign.html / API_Verify.html that would have caught the re-hashing behaviour: - RSASSA_PKCS1_V1_5 is deterministic, so sign(RAW, message) and sign(DIGEST, hash(message)) must be byte-identical. - "The message type does not need to be the same as the one used for signing": RAW-signed verifies as DIGEST and vice versa, across PSS, PKCS1v15 and ECDSA. - A DIGEST signature verifies OUTSIDE KMS with GetPublicKey's key over the original message. - A digest whose length does not match the signing algorithm's hash is rejected on both Sign and Verify with "Digest is invalid length for algorithm ...". - EXTERNAL_MU is accepted as an enum value and rejected as unsupported (ML-DSA keys), rather than being called invalid.
the-gabe
force-pushed
the
kms-sign-digest
branch
from
August 24, 2026 18:20
df039c2 to
96488ed
Compare
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.
Sign and Verify parsed MessageType and then ignored it (responses.py documented DIGEST as "not yet implemented"), so a DIGEST request had its digest hashed AGAIN. The resulting signature verifies against sha256(digest) instead of the digest — meaning any client that signs with MessageType=DIGEST (the documented way to sign large messages, and what real-world KMS JWT signers do) produced signatures that fail external verification against GetPublicKey, while identical code works on AWS.
Per API_Sign.html / API_Verify.html:
The existing digest tests encoded the old behaviour: they signed DIGEST and verified RAW with the same bytes (which only passes when both sides re-hash), and paired SHA-256-sized digests with SHA_384/512 algorithms (which real KMS rejects on length). They now hash with the signing algorithm's own hash and verify as DIGEST.
Not changed here: moto returns SignatureValid=false for a failed verification, where real KMS raises KMSInvalidSignatureException — a pre-existing, moto-wide divergence left for a separate discussion.