fix(linq): accept prefixed and uppercase webhook signatures

This commit is contained in:
Chummy 2026-02-19 14:35:33 +08:00
parent 361e750576
commit bc0be9a3c1

View file

@ -347,10 +347,17 @@ pub fn verify_linq_signature(secret: &str, body: &str, timestamp: &str, signatur
return false; return false;
}; };
mac.update(message.as_bytes()); mac.update(message.as_bytes());
let expected = hex::encode(mac.finalize().into_bytes()); let signature_hex = signature
.trim()
.strip_prefix("sha256=")
.unwrap_or(signature);
let Ok(provided) = hex::decode(signature_hex.trim()) else {
tracing::warn!("Linq: invalid webhook signature format");
return false;
};
// Constant-time comparison // Constant-time comparison via HMAC verify.
crate::security::pairing::constant_time_eq(&expected, signature) mac.verify_slice(&provided).is_ok()
} }
#[cfg(test)] #[cfg(test)]
@ -587,6 +594,38 @@ mod tests {
); );
} }
#[test]
fn linq_signature_verification_accepts_sha256_prefix() {
let secret = "test_webhook_secret";
let body = r#"{"event_type":"message.received"}"#;
let now = chrono::Utc::now().timestamp().to_string();
use hmac::{Hmac, Mac};
use sha2::Sha256;
let message = format!("{now}.{body}");
let mut mac = Hmac::<Sha256>::new_from_slice(secret.as_bytes()).unwrap();
mac.update(message.as_bytes());
let signature = format!("sha256={}", hex::encode(mac.finalize().into_bytes()));
assert!(verify_linq_signature(secret, body, &now, &signature));
}
#[test]
fn linq_signature_verification_accepts_uppercase_hex() {
let secret = "test_webhook_secret";
let body = r#"{"event_type":"message.received"}"#;
let now = chrono::Utc::now().timestamp().to_string();
use hmac::{Hmac, Mac};
use sha2::Sha256;
let message = format!("{now}.{body}");
let mut mac = Hmac::<Sha256>::new_from_slice(secret.as_bytes()).unwrap();
mac.update(message.as_bytes());
let signature = hex::encode(mac.finalize().into_bytes()).to_ascii_uppercase();
assert!(verify_linq_signature(secret, body, &now, &signature));
}
#[test] #[test]
fn linq_parse_normalizes_phone_with_plus() { fn linq_parse_normalizes_phone_with_plus() {
let ch = LinqChannel::new( let ch = LinqChannel::new(