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
7//! Serialization of a DKIM1 [`Signature`] as a `DKIM-Signature` header.
8
9use super::{Canonicalization, HashAlgorithm, Signature};
10use crate::{
11    headers::{HEADER_CAPACITY, HeaderWriter, IntegerBuffer, Writer},
12    utf8::to_str_lossy,
13};
14use encodify::{Fold, base64, qp};
15use std::fmt::{Display, Formatter};
16
17impl Signature {
18    /// Writes the signature as a `DKIM-Signature` header.
19    ///
20    /// With `as_header` set, writes `DKIM-Signature: <tags>;\r\n`, folded
21    /// with `\r\n\t`, ready to prepend to a message; this is what
22    /// [`HeaderWriter::write_header`] does. With `as_header` unset, writes the
23    /// form hashed during signing: no trailing CRLF and, under relaxed header
24    /// canonicalization, a lowercase `dkim-signature:` name folded with
25    /// single spaces. Tags with default values (`i=`, `t=`, `x=`, `l=`,
26    /// `r=`, `atps=`) are omitted.
27    pub fn write(&self, writer: &mut impl Writer, as_header: bool) {
28        let (header, new_line) = match self.ch {
29            Canonicalization::Relaxed if !as_header => (&b"dkim-signature:"[..], &b" "[..]),
30            _ => (&b"DKIM-Signature: "[..], &b"\r\n\t"[..]),
31        };
32        let fold = Fold {
33            separator: new_line,
34            ..Fold::DKIM
35        };
36        writer.write(header);
37        writer.write(b"v=1; a=");
38        writer.write(self.a.name().as_bytes());
39        for (tag, value) in [(&b"; s="[..], &self.s), (&b"; d="[..], &self.d)] {
40            writer.write(tag);
41            writer.write(value.as_bytes());
42        }
43        writer.write(b"; c=");
44        self.ch.serialize_name(writer);
45        writer.write(b"/");
46        self.cb.serialize_name(writer);
47
48        if let Some(atps) = &self.atps {
49            writer.write(b"; atps=");
50            writer.write(atps.as_bytes());
51            writer.write(b"; atpsh=");
52            writer.write(match self.atpsh {
53                Some(HashAlgorithm::Sha256) => b"sha256",
54                Some(HashAlgorithm::Sha1) => b"sha1",
55                Some(HashAlgorithm::Sha512) | None => b"none",
56            });
57        }
58        if self.r {
59            writer.write(b"; r=y");
60        }
61
62        writer.write(b";");
63        writer.write(new_line);
64
65        let mut bw = 1;
66        for (num, h) in self.h.iter().enumerate() {
67            if bw + h.len() + 1 >= 76 {
68                writer.write(new_line);
69                bw = 1;
70            }
71            if num > 0 {
72                writer.write_len(b":", &mut bw);
73            } else {
74                writer.write_len(b"h=", &mut bw);
75            }
76            writer.write_len(h.as_bytes(), &mut bw);
77        }
78
79        if !self.i.is_empty() {
80            if bw + self.i.len() + 3 >= 76 {
81                writer.write(b";");
82                writer.write(new_line);
83                bw = 1;
84            } else {
85                writer.write_len(b"; ", &mut bw);
86            }
87            writer.write_len(b"i=", &mut bw);
88            qp::DKIM.encode_folded(&self.i, &mut bw, fold, |piece| writer.write(piece));
89        }
90
91        let mut integer = IntegerBuffer::new();
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 = integer.digits(value);
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, &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            base64::STANDARD.encode_folded(value, &mut bw, fold, |piece| writer.write(piece));
115        }
116
117        writer.write(b";");
118        if as_header {
119            writer.write(b"\r\n");
120        }
121    }
122}
123
124impl HeaderWriter for Signature {
125    fn write_header(&self, writer: &mut impl Writer) {
126        self.write(writer, true);
127    }
128}
129
130impl Display for Signature {
131    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
132        let mut buf = Vec::with_capacity(HEADER_CAPACITY);
133        self.write(&mut buf, false);
134        f.write_str(&to_str_lossy(&buf))
135    }
136}