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