Commit
Author: Dirkjan Ochtman [dirkjan@ochtman.nl]
Hash: b52e6750cea090929555bc443273fb24e5470d94
Timestamp: Fri, 09 Dec 2022 16:34:43 +0000 (1 year ago)

+80 -62 +/-6 browse
Make VerifyingKeys responsible for input hashing
1diff --git a/src/arc/seal.rs b/src/arc/seal.rs
2index 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
24index 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
94index 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
198index 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
243index 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
283index 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 }