mail_auth/dkim/
headers.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::fmt::{Display, Formatter};
8
9use crate::common::headers::{HeaderWriter, Writer};
10
11use super::{Algorithm, Canonicalization, HashAlgorithm, Signature};
12
13impl Signature {
14    pub fn write(&self, writer: &mut impl Writer, as_header: bool) {
15        let (header, new_line) = match self.ch {
16            Canonicalization::Relaxed if !as_header => (&b"dkim-signature:"[..], &b" "[..]),
17            _ => (&b"DKIM-Signature: "[..], &b"\r\n\t"[..]),
18        };
19        writer.write(header);
20        writer.write(b"v=1; a=");
21        writer.write(match self.a {
22            Algorithm::RsaSha256 => b"rsa-sha256",
23            Algorithm::RsaSha1 => b"rsa-sha1",
24            Algorithm::Ed25519Sha256 => b"ed25519-sha256",
25        });
26        for (tag, value) in [(&b"; s="[..], &self.s), (&b"; d="[..], &self.d)] {
27            writer.write(tag);
28            writer.write(value.as_bytes());
29        }
30        writer.write(b"; c=");
31        self.ch.serialize_name(writer);
32        writer.write(b"/");
33        self.cb.serialize_name(writer);
34
35        if let Some(atps) = &self.atps {
36            writer.write(b"; atps=");
37            writer.write(atps.as_bytes());
38            writer.write(b"; atpsh=");
39            writer.write(match self.atpsh {
40                Some(HashAlgorithm::Sha256) => b"sha256",
41                Some(HashAlgorithm::Sha1) => b"sha1",
42                _ => b"none",
43            });
44        }
45        if self.r {
46            writer.write(b"; r=y");
47        }
48
49        writer.write(b";");
50        writer.write(new_line);
51
52        let mut bw = 1;
53        for (num, h) in self.h.iter().enumerate() {
54            if bw + h.len() + 1 >= 76 {
55                writer.write(new_line);
56                bw = 1;
57            }
58            if num > 0 {
59                writer.write_len(b":", &mut bw);
60            } else {
61                writer.write_len(b"h=", &mut bw);
62            }
63            writer.write_len(h.as_bytes(), &mut bw);
64        }
65
66        if !self.i.is_empty() {
67            if bw + self.i.len() + 3 >= 76 {
68                writer.write(b";");
69                writer.write(new_line);
70                bw = 1;
71            } else {
72                writer.write_len(b"; ", &mut bw);
73            }
74            writer.write_len(b"i=", &mut bw);
75
76            for &ch in self.i.as_bytes().iter() {
77                match ch {
78                    0..=0x20 | b';' | 0x7f..=u8::MAX => {
79                        writer.write_len(format!("={ch:02X}").as_bytes(), &mut bw);
80                    }
81                    _ => {
82                        writer.write_len(&[ch], &mut bw);
83                    }
84                }
85                if bw >= 76 {
86                    writer.write(new_line);
87                    bw = 1;
88                }
89            }
90        }
91
92        for (tag, value) in [
93            (&b"t="[..], self.t),
94            (&b"x="[..], self.x),
95            (&b"l="[..], self.l),
96        ] {
97            if value > 0 {
98                let value = value.to_string();
99                writer.write_len(b";", &mut bw);
100                if bw + tag.len() + value.len() >= 76 {
101                    writer.write(new_line);
102                    bw = 1;
103                } else {
104                    writer.write_len(b" ", &mut bw);
105                }
106
107                writer.write_len(tag, &mut bw);
108                writer.write_len(value.as_bytes(), &mut bw);
109            }
110        }
111
112        for (tag, value) in [(&b"; bh="[..], &self.bh), (&b"; b="[..], &self.b)] {
113            writer.write_len(tag, &mut bw);
114            for &byte in value {
115                writer.write_len(&[byte], &mut bw);
116                if bw >= 76 {
117                    writer.write(new_line);
118                    bw = 1;
119                }
120            }
121        }
122
123        writer.write(b";");
124        if as_header {
125            writer.write(b"\r\n");
126        }
127    }
128}
129
130impl HeaderWriter for Signature {
131    fn write_header(&self, writer: &mut impl Writer) {
132        self.write(writer, true);
133    }
134}
135
136impl Display for Signature {
137    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
138        let mut buf = Vec::new();
139        self.write(&mut buf, false);
140        f.write_str(&String::from_utf8_lossy(&buf))
141    }
142}