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