mail_auth/arc/
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 crate::{
8    common::{
9        crypto::Algorithm,
10        headers::{HeaderWriter, Writer},
11    },
12    dkim::Canonicalization,
13    AuthenticationResults,
14};
15
16use super::{ArcSet, ChainValidation, Seal, Signature};
17
18impl Signature {
19    pub(crate) fn write(&self, writer: &mut impl Writer, as_header: bool) {
20        let (header, new_line) = match self.ch {
21            Canonicalization::Relaxed if !as_header => (&b"arc-message-signature:"[..], &b" "[..]),
22            _ => (&b"ARC-Message-Signature: "[..], &b"\r\n\t"[..]),
23        };
24        writer.write(header);
25        writer.write(b"i=");
26        writer.write(self.i.to_string().as_bytes());
27        writer.write(b"; 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        writer.write(b";");
43        writer.write(new_line);
44
45        let mut bw = 1;
46        for (num, h) in self.h.iter().enumerate() {
47            if bw + h.len() + 1 >= 76 {
48                writer.write(new_line);
49                bw = 1;
50            }
51            if num > 0 {
52                writer.write_len(b":", &mut bw);
53            } else {
54                writer.write_len(b"h=", &mut bw);
55            }
56            writer.write_len(h.as_bytes(), &mut bw);
57        }
58
59        for (tag, value) in [
60            (&b"t="[..], self.t),
61            (&b"x="[..], self.x),
62            (&b"l="[..], self.l),
63        ] {
64            if value > 0 {
65                let value = value.to_string();
66                writer.write_len(b";", &mut bw);
67                if bw + tag.len() + value.len() >= 76 {
68                    writer.write(new_line);
69                    bw = 1;
70                } else {
71                    writer.write_len(b" ", &mut bw);
72                }
73
74                writer.write_len(tag, &mut bw);
75                writer.write_len(value.as_bytes(), &mut bw);
76            }
77        }
78
79        for (tag, value) in [(&b"; bh="[..], &self.bh), (&b"; b="[..], &self.b)] {
80            writer.write_len(tag, &mut bw);
81            for &byte in value {
82                writer.write_len(&[byte], &mut bw);
83                if bw >= 76 {
84                    writer.write(new_line);
85                    bw = 1;
86                }
87            }
88        }
89
90        writer.write(b";");
91        if as_header {
92            writer.write(b"\r\n");
93        }
94    }
95}
96
97impl Seal {
98    pub(crate) fn write(&self, writer: &mut impl Writer, as_header: bool) {
99        let (header, new_line) = if !as_header {
100            (&b"arc-seal:"[..], &b" "[..])
101        } else {
102            (&b"ARC-Seal: "[..], &b"\r\n\t"[..])
103        };
104
105        writer.write(header);
106        writer.write(b"i=");
107        writer.write(self.i.to_string().as_bytes());
108        writer.write(b"; a=");
109        writer.write(match self.a {
110            Algorithm::RsaSha256 => b"rsa-sha256",
111            Algorithm::RsaSha1 => b"rsa-sha1",
112            Algorithm::Ed25519Sha256 => b"ed25519-sha256",
113        });
114        for (tag, value) in [(&b"; s="[..], &self.s), (&b"; d="[..], &self.d)] {
115            writer.write(tag);
116            writer.write(value.as_bytes());
117        }
118        writer.write(b"; cv=");
119        writer.write(match self.cv {
120            ChainValidation::None => b"none",
121            ChainValidation::Fail => b"fail",
122            ChainValidation::Pass => b"pass",
123        });
124
125        writer.write(b";");
126        writer.write(new_line);
127
128        let mut bw = 1;
129        if self.t > 0 {
130            writer.write_len(b"t=", &mut bw);
131            writer.write_len(self.t.to_string().as_bytes(), &mut bw);
132            writer.write_len(b"; ", &mut bw);
133        }
134
135        writer.write_len(b"b=", &mut bw);
136        for &byte in &self.b {
137            writer.write_len(&[byte], &mut bw);
138            if bw >= 76 {
139                writer.write(new_line);
140                bw = 1;
141            }
142        }
143
144        writer.write(b";");
145        if as_header {
146            writer.write(b"\r\n");
147        }
148    }
149}
150
151impl AuthenticationResults<'_> {
152    pub(crate) fn write(&self, writer: &mut impl Writer, i: u32, as_header: bool) {
153        writer.write(if !as_header {
154            b"arc-authentication-results:"
155        } else {
156            b"ARC-Authentication-Results: "
157        });
158        writer.write(b"i=");
159        writer.write(i.to_string().as_bytes());
160        writer.write(b"; ");
161        writer.write(self.hostname.as_bytes());
162        if !as_header {
163            let mut last_is_space = false;
164            for &ch in self.auth_results.as_bytes() {
165                if !ch.is_ascii_whitespace() {
166                    if last_is_space {
167                        writer.write(b" ");
168                        last_is_space = false;
169                    }
170                    writer.write(&[ch]);
171                } else {
172                    last_is_space = true;
173                }
174            }
175        } else {
176            writer.write(self.auth_results.as_bytes());
177        }
178        writer.write(b"\r\n");
179    }
180}
181
182impl HeaderWriter for ArcSet<'_> {
183    fn write_header(&self, writer: &mut impl Writer) {
184        self.seal.write(writer, true);
185        self.signature.write(writer, true);
186        self.results.write(writer, self.seal.i, true);
187    }
188}