makegov-tango-webhooks 0.1.0

HMAC-SHA256 signing and verification for Tango webhook deliveries
Documentation

HMAC-SHA256 signing and verification for Tango webhook deliveries.

Tango signs each webhook delivery with an HTTP header of the form:

X-Tango-Signature: sha256=<lowercase hex HMAC-SHA256 of raw body>

The signature is computed over the raw request body bytes, keyed by the endpoint's secret. Verify against the bytes you received off the wire — re-serializing a parsed JSON document will produce a different signature because of whitespace, key ordering, and float formatting differences.

This crate has no transport dependency. It pulls in only hmac, sha2, subtle, and hex, so a webhook receiver can verify deliveries without linking the full SDK.

Quick start

use tango_webhooks::{generate, verify, SIGNATURE_HEADER};

let body = br#"{"event_type":"alerts.contract.match"}"#;
let secret = "topsecret";

// Server side (or in tests): produce a signature for `body`.
let header = generate(body, secret);
assert!(header.starts_with("sha256="));

// Receiver side: verify the header you read off the request.
assert!(verify(body, &header, secret));
assert!(!verify(body, &header, "wrong-secret"));

// The canonical header name to look up on the request:
assert_eq!(SIGNATURE_HEADER, "X-Tango-Signature");

Constant-time comparison

[verify] decodes both signatures to bytes and compares them with [subtle::ConstantTimeEq]. The comparison does not short-circuit on the first differing byte, which protects against timing-based signature recovery attacks.

Why no axum/actix middleware?

Transport adapters live behind cargo features added in a later release. This crate intentionally stays tiny — a verifier service depends on tango-webhooks alone, not the full SDK.