use crate::{
crypto::{self, HashAlgorithm, VerifyingKey},
header::HeaderFields,
message_hash, parse,
signature::DkimSignature,
verifier::VerificationError,
};
use std::{borrow::Cow, str};
use tracing::trace;
pub fn perform_verification(
headers: &HeaderFields,
verifying_key: &VerifyingKey,
sig: &DkimSignature,
name: &str,
value: &str,
) -> Result<(), VerificationError> {
let hash_alg = sig.algorithm.hash_algorithm();
let original_dkim_sig = make_original_dkim_sig(value);
let data_hash = message_hash::compute_data_hash(
hash_alg,
sig.canonicalization.header,
headers,
&sig.signed_headers,
name,
&original_dkim_sig,
);
let signature_data = &sig.signature_data;
verify_signature(verifying_key, hash_alg, &data_hash, signature_data)
}
fn make_original_dkim_sig(value: &str) -> Cow<'_, str> {
fn b_tag_prefix_len(val: &str) -> Option<usize> {
let (s, rest) = val.split_once('=')?;
if parse::trim_surrounding_fws(s) == "b" {
Some(val.len() - rest.len())
} else {
None
}
}
let mut val = Cow::from(value);
let mut last_i = 0;
let mut ms = val.match_indices(';');
loop {
match ms.next() {
Some((i, _)) => {
if let Some(n) = b_tag_prefix_len(&val[last_i..i]) {
val.to_mut().drain((last_i + n)..i);
break;
}
last_i = i + 1;
}
None => {
if last_i != val.len() {
if let Some(n) = b_tag_prefix_len(&val[last_i..]) {
val = value[..(last_i + n)].into();
}
}
break;
}
}
}
val
}
fn verify_signature(
key: &VerifyingKey,
hash_alg: HashAlgorithm,
data_hash: &[u8],
signature_data: &[u8],
) -> Result<(), VerificationError> {
match key {
VerifyingKey::Rsa(pk) => {
match crypto::verify_rsa(pk, hash_alg, data_hash, signature_data) {
Ok(()) => {
trace!("RSA public key verification successful");
Ok(())
}
Err(e) => {
trace!("RSA public key verification failed: {e}");
Err(VerificationError::VerificationFailure(crypto::VerificationError::VerificationFailure))
}
}
}
VerifyingKey::Ed25519(pk) => {
match crypto::verify_ed25519(pk, data_hash, signature_data) {
Ok(()) => {
trace!("Ed25519 public key verification successful");
Ok(())
}
Err(e) => {
trace!("Ed25519 public key verification failed: {e}");
Err(VerificationError::VerificationFailure(crypto::VerificationError::VerificationFailure))
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn make_original_dkim_sig_basic() {
let s = make_original_dkim_sig(" a = 1 ; b = 2 ; c = 3 ");
assert_eq!(s, " a = 1 ; b =; c = 3 ");
assert!(matches!(s, Cow::Owned(_)));
let s = make_original_dkim_sig(" a = 1 ; b = 2 ;");
assert_eq!(s, " a = 1 ; b =;");
assert!(matches!(s, Cow::Owned(_)));
let s = make_original_dkim_sig(" a = 1 ; b = 2 ");
assert_eq!(s, " a = 1 ; b =");
assert!(matches!(s, Cow::Borrowed(_)));
let s = make_original_dkim_sig(" a = 1 ; b =");
assert_eq!(s, " a = 1 ; b =");
assert!(matches!(s, Cow::Borrowed(_)));
}
}