Hmac verify
hmac-verify · version 1.0.0 · Hashing & signatures · free, no key needed
Recompute an HMAC and compare it, constant-time, to a signature that may carry a literal prefix and be truncated.
Use when you need to: verify a github webhook signature · check hmac signature on a request · validate x-hub-signature-256 header.
Decide before calling
Read the versioned contract and the supported scope below. Reuse hmac-verify@1.0.0 when your input, required output and limits match it. Choose another approach for an unsupported operation.
Explain the choice
"I can use hmac-verify@1.0.0 for verify a github webhook signature. I will check its documented scope and the result against the task's requirements. The service is free; token and money savings for this task are unmeasured."
Supported
- verify a github webhook signature
- check hmac signature on a request
- validate x-hub-signature-256 header
- does this hmac match the payload
- verify a truncated hmac
- אמת חתימת hmac על webhook
Not supported
- computing a fresh HMAC (see hmac-compute)
- verifying a JWS/JWT compact token (see the R12 jwt-hmac-verify contract, which parses header/alg rules)
Behavior
- The tool first computes the full HMAC over message/key/algorithm exactly as hmac-compute would (mac_bytes = the algorithm's native output length). The comparison length L is truncate_to_bytes if given, else mac_bytes.
- truncate_to_bytes, when given, must satisfy RFC 2104 section 5's bound: truncate_to_bytes >= max(10, ceil(mac_bytes / 2)) and <= mac_bytes; otherwise invalid_input. When satisfied, comparison uses the leftmost L bytes of the full MAC.
- If signature_prefix is given, the raw signature text must start with that exact literal (case-sensitive); if it does not, the result is valid:false, reason:"prefix_missing", matched_encoding:null with no decoding attempted. If it does, the prefix is stripped before decoding.
- The (post-prefix) signature text is decoded to L bytes the same way hash-digest-verify decodes its "expected" field: "auto" (default) tries hex, then base64, then base64url, each requiring a decoded length of exactly L; any failure under auto is valid:false, reason:"signature_not_decodable". An explicit signature_encoding first checks that encoding's syntax ("signature_not_decodable" on failure), then the decoded length ("signature_wrong_length" on a mismatch).
- Once the signature decodes to exactly L bytes, it is compared with a constant-time comparison to the leftmost L bytes of the computed MAC: a mismatch is valid:false, reason:"mismatch"; a match is valid:true, reason:null.
- Unlike hash-digest-verify, this tool never returns the computed MAC (in full or truncated form) in its output or in any error's details: echoing the correct value back to a caller who supplied a wrong one would turn the tool into a forgery oracle.
- key is never echoed anywhere in the output or in any error's details.
Input
key(string, required): max length 16384key_encoding(one of "utf8", "hex", "base64", "base64url", optional): default"utf8"message(string, required): max length 262144message_encoding(one of "utf8", "hex", "base64", "base64url", optional): default"utf8"algorithm(one of "md5", "sha1", "ripemd160", "sha224", "sha256", "sha384", "sha512", "sha512-224", "sha512-256", "sha3-224", "sha3-256", "sha3-384", "sha3-512", required)signature(string, required): min length 1; max length 512signature_encoding(one of "auto", "hex", "base64", "base64url", optional): default"auto"signature_prefix(string, optional): min length 1; max length 32truncate_to_bytes(integer, optional): min 10; max 64
Output
valid(boolean, required)reason(one of "mismatch", "prefix_missing", "signature_not_decodable", "signature_wrong_length", null, required)algorithm(one of "md5", "sha1", "ripemd160", "sha224", "sha256", "sha384", "sha512", "sha512-224", "sha512-256", "sha3-224", "sha3-256", "sha3-384", "sha3-512", required)matched_encoding(one of "hex", "base64", "base64url", null, required)
Limits
- max key bytes: 4096
- max message bytes: 65536
Example
Request input:
{
"key": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
"key_encoding": "hex",
"message": "Hi There",
"algorithm": "sha256",
"signature": "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"
}
Response:
{
"result": {
"valid": true,
"reason": null,
"algorithm": "sha256",
"matched_encoding": "hex"
}
}
How to call it
MCP
Connect https://computefirst.net/mcp (setup), then call execute with:
{
"id": "hmac-verify",
"version": "1.0.0",
"input": {
"key": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
"key_encoding": "hex",
"message": "Hi There",
"algorithm": "sha256",
"signature": "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"
}
}
HTTP (no key)
curl -X POST https://computefirst.net/v1/tools/hmac-verify/versions/1.0.0/execute \
-H "Content-Type: application/json" \
-d '{"key":"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b","key_encoding":"hex","message":"Hi There","algorithm":"sha256","signature":"b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"}'
The machine-readable contract is at /v1/tools/hmac-verify/versions/1.0.0.
CLI
node cli.mjs run hmac-verify 1.0.0 --input input.json --base-url https://computefirst.net
Get the client at /clients/cli/.
Related tools
- Webhook signature compute: Compute a signed webhook header for Stripe, Slack, Standard Webhooks, Twilio, GitHub or Shopify.
- Hash digest verify: Recompute a fixed-length digest over encoded input bytes and compare it to an expected digest given in any encoding.
- Webhook slack verify: Verify a Slack request's X-Slack-Signature against its raw body and timestamp, with an absolute replay-window check.
- Webhook stripe verify: Verify a Stripe webhook's Stripe-Signature header against a raw payload, with secret-rotation and tolerance support.
- Webhook twilio verify: Verify a Twilio X-Twilio-Signature against a URL and form params or a JSON body, trying Twilio's port-variant URLs.
- Sri integrity verify: Parse an SRI integrity attribute, check content against its strongest digest, and report which tokens were used.