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