Skip to main content

cloudconvert_sdk/
webhook.rs

1//! Webhook payload signing and signature verification helpers.
2//!
3//! CloudConvert signs webhook bodies with the webhook signing secret. These
4//! helpers compute the expected HMAC-SHA256 hex digest for outbound tests and
5//! verify inbound `CloudConvert-Signature` header values.
6
7use hmac::{Hmac, KeyInit, Mac};
8use sha2::Sha256;
9
10use crate::{Error, Result, SigningSecret};
11
12type HmacSha256 = Hmac<Sha256>;
13
14/// Returns `true` when `signature_hex` matches the HMAC-SHA256 digest of `payload`.
15pub fn verify_signature(
16    payload: impl AsRef<[u8]>,
17    signature_hex: impl AsRef<str>,
18    signing_secret: &SigningSecret,
19) -> Result<bool> {
20    let expected = hex::decode(signature_hex.as_ref()).map_err(|_| Error::InvalidSignatureHex)?;
21    let mut mac = HmacSha256::new_from_slice(signing_secret.expose().as_bytes())
22        .map_err(|_| Error::InvalidSignatureHex)?;
23    mac.update(payload.as_ref());
24    Ok(mac.verify_slice(&expected).is_ok())
25}
26
27/// Computes the HMAC-SHA256 hex digest CloudConvert uses for webhook payloads.
28pub fn sign_payload(payload: impl AsRef<[u8]>, signing_secret: &SigningSecret) -> Result<String> {
29    let mut mac = HmacSha256::new_from_slice(signing_secret.expose().as_bytes())
30        .map_err(|_| Error::InvalidSignatureHex)?;
31    mac.update(payload.as_ref());
32    Ok(hex::encode(mac.finalize().into_bytes()))
33}