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