Skip to main content

mail_auth/common/
verify.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7use super::{
8    cache::NoCache,
9    crypto::{Algorithm, VerifyingKey},
10};
11use crate::{
12    Error, IprevOutput, IprevResult, MX, MessageAuthenticator, Parameters, RecordSet,
13    ResolverCache, Txt, dkim::Canonicalization,
14};
15use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
16
17pub struct DomainKey {
18    pub p: Box<dyn VerifyingKey + Send + Sync>,
19    pub f: u64,
20}
21
22impl MessageAuthenticator {
23    pub async fn verify_iprev<'x, TXT, MXX, IPV4, IPV6, PTR>(
24        &self,
25        params: impl Into<Parameters<'x, IpAddr, TXT, MXX, IPV4, IPV6, PTR>>,
26    ) -> IprevOutput
27    where
28        TXT: ResolverCache<Box<str>, Txt> + 'x,
29        MXX: ResolverCache<Box<str>, RecordSet<MX>> + 'x,
30        IPV4: ResolverCache<Box<str>, RecordSet<Ipv4Addr>> + 'x,
31        IPV6: ResolverCache<Box<str>, RecordSet<Ipv6Addr>> + 'x,
32        PTR: ResolverCache<IpAddr, RecordSet<Box<str>>> + 'x,
33    {
34        let params = params.into();
35        match self.ptr_lookup(params.params, params.cache_ptr).await {
36            Ok(ptr) => {
37                let mut last_err = None;
38                for host in ptr.rrset.iter().take(2) {
39                    match &params.params {
40                        IpAddr::V4(ip) => match self.ipv4_lookup(host, params.cache_ipv4).await {
41                            Ok(ips) => {
42                                if ips.rrset.iter().any(|cip| cip == ip) {
43                                    return IprevOutput {
44                                        result: IprevResult::Pass,
45                                        ptr: Some(ptr.rrset.clone()),
46                                    };
47                                }
48                            }
49                            Err(err) => {
50                                last_err = err.into();
51                            }
52                        },
53                        IpAddr::V6(ip) => match self.ipv6_lookup(host, params.cache_ipv6).await {
54                            Ok(ips) => {
55                                if ips.rrset.iter().any(|cip| cip == ip) {
56                                    return IprevOutput {
57                                        result: IprevResult::Pass,
58                                        ptr: Some(ptr.rrset.clone()),
59                                    };
60                                }
61                            }
62                            Err(err) => {
63                                last_err = err.into();
64                            }
65                        },
66                    }
67                }
68
69                IprevOutput {
70                    result: if let Some(err) = last_err {
71                        err.into()
72                    } else {
73                        IprevResult::Fail(Error::NotAligned)
74                    },
75                    ptr: Some(ptr.rrset.clone()),
76                }
77            }
78            Err(err) => IprevOutput {
79                result: err.into(),
80                ptr: None,
81            },
82        }
83    }
84}
85
86impl From<IpAddr>
87    for Parameters<
88        '_,
89        IpAddr,
90        NoCache<Box<str>, Txt>,
91        NoCache<Box<str>, RecordSet<MX>>,
92        NoCache<Box<str>, RecordSet<Ipv4Addr>>,
93        NoCache<Box<str>, RecordSet<Ipv6Addr>>,
94        NoCache<IpAddr, RecordSet<Box<str>>>,
95    >
96{
97    fn from(params: IpAddr) -> Self {
98        Parameters::new(params)
99    }
100}
101
102impl IprevOutput {
103    pub fn result(&self) -> &IprevResult {
104        &self.result
105    }
106}
107
108impl DomainKey {
109    pub(crate) fn verify<'a>(
110        &self,
111        headers: &mut dyn Iterator<Item = (&'a [u8], &'a [u8])>,
112        input: &impl VerifySignature,
113        canonicalization: Canonicalization,
114    ) -> crate::Result<()> {
115        self.p.verify(
116            headers,
117            input.signature(),
118            canonicalization,
119            input.algorithm(),
120        )
121    }
122}
123
124pub trait VerifySignature {
125    fn selector(&self) -> &str;
126
127    fn domain(&self) -> &str;
128
129    fn signature(&self) -> &[u8];
130
131    fn algorithm(&self) -> Algorithm;
132
133    fn domain_key(&self) -> String {
134        let s = self.selector();
135        let d = self.domain();
136        let mut key = String::with_capacity(s.len() + d.len() + 13);
137        key.push_str(s);
138        key.push_str("._domainkey.");
139        key.push_str(d);
140        key.push('.');
141        key
142    }
143}