mini_mail_auth/dkim/
canonicalize.rs1use super::{Canonicalization, Signature};
2use crate::common::headers::{HeaderStream, Writable, Writer};
3
4pub struct CanonicalBody<'a> {
5 canonicalization: Canonicalization,
6 body: &'a [u8],
7}
8
9impl Writable for CanonicalBody<'_> {
10 fn write(self, hasher: &mut impl Writer) {
11 let mut crlf_seq = 0;
12 match self.canonicalization {
13 Canonicalization::Relaxed => {
14 let mut last_ch = 0;
15 let mut is_empty = true;
16 for &ch in self.body {
17 match ch {
18 b' ' | b'\t' => {
19 while crlf_seq > 0 {
20 hasher.write(b"\r\n");
21 crlf_seq -= 1;
22 }
23 is_empty = false;
24 }
25 b'\n' => crlf_seq += 1,
26 b'\r' => {}
27 _ => {
28 while crlf_seq > 0 {
29 hasher.write(b"\r\n");
30 crlf_seq -= 1;
31 }
32 if last_ch == b' ' || last_ch == b'\t' {
33 hasher.write(b" ");
34 }
35 hasher.write(&[ch]);
36 is_empty = false;
37 }
38 }
39 last_ch = ch;
40 }
41 if !is_empty {
42 hasher.write(b"\r\n");
43 }
44 }
45 Canonicalization::Simple => {
46 for &ch in self.body {
47 match ch {
48 b'\n' => crlf_seq += 1,
49 b'\r' => {}
50 _ => {
51 while crlf_seq > 0 {
52 hasher.write(b"\r\n");
53 crlf_seq -= 1;
54 }
55 hasher.write(&[ch]);
56 }
57 }
58 }
59 if crlf_seq == 0 && !self.body.is_empty() {
60 hasher.write(b"\r\n");
61 }
62 }
63 }
64 }
65}
66
67impl Canonicalization {
68 pub fn canonicalize_headers<'a>(
69 &self,
70 headers: impl Iterator<Item = (&'a [u8], &'a [u8])>,
71 hasher: &mut impl Writer,
72 ) {
73 match self {
74 Canonicalization::Relaxed => {
75 for (name, value) in headers {
76 for &ch in name {
77 if !ch.is_ascii_whitespace() {
78 hasher.write(&[ch.to_ascii_lowercase()]);
79 }
80 }
81 hasher.write(b":");
82 let mut bw = 0;
83 let mut last_ch = 0;
84 for &ch in value {
85 if !ch.is_ascii_whitespace() {
86 if [b' ', b'\t'].contains(&last_ch) && bw > 0 {
87 hasher.write_len(b" ", &mut bw);
88 }
89 hasher.write_len(&[ch], &mut bw);
90 }
91 last_ch = ch;
92 }
93 if last_ch == b'\n' {
94 hasher.write(b"\r\n");
95 }
96 }
97 }
98 Canonicalization::Simple => {
99 for (name, value) in headers {
100 hasher.write(name);
101 hasher.write(b":");
102 hasher.write(value);
103 }
104 }
105 }
106 }
107
108 pub fn canonical_headers<'a>(
109 &self,
110 headers: Vec<(&'a [u8], &'a [u8])>,
111 ) -> CanonicalHeaders<'a> {
112 CanonicalHeaders {
113 canonicalization: *self,
114 headers,
115 }
116 }
117
118 pub fn canonical_body<'a>(&self, body: &'a [u8], l: u64) -> CanonicalBody<'a> {
119 CanonicalBody {
120 canonicalization: *self,
121 body: if l == 0 || body.is_empty() {
122 body
123 } else {
124 &body[..std::cmp::min(l as usize, body.len())]
125 },
126 }
127 }
128
129 pub fn serialize_name(&self, writer: &mut impl Writer) {
130 writer.write(match self {
131 Canonicalization::Relaxed => b"relaxed",
132 Canonicalization::Simple => b"simple",
133 });
134 }
135}
136
137impl Signature {
138 pub fn canonicalize<'x>(
139 &self,
140 mut message: impl HeaderStream<'x>,
141 ) -> (usize, CanonicalHeaders<'x>, Vec<String>, CanonicalBody<'x>) {
142 let mut headers = Vec::with_capacity(self.h.len());
143 let mut found_headers = vec![false; self.h.len()];
144 let mut signed_headers = Vec::with_capacity(self.h.len());
145
146 while let Some((name, value)) = message.next_header() {
147 if let Some(pos) = self
148 .h
149 .iter()
150 .position(|header| name.eq_ignore_ascii_case(header.as_bytes()))
151 {
152 headers.push((name, value));
153 found_headers[pos] = true;
154 signed_headers.push(std::str::from_utf8(name).unwrap().into());
155 }
156 }
157
158 let body = message.body();
159 let body_len = body.len();
160 let canonical_headers = self.ch.canonical_headers(headers);
161 let canonical_body = self.ch.canonical_body(body, u64::MAX);
162
163 signed_headers.reverse();
164 for (header, found) in self.h.iter().zip(found_headers) {
165 if !found {
166 signed_headers.push(header.to_string());
167 }
168 }
169
170 (body_len, canonical_headers, signed_headers, canonical_body)
171 }
172}
173
174pub struct CanonicalHeaders<'a> {
175 canonicalization: Canonicalization,
176 headers: Vec<(&'a [u8], &'a [u8])>,
177}
178
179impl Writable for CanonicalHeaders<'_> {
180 fn write(self, writer: &mut impl Writer) {
181 self.canonicalization
182 .canonicalize_headers(self.headers.into_iter().rev(), writer)
183 }
184}