Commit
+80 -62 +/-6 browse
1 | diff --git a/src/arc/seal.rs b/src/arc/seal.rs |
2 | index b471559..1deea47 100644 |
3 | --- a/src/arc/seal.rs |
4 | +++ b/src/arc/seal.rs |
5 | @@ -99,7 +99,7 @@ impl<'x> ArcSet<'x> { |
6 | let mut header_hasher = Sha256::new(); |
7 | if !arc_output.set.is_empty() { |
8 | Canonicalization::Relaxed.canonicalize_headers( |
9 | - arc_output.set.iter().flat_map(|set| { |
10 | + &mut arc_output.set.iter().flat_map(|set| { |
11 | [ |
12 | (set.results.name, set.results.value), |
13 | (set.signature.name, set.signature.value), |
14 | @@ -194,7 +194,7 @@ impl<'x> Signature<'x> { |
15 | |
16 | let body_len = message.body.len(); |
17 | self.ch |
18 | - .canonicalize_headers(headers.into_iter().rev(), header_hasher)?; |
19 | + .canonicalize_headers(&mut headers.into_iter().rev(), header_hasher)?; |
20 | self.cb.canonicalize_body(message.body, body_hasher)?; |
21 | |
22 | // Add any missing headers |
23 | diff --git a/src/arc/verify.rs b/src/arc/verify.rs |
24 | index 28c9c65..b778424 100644 |
25 | --- a/src/arc/verify.rs |
26 | +++ b/src/arc/verify.rs |
27 | @@ -10,12 +10,9 @@ |
28 | |
29 | use std::time::SystemTime; |
30 | |
31 | - use sha1::Sha1; |
32 | - use sha2::Sha256; |
33 | - |
34 | use crate::{ |
35 | common::{ |
36 | - crypto::{Algorithm, HashAlgorithm}, |
37 | + crypto::HashAlgorithm, |
38 | headers::Header, |
39 | verify::{DomainKey, VerifySignature}, |
40 | }, |
41 | @@ -120,14 +117,7 @@ impl Resolver { |
42 | |
43 | // Hash headers |
44 | let dkim_hdr_value = header.value.strip_signature(); |
45 | - let headers = message.signed_headers(&signature.h, header.name, &dkim_hdr_value); |
46 | - let hh = match signature.a { |
47 | - Algorithm::RsaSha256 | Algorithm::Ed25519Sha256 => { |
48 | - signature.ch.hash_headers::<Sha256>(headers) |
49 | - } |
50 | - Algorithm::RsaSha1 => signature.ch.hash_headers::<Sha1>(headers), |
51 | - } |
52 | - .unwrap_or_default(); |
53 | + let mut headers = message.signed_headers(&signature.h, header.name, &dkim_hdr_value); |
54 | |
55 | // Obtain record |
56 | let record = match self.txt_lookup::<DomainKey>(signature.domain_key()).await { |
57 | @@ -138,7 +128,7 @@ impl Resolver { |
58 | }; |
59 | |
60 | // Verify signature |
61 | - if let Err(err) = signature.verify(record.as_ref(), &hh) { |
62 | + if let Err(err) = record.verify(&mut headers, *signature, signature.ch) { |
63 | return output.with_result(DkimResult::Fail(err)); |
64 | } |
65 | |
66 | @@ -156,7 +146,7 @@ impl Resolver { |
67 | |
68 | // Build Seal headers |
69 | let seal_signature = header.value.strip_signature(); |
70 | - let headers = output |
71 | + let mut headers = output |
72 | .set |
73 | .iter() |
74 | .take(pos) |
75 | @@ -173,16 +163,8 @@ impl Resolver { |
76 | (set.seal.name, &seal_signature), |
77 | ]); |
78 | |
79 | - let hh = match seal.a { |
80 | - Algorithm::RsaSha256 | Algorithm::Ed25519Sha256 => { |
81 | - Canonicalization::Relaxed.hash_headers::<Sha256>(headers) |
82 | - } |
83 | - Algorithm::RsaSha1 => Canonicalization::Relaxed.hash_headers::<Sha1>(headers), |
84 | - } |
85 | - .unwrap_or_default(); |
86 | - |
87 | // Verify ARC Seal |
88 | - if let Err(err) = seal.verify(record.as_ref(), &hh) { |
89 | + if let Err(err) = record.verify(&mut headers, *seal, Canonicalization::Relaxed) { |
90 | return output.with_result(DkimResult::Fail(err)); |
91 | } |
92 | } |
93 | diff --git a/src/common/crypto.rs b/src/common/crypto.rs |
94 | index b443c7d..e2c024a 100644 |
95 | --- a/src/common/crypto.rs |
96 | +++ b/src/common/crypto.rs |
97 | @@ -9,7 +9,7 @@ use rsa::{ |
98 | use sha1::{digest::Output, Sha1}; |
99 | use sha2::{digest::Digest, Sha256}; |
100 | |
101 | - use crate::{Error, Result}; |
102 | + use crate::{dkim::Canonicalization, Error, Result}; |
103 | |
104 | pub trait SigningKey { |
105 | type Hasher: Digest + AssociatedOid + io::Write; |
106 | @@ -112,7 +112,13 @@ impl SigningKey for Ed25519Key { |
107 | } |
108 | |
109 | pub trait VerifyingKey { |
110 | - fn verify(&self, hash: &[u8], signature: &[u8], algorithm: Algorithm) -> Result<()>; |
111 | + fn verify<'a>( |
112 | + &self, |
113 | + headers: &mut dyn Iterator<Item = (&'a [u8], &'a [u8])>, |
114 | + signature: &[u8], |
115 | + canonicalication: Canonicalization, |
116 | + algorithm: Algorithm, |
117 | + ) -> Result<()>; |
118 | } |
119 | |
120 | pub(crate) enum VerifyingKeyType { |
121 | @@ -144,21 +150,35 @@ pub(crate) struct RsaPublicKey { |
122 | } |
123 | |
124 | impl VerifyingKey for RsaPublicKey { |
125 | - fn verify(&self, hash: &[u8], signature: &[u8], algorithm: Algorithm) -> Result<()> { |
126 | + fn verify<'a>( |
127 | + &self, |
128 | + headers: &mut dyn Iterator<Item = (&'a [u8], &'a [u8])>, |
129 | + signature: &[u8], |
130 | + canonicalization: Canonicalization, |
131 | + algorithm: Algorithm, |
132 | + ) -> Result<()> { |
133 | match algorithm { |
134 | - Algorithm::RsaSha1 => self |
135 | - .inner |
136 | - .verify(PaddingScheme::new_pkcs1v15_sign::<Sha1>(), hash, signature) |
137 | - .map_err(|_| Error::FailedVerification), |
138 | - Algorithm::RsaSha256 => self |
139 | - .inner |
140 | - .verify( |
141 | - PaddingScheme::new_pkcs1v15_sign::<Sha256>(), |
142 | - hash, |
143 | - signature, |
144 | - ) |
145 | - .map_err(|_| Error::FailedVerification), |
146 | - _ => Err(Error::IncompatibleAlgorithms), |
147 | + Algorithm::RsaSha256 => { |
148 | + let hash = canonicalization |
149 | + .hash_headers::<Sha256>(headers) |
150 | + .unwrap_or_default(); |
151 | + self.inner |
152 | + .verify( |
153 | + PaddingScheme::new_pkcs1v15_sign::<Sha256>(), |
154 | + &hash, |
155 | + signature, |
156 | + ) |
157 | + .map_err(|_| Error::FailedVerification) |
158 | + } |
159 | + Algorithm::RsaSha1 => { |
160 | + let hash = canonicalization |
161 | + .hash_headers::<Sha1>(headers) |
162 | + .unwrap_or_default(); |
163 | + self.inner |
164 | + .verify(PaddingScheme::new_pkcs1v15_sign::<Sha1>(), &hash, signature) |
165 | + .map_err(|_| Error::FailedVerification) |
166 | + } |
167 | + Algorithm::Ed25519Sha256 => Err(Error::IncompatibleAlgorithms), |
168 | } |
169 | } |
170 | } |
171 | @@ -168,14 +188,23 @@ pub(crate) struct Ed25519PublicKey { |
172 | } |
173 | |
174 | impl VerifyingKey for Ed25519PublicKey { |
175 | - fn verify(&self, hash: &[u8], signature: &[u8], algorithm: Algorithm) -> Result<()> { |
176 | + fn verify<'a>( |
177 | + &self, |
178 | + headers: &mut dyn Iterator<Item = (&'a [u8], &'a [u8])>, |
179 | + signature: &[u8], |
180 | + canonicalization: Canonicalization, |
181 | + algorithm: Algorithm, |
182 | + ) -> Result<()> { |
183 | if !matches!(algorithm, Algorithm::Ed25519Sha256) { |
184 | return Err(Error::IncompatibleAlgorithms); |
185 | } |
186 | |
187 | + let hash = canonicalization |
188 | + .hash_headers::<Sha256>(headers) |
189 | + .unwrap_or_default(); |
190 | self.inner |
191 | .verify_strict( |
192 | - hash, |
193 | + &hash, |
194 | &ed25519_dalek::Signature::from_bytes(signature) |
195 | .map_err(|err| Error::CryptoError(err.to_string()))?, |
196 | ) |
197 | diff --git a/src/common/verify.rs b/src/common/verify.rs |
198 | index 891c74b..1a54302 100644 |
199 | --- a/src/common/verify.rs |
200 | +++ b/src/common/verify.rs |
201 | @@ -8,6 +8,8 @@ |
202 | * except according to those terms. |
203 | */ |
204 | |
205 | + use crate::dkim::Canonicalization; |
206 | + |
207 | use super::crypto::{Algorithm, VerifyingKey}; |
208 | |
209 | pub struct DomainKey { |
210 | @@ -15,6 +17,22 @@ pub struct DomainKey { |
211 | pub(crate) f: u64, |
212 | } |
213 | |
214 | + impl DomainKey { |
215 | + pub(crate) fn verify<'a>( |
216 | + &self, |
217 | + headers: &mut dyn Iterator<Item = (&'a [u8], &'a [u8])>, |
218 | + input: &impl VerifySignature, |
219 | + canonicalization: Canonicalization, |
220 | + ) -> crate::Result<()> { |
221 | + self.p.verify( |
222 | + headers, |
223 | + input.signature(), |
224 | + canonicalization, |
225 | + input.algorithm(), |
226 | + ) |
227 | + } |
228 | + } |
229 | + |
230 | pub(crate) trait VerifySignature { |
231 | fn selector(&self) -> &str; |
232 | |
233 | @@ -34,8 +52,4 @@ pub(crate) trait VerifySignature { |
234 | key.push('.'); |
235 | key |
236 | } |
237 | - |
238 | - fn verify(&self, record: &DomainKey, hh: &[u8]) -> crate::Result<()> { |
239 | - record.p.verify(hh, self.signature(), self.algorithm()) |
240 | - } |
241 | } |
242 | diff --git a/src/dkim/canonicalize.rs b/src/dkim/canonicalize.rs |
243 | index 7ed0383..bf727d3 100644 |
244 | --- a/src/dkim/canonicalize.rs |
245 | +++ b/src/dkim/canonicalize.rs |
246 | @@ -80,7 +80,7 @@ impl Canonicalization { |
247 | |
248 | pub fn canonicalize_headers<'x>( |
249 | &self, |
250 | - headers: impl Iterator<Item = (&'x [u8], &'x [u8])>, |
251 | + headers: &mut dyn Iterator<Item = (&'x [u8], &'x [u8])>, |
252 | mut hasher: impl io::Write, |
253 | ) -> io::Result<()> { |
254 | match self { |
255 | @@ -123,7 +123,7 @@ impl Canonicalization { |
256 | |
257 | pub fn hash_headers<'x, T>( |
258 | &self, |
259 | - headers: impl Iterator<Item = (&'x [u8], &'x [u8])>, |
260 | + headers: &mut dyn Iterator<Item = (&'x [u8], &'x [u8])>, |
261 | ) -> io::Result<Vec<u8>> |
262 | where |
263 | T: Digest + io::Write, |
264 | @@ -188,7 +188,7 @@ impl<'x> Signature<'x> { |
265 | .unwrap_or_default(); |
266 | let body_len = body.len(); |
267 | self.ch |
268 | - .canonicalize_headers(headers.into_iter().rev(), header_hasher)?; |
269 | + .canonicalize_headers(&mut headers.into_iter().rev(), header_hasher)?; |
270 | self.cb.canonicalize_body(body, body_hasher)?; |
271 | |
272 | // Add any missing headers |
273 | @@ -272,7 +272,7 @@ mod test { |
274 | let mut body = Vec::new(); |
275 | |
276 | canonicalization |
277 | - .canonicalize_headers(parsed_headers.clone().into_iter(), &mut headers) |
278 | + .canonicalize_headers(&mut parsed_headers.clone().into_iter(), &mut headers) |
279 | .unwrap(); |
280 | canonicalization |
281 | .canonicalize_body(raw_body, &mut body) |
282 | diff --git a/src/dkim/verify.rs b/src/dkim/verify.rs |
283 | index fa6fe65..2cb335c 100644 |
284 | --- a/src/dkim/verify.rs |
285 | +++ b/src/dkim/verify.rs |
286 | @@ -22,8 +22,8 @@ use crate::{ |
287 | }; |
288 | |
289 | use super::{ |
290 | - Algorithm, Atps, DomainKeyReport, Flag, HashAlgorithm, Signature, RR_DNS, RR_EXPIRATION, |
291 | - RR_OTHER, RR_SIGNATURE, RR_VERIFICATION, |
292 | + Atps, DomainKeyReport, Flag, HashAlgorithm, Signature, RR_DNS, RR_EXPIRATION, RR_OTHER, |
293 | + RR_SIGNATURE, RR_VERIFICATION, |
294 | }; |
295 | |
296 | impl Resolver { |
297 | @@ -108,17 +108,10 @@ impl Resolver { |
298 | |
299 | // Hash headers |
300 | let dkim_hdr_value = header.value.strip_signature(); |
301 | - let headers = message.signed_headers(&signature.h, header.name, &dkim_hdr_value); |
302 | - let hh = match signature.a { |
303 | - Algorithm::RsaSha256 | Algorithm::Ed25519Sha256 => { |
304 | - signature.ch.hash_headers::<Sha256>(headers) |
305 | - } |
306 | - Algorithm::RsaSha1 => signature.ch.hash_headers::<Sha1>(headers), |
307 | - } |
308 | - .unwrap_or_default(); |
309 | + let mut headers = message.signed_headers(&signature.h, header.name, &dkim_hdr_value); |
310 | |
311 | // Verify signature |
312 | - if let Err(err) = signature.verify(record.as_ref(), &hh) { |
313 | + if let Err(err) = record.verify(&mut headers, signature, signature.ch) { |
314 | output.push(DkimOutput::fail(err).with_signature(signature)); |
315 | continue; |
316 | } |