Skip to main content

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