| Crates.io | foodshare-crypto |
| lib.rs | foodshare-crypto |
| version | 1.3.1 |
| created_at | 2026-01-03 05:44:50.538663+00 |
| updated_at | 2026-01-03 05:44:50.538663+00 |
| description | Cryptographic utilities for webhook verification and HMAC |
| homepage | |
| repository | https://github.com/Foodshareclub/foodshare-tools |
| max_upload_size | |
| id | 2019636 |
| size | 14,043 |
Cryptographic utilities for webhook verification and HMAC signature generation.
Add to your Cargo.toml:
[dependencies]
foodshare-crypto = "1.3"
| Feature | Default | Description |
|---|---|---|
wasm |
No | Enable WebAssembly bindings |
use foodshare_crypto::{hmac_sha256, hmac_sha1};
// HMAC-SHA256 (Stripe, Meta)
let signature = hmac_sha256(b"your-secret-key", b"payload");
// HMAC-SHA1 (GitHub)
let signature = hmac_sha1(b"your-secret-key", b"payload");
use foodshare_crypto::{hmac_sha256, constant_time_compare};
fn verify_stripe_webhook(payload: &[u8], signature: &str, secret: &[u8]) -> bool {
let expected = hmac_sha256(secret, payload);
constant_time_compare(expected.as_bytes(), signature.as_bytes())
}
Always use constant-time comparison for security-sensitive operations:
use foodshare_crypto::constant_time_compare;
// Safe: takes the same time regardless of where strings differ
let is_valid = constant_time_compare(b"signature1", b"signature2");
use foodshare_crypto::{hmac_sha256, constant_time_compare};
fn verify_stripe(payload: &str, sig_header: &str, secret: &str) -> bool {
// Stripe sends: t=timestamp,v1=signature
let parts: Vec<&str> = sig_header.split(',').collect();
let timestamp = parts[0].strip_prefix("t=").unwrap();
let signature = parts[1].strip_prefix("v1=").unwrap();
let signed_payload = format!("{}.{}", timestamp, payload);
let expected = hmac_sha256(secret.as_bytes(), signed_payload.as_bytes());
constant_time_compare(expected.as_bytes(), signature.as_bytes())
}
use foodshare_crypto::{hmac_sha1, constant_time_compare};
fn verify_github(payload: &[u8], signature: &str, secret: &[u8]) -> bool {
// GitHub sends: sha1=signature
let sig = signature.strip_prefix("sha1=").unwrap_or(signature);
let expected = hmac_sha1(secret, payload);
constant_time_compare(expected.as_bytes(), sig.as_bytes())
}
use foodshare_crypto::{hmac_sha256, constant_time_compare};
fn verify_meta(payload: &[u8], signature: &str, secret: &[u8]) -> bool {
// Meta sends: sha256=signature
let sig = signature.strip_prefix("sha256=").unwrap_or(signature);
let expected = hmac_sha256(secret, payload);
constant_time_compare(expected.as_bytes(), sig.as_bytes())
}
== for signature comparisonFor browser/Deno usage, see @foodshare/crypto-wasm.
import init, { hmac_sha256_hex, verify_webhook_sha256 } from '@foodshare/crypto-wasm';
await init();
const signature = hmac_sha256_hex('secret', 'payload');
const isValid = verify_webhook_sha256('secret', 'payload', signature);
MIT License - see LICENSE for details.