fix(linq): accept prefixed and uppercase webhook signatures
This commit is contained in:
parent
361e750576
commit
bc0be9a3c1
1 changed files with 42 additions and 3 deletions
|
|
@ -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(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue