You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We (Cursor) want to adhere to the Standard Webhooks spec for asymmetric signing, but v1a cannot be implemented with KMS-held keys, so we had to diverge in production. Proposal: add a pre-hashed variant of the asymmetric signature scheme — sign sha256(msg_id.timestamp.payload) instead of the raw string — so producers whose signing keys live in a KMS/HSM can comply with the spec.
Why v1a can't be implemented with KMS-held keys
v1a signs the raw msg_id.timestamp.payload string (Signature scheme) with pure Ed25519 (RFC 8032), which requires the full message — there is no standard way to hand it a precomputed digest. Managed key services cap that input:
AWS KMS: Sign accepts messages of 0–4096 bytes, and the pure-Ed25519 algorithm (ED25519_SHA_512) requires MessageType: RAW — signed content above 4096 bytes cannot be signed at all. The pre-hashed algorithm KMS offers instead (ED25519_PH_SHA_512) is Ed25519ph, whose signatures deliberately do not verify as pure Ed25519 (RFC 8032 dom2 domain separation), so it is not v1a-compatible either.
Google Cloud KMS: EC_SIGN_ED25519 is PureEdDSA and takes only the raw data field, subject to request-size limits; there is no Ed25519ph offering.
Pure-mode CKM_EDDSA on PKCS#11 HSMs likewise needs the full message delivered to the token.
The spec recommends payloads of up to ~20kb (Payload size), so ordinary in-spec payloads already exceed the AWS cap — and non-extractable KMS keys are exactly the custody model that motivated asymmetric signatures in #25 / #28. AWS KMS only added Ed25519 support in November 2025, so more producers are about to hit this.
The scheme we run in production
We hold Ed25519 keys in a KMS and sign the digest of the standard signed content:
content = "{msg_id}.{timestamp}.{payload}" # same content string as v1/v1a
signature = ed25519_sign(kms_key, sha256(content)) # pure Ed25519 over a fixed-size input
header = "webhook-signature: <scheme-id>,{base64(signature)}"
Receivers recompute the digest and verify pure Ed25519 over it. This works with any KMS pure-mode signer at any payload size. We emit it under a version identifier distinct from v1a, since the two schemes don't inter-verify; spec-compliant consumers skip unknown identifiers (we verified the JavaScript, Python, and Go reference libraries all do), so it coexists with standard signatures in a single webhook-signature header.
Key handling also diverges from the spec's whsk_/whpk_ model, and the addition should cover it. KMS private keys are non-extractable, so a whsk_ string never exists, and one shared key set serves all consumers, with public keys distributed out of band rather than as per-consumer whpk_ strings. A shared key cannot by itself prove a delivery was addressed to the verifying consumer — the cross-tenant replay concern #34 raises — so the guidance would also need to cover consumer binding.
Known trade-off, stated for the record: hashing before signing gives up PureEdDSA's collision resilience (RFC 8032 §4) and rests on the digest's collision resistance — the same posture as Ed25519ph, ECDSA, and RSA, all of which sign digests. The proposal is also complementary to #34: the construction proposed there likewise has a fixed-size asymmetric signing input.
Ask
Can this divergence be added to the spec? Concretely, the addition would be:
A pre-hashed asymmetric signature scheme with its own signature identifier: sign the SHA-256 digest of the standard content string with pure Ed25519.
Key-handling guidance for KMS-held, shared keys: no whsk_ serialization, out-of-band public-key distribution, and the consumer-binding check shared keys require.
We understand the RFC process needs a champion per CONTRIBUTING.md — we're happy to help where we can with spec text and review.
We (Cursor) want to adhere to the Standard Webhooks spec for asymmetric signing, but
v1acannot be implemented with KMS-held keys, so we had to diverge in production. Proposal: add a pre-hashed variant of the asymmetric signature scheme — signsha256(msg_id.timestamp.payload)instead of the raw string — so producers whose signing keys live in a KMS/HSM can comply with the spec.Why
v1acan't be implemented with KMS-held keysv1asigns the rawmsg_id.timestamp.payloadstring (Signature scheme) with pure Ed25519 (RFC 8032), which requires the full message — there is no standard way to hand it a precomputed digest. Managed key services cap that input:Signaccepts messages of 0–4096 bytes, and the pure-Ed25519 algorithm (ED25519_SHA_512) requiresMessageType: RAW— signed content above 4096 bytes cannot be signed at all. The pre-hashed algorithm KMS offers instead (ED25519_PH_SHA_512) is Ed25519ph, whose signatures deliberately do not verify as pure Ed25519 (RFC 8032dom2domain separation), so it is notv1a-compatible either.EC_SIGN_ED25519is PureEdDSA and takes only the rawdatafield, subject to request-size limits; there is no Ed25519ph offering.CKM_EDDSAon PKCS#11 HSMs likewise needs the full message delivered to the token.The spec recommends payloads of up to ~20kb (Payload size), so ordinary in-spec payloads already exceed the AWS cap — and non-extractable KMS keys are exactly the custody model that motivated asymmetric signatures in #25 / #28. AWS KMS only added Ed25519 support in November 2025, so more producers are about to hit this.
The scheme we run in production
We hold Ed25519 keys in a KMS and sign the digest of the standard signed content:
Receivers recompute the digest and verify pure Ed25519 over it. This works with any KMS pure-mode signer at any payload size. We emit it under a version identifier distinct from
v1a, since the two schemes don't inter-verify; spec-compliant consumers skip unknown identifiers (we verified the JavaScript, Python, and Go reference libraries all do), so it coexists with standard signatures in a singlewebhook-signatureheader.Key handling also diverges from the spec's
whsk_/whpk_model, and the addition should cover it. KMS private keys are non-extractable, so awhsk_string never exists, and one shared key set serves all consumers, with public keys distributed out of band rather than as per-consumerwhpk_strings. A shared key cannot by itself prove a delivery was addressed to the verifying consumer — the cross-tenant replay concern #34 raises — so the guidance would also need to cover consumer binding.Known trade-off, stated for the record: hashing before signing gives up PureEdDSA's collision resilience (RFC 8032 §4) and rests on the digest's collision resistance — the same posture as Ed25519ph, ECDSA, and RSA, all of which sign digests. The proposal is also complementary to #34: the construction proposed there likewise has a fixed-size asymmetric signing input.
Ask
Can this divergence be added to the spec? Concretely, the addition would be:
whsk_serialization, out-of-band public-key distribution, and the consumer-binding check shared keys require.We understand the RFC process needs a champion per CONTRIBUTING.md — we're happy to help where we can with spec text and review.
Prior art we checked before filing
wh*_prefix, key discovery, questionable cryptography recommendations #25 / Temporarily remove references to asymmetric signatures #28 / spec: update signature schemes #29 — HSM/KMS key custody motivates asymmetric signatures (and spec: signing secrets, encoding,wh*_prefix, key discovery, questionable cryptography recommendations #25 floats JWKS-based key discovery), but signing-input size caps are never discussed.v1a; both implement/track signing the raw content, which precludes KMS-held keys for larger payloads.