Commit
Author: Dirkjan Ochtman [dirkjan@ochtman.nl]
Hash: db5228fb5dcd6a0262362985cb2df4bc97d9bfba
Timestamp: Wed, 14 Dec 2022 13:44:17 +0000 (1 year ago)

+85 -63 +/-10 browse
Add indirection to abstract over hashing algorithm implementation
1diff --git a/examples/arc_seal.rs b/examples/arc_seal.rs
2index 1c31c3c..f6f84c0 100644
3--- a/examples/arc_seal.rs
4+++ b/examples/arc_seal.rs
5 @@ -10,10 +10,12 @@
6
7 use mail_auth::{
8 arc::ArcSealer,
9- common::{crypto::RsaKey, headers::HeaderWriter},
10+ common::{
11+ crypto::{RsaKey, Sha256},
12+ headers::HeaderWriter,
13+ },
14 AuthenticatedMessage, AuthenticationResults, Resolver,
15 };
16- use sha2::Sha256;
17
18 const TEST_MESSAGE: &str = include_str!("../resources/arc/001.txt");
19
20 diff --git a/examples/dkim_sign.rs b/examples/dkim_sign.rs
21index a05e196..edf9fa0 100644
22--- a/examples/dkim_sign.rs
23+++ b/examples/dkim_sign.rs
24 @@ -9,11 +9,14 @@
25 */
26
27 use mail_auth::{
28- common::{crypto::Ed25519Key, crypto::RsaKey, headers::HeaderWriter},
29+ common::{
30+ crypto::RsaKey,
31+ crypto::{Ed25519Key, Sha256},
32+ headers::HeaderWriter,
33+ },
34 dkim::DkimSigner,
35 };
36 use mail_parser::decoders::base64::base64_decode;
37- use sha2::Sha256;
38
39 const RSA_PRIVATE_KEY: &str = r#"-----BEGIN RSA PRIVATE KEY-----
40 MIICXwIBAAKBgQDwIRP/UC3SBsEmGqZ9ZJW3/DkMoGeLnQg1fWn7/zYtIxN2SnFC
41 diff --git a/src/arc/builder.rs b/src/arc/builder.rs
42index f5780d4..57c5ed8 100644
43--- a/src/arc/builder.rs
44+++ b/src/arc/builder.rs
45 @@ -8,10 +8,8 @@
46 * except according to those terms.
47 */
48
49- use sha2::Sha256;
50-
51 use crate::{
52- common::crypto::SigningKey,
53+ common::crypto::{Sha256, SigningKey},
54 dkim::{Canonicalization, Done, NeedDomain, NeedHeaders, NeedSelector},
55 };
56
57 diff --git a/src/arc/mod.rs b/src/arc/mod.rs
58index ed4ea20..a285379 100644
59--- a/src/arc/mod.rs
60+++ b/src/arc/mod.rs
61 @@ -14,11 +14,9 @@ pub mod parse;
62 pub mod seal;
63 pub mod verify;
64
65- use sha2::Sha256;
66-
67 use crate::{
68 common::{
69- crypto::{Algorithm, SigningKey},
70+ crypto::{Algorithm, Sha256, SigningKey},
71 headers::Header,
72 verify::VerifySignature,
73 },
74 diff --git a/src/arc/seal.rs b/src/arc/seal.rs
75index 6835c16..c61c245 100644
76--- a/src/arc/seal.rs
77+++ b/src/arc/seal.rs
78 @@ -11,11 +11,10 @@
79 use std::time::SystemTime;
80
81 use mail_builder::encoders::base64::base64_encode;
82- use sha2::Sha256;
83
84 use crate::{
85 common::{
86- crypto::{HashContext, SigningKey},
87+ crypto::{HashContext, Sha256, SigningKey},
88 headers::Writer,
89 },
90 dkim::{Canonicalization, Done},
91 @@ -169,12 +168,11 @@ mod test {
92 use std::time::{Duration, Instant};
93
94 use mail_parser::decoders::base64::base64_decode;
95- use sha2::Sha256;
96
97 use crate::{
98 arc::ArcSealer,
99 common::{
100- crypto::{Ed25519Key, RsaKey, SigningKey},
101+ crypto::{Ed25519Key, RsaKey, Sha256, SigningKey},
102 headers::HeaderWriter,
103 parse::TxtRecordParser,
104 verify::DomainKey,
105 diff --git a/src/common/base32.rs b/src/common/base32.rs
106index c9e9d88..3b270a7 100644
107--- a/src/common/base32.rs
108+++ b/src/common/base32.rs
109 @@ -78,9 +78,11 @@ impl Writer for Base32Writer {
110
111 #[cfg(test)]
112 mod tests {
113- use sha1::{Digest, Sha1};
114-
115- use crate::common::{base32::Base32Writer, headers::Writer};
116+ use crate::common::{
117+ base32::Base32Writer,
118+ crypto::{HashContext, HashImpl, Sha1},
119+ headers::Writer,
120+ };
121
122 #[test]
123 fn base32_hash() {
124 @@ -89,9 +91,9 @@ mod tests {
125 ("two.example.net", "ZTZGRRV3F45A4U6HLDKBF3ZCOW4V2AJX"),
126 ] {
127 let mut writer = Base32Writer::with_capacity(10);
128- let mut hash = Sha1::new();
129- hash.update(test.as_bytes());
130- writer.write(&hash.finalize()[..]);
131+ let mut hash = Sha1::hasher();
132+ hash.write(test.as_bytes());
133+ writer.write(hash.finish().as_ref());
134 assert_eq!(writer.finalize(), expected_result);
135 }
136 }
137 diff --git a/src/common/crypto.rs b/src/common/crypto.rs
138index a897c9b..0852061 100644
139--- a/src/common/crypto.rs
140+++ b/src/common/crypto.rs
141 @@ -1,4 +1,4 @@
142- use std::{io, marker::PhantomData};
143+ use std::marker::PhantomData;
144
145 use ed25519_dalek::Signer;
146 use rsa::{
147 @@ -6,20 +6,20 @@ use rsa::{
148 pkcs8::{AssociatedOid, ObjectIdentifier},
149 PaddingScheme, PublicKey as _, RsaPrivateKey,
150 };
151- use sha1::{digest::Output, Sha1};
152- use sha2::{digest::Digest, Sha256};
153+ use sha1::digest::Output;
154+ use sha2::digest::Digest;
155
156 use crate::{dkim::Canonicalization, Error, Result};
157
158 use super::headers::Writer;
159
160 pub trait SigningKey {
161- type Hasher: HashContext;
162+ type Hasher: HashImpl;
163
164 fn sign(&self, data: HashOutput) -> Result<Vec<u8>>;
165
166- fn hasher(&self) -> Self::Hasher {
167- Self::Hasher::new()
168+ fn hasher(&self) -> <Self::Hasher as HashImpl>::Context {
169+ <Self::Hasher as HashImpl>::hasher()
170 }
171
172 fn algorithm(&self) -> Algorithm;
173 @@ -31,7 +31,7 @@ pub struct RsaKey<T> {
174 padding: PhantomData<T>,
175 }
176
177- impl<T: Digest + AssociatedOid + io::Write> RsaKey<T> {
178+ impl<T: HashImpl> RsaKey<T> {
179 /// Creates a new RSA private key from a PKCS1 PEM string.
180 pub fn from_pkcs1_pem(private_key_pem: &str) -> Result<Self> {
181 let inner = RsaPrivateKey::from_pkcs1_pem(private_key_pem)
182 @@ -61,7 +61,7 @@ impl SigningKey for RsaKey<Sha1> {
183 fn sign(&self, data: HashOutput) -> Result<Vec<u8>> {
184 self.inner
185 .sign(
186- PaddingScheme::new_pkcs1v15_sign::<Self::Hasher>(),
187+ PaddingScheme::new_pkcs1v15_sign::<<Self::Hasher as HashImpl>::Context>(),
188 data.as_ref(),
189 )
190 .map_err(|err| Error::CryptoError(err.to_string()))
191 @@ -78,7 +78,7 @@ impl SigningKey for RsaKey<Sha256> {
192 fn sign(&self, data: HashOutput) -> Result<Vec<u8>> {
193 self.inner
194 .sign(
195- PaddingScheme::new_pkcs1v15_sign::<Self::Hasher>(),
196+ PaddingScheme::new_pkcs1v15_sign::<<Self::Hasher as HashImpl>::Context>(),
197 data.as_ref(),
198 )
199 .map_err(|err| Error::CryptoError(err.to_string()))
200 @@ -173,7 +173,7 @@ impl VerifyingKey for RsaPublicKey {
201 let hash = canonicalization.hash_headers::<Sha256>(headers);
202 self.inner
203 .verify(
204- PaddingScheme::new_pkcs1v15_sign::<Sha256>(),
205+ PaddingScheme::new_pkcs1v15_sign::<sha2::Sha256>(),
206 hash.as_ref(),
207 signature,
208 )
209 @@ -183,7 +183,7 @@ impl VerifyingKey for RsaPublicKey {
210 let hash = canonicalization.hash_headers::<Sha1>(headers);
211 self.inner
212 .verify(
213- PaddingScheme::new_pkcs1v15_sign::<Sha1>(),
214+ PaddingScheme::new_pkcs1v15_sign::<sha1::Sha1>(),
215 hash.as_ref(),
216 signature,
217 )
218 @@ -221,40 +221,59 @@ impl VerifyingKey for Ed25519PublicKey {
219 }
220 }
221
222- impl Writer for Sha1 {
223+ impl Writer for sha1::Sha1 {
224 fn write(&mut self, buf: &[u8]) {
225 self.update(buf);
226 }
227 }
228
229- impl Writer for Sha256 {
230+ impl Writer for sha2::Sha256 {
231 fn write(&mut self, buf: &[u8]) {
232 self.update(buf);
233 }
234 }
235
236 pub trait HashContext: Writer + Sized {
237- fn new() -> Self;
238 fn finish(self) -> HashOutput;
239 }
240
241- impl HashContext for Sha1 {
242- fn new() -> Self {
243- <Self as Digest>::new()
244+ impl HashContext for sha1::Sha1 {
245+ fn finish(self) -> HashOutput {
246+ HashOutput::Sha1(self.finalize())
247 }
248+ }
249
250+ impl HashContext for sha2::Sha256 {
251 fn finish(self) -> HashOutput {
252- HashOutput::Sha1(self.finalize())
253+ HashOutput::Sha256(self.finalize())
254 }
255 }
256
257- impl HashContext for Sha256 {
258- fn new() -> Self {
259- <Self as Digest>::new()
260+ pub trait HashImpl {
261+ type Context: HashContext;
262+
263+ fn hasher() -> Self::Context;
264+ }
265+
266+ #[derive(Clone, Copy)]
267+ pub struct Sha1;
268+
269+ impl HashImpl for Sha1 {
270+ type Context = sha1::Sha1;
271+
272+ fn hasher() -> Self::Context {
273+ <Self::Context as Digest>::new()
274 }
275+ }
276
277- fn finish(self) -> HashOutput {
278- HashOutput::Sha256(self.finalize())
279+ #[derive(Clone, Copy)]
280+ pub struct Sha256;
281+
282+ impl HashImpl for Sha256 {
283+ type Context = sha2::Sha256;
284+
285+ fn hasher() -> Self::Context {
286+ <Self::Context as Digest>::new()
287 }
288 }
289
290 @@ -268,15 +287,15 @@ pub enum HashAlgorithm {
291 impl HashAlgorithm {
292 pub fn hash(&self, data: &[u8]) -> HashOutput {
293 match self {
294- Self::Sha1 => HashOutput::Sha1(Sha1::digest(data)),
295- Self::Sha256 => HashOutput::Sha256(Sha256::digest(data)),
296+ Self::Sha1 => HashOutput::Sha1(sha1::Sha1::digest(data)),
297+ Self::Sha256 => HashOutput::Sha256(sha2::Sha256::digest(data)),
298 }
299 }
300 }
301
302 pub enum HashOutput {
303- Sha1(Output<Sha1>),
304- Sha256(Output<Sha256>),
305+ Sha1(Output<sha1::Sha1>),
306+ Sha256(Output<sha2::Sha256>),
307 }
308
309 impl AsRef<[u8]> for HashOutput {
310 @@ -293,8 +312,8 @@ impl TryFrom<&ObjectIdentifier> for HashAlgorithm {
311
312 fn try_from(oid: &ObjectIdentifier) -> Result<Self> {
313 match oid {
314- oid if oid == &Sha256::OID => Ok(HashAlgorithm::Sha256),
315- oid if oid == &Sha1::OID => Ok(HashAlgorithm::Sha1),
316+ oid if oid == &sha2::Sha256::OID => Ok(HashAlgorithm::Sha256),
317+ oid if oid == &sha1::Sha1::OID => Ok(HashAlgorithm::Sha1),
318 _ => Err(Error::CryptoError("Unsupported hash algorithm".to_string())),
319 }
320 }
321 diff --git a/src/common/message.rs b/src/common/message.rs
322index d55b158..0f1ab40 100644
323--- a/src/common/message.rs
324+++ b/src/common/message.rs
325 @@ -9,10 +9,12 @@
326 */
327
328 use mail_parser::{parsers::MessageStream, HeaderValue};
329- use sha1::Sha1;
330- use sha2::Sha256;
331
332- use crate::{arc, common::crypto::HashAlgorithm, dkim, AuthenticatedMessage};
333+ use crate::{
334+ arc,
335+ common::crypto::{HashAlgorithm, Sha1, Sha256},
336+ dkim, AuthenticatedMessage,
337+ };
338
339 use super::headers::{AuthenticatedHeader, Header, HeaderParser};
340
341 diff --git a/src/dkim/canonicalize.rs b/src/dkim/canonicalize.rs
342index 7ad70bd..60ab372 100644
343--- a/src/dkim/canonicalize.rs
344+++ b/src/dkim/canonicalize.rs
345 @@ -8,9 +8,10 @@
346 * except according to those terms.
347 */
348
349- use sha1::Digest;
350-
351- use crate::common::headers::{HeaderIterator, Writer};
352+ use crate::common::{
353+ crypto::{HashContext, HashImpl},
354+ headers::{HeaderIterator, Writer},
355+ };
356
357 use super::{Canonicalization, Signature};
358
359 @@ -114,17 +115,17 @@ impl Canonicalization {
360 }
361 }
362
363- pub fn hash_headers<'x, T: Digest + Writer>(
364+ pub fn hash_headers<'x, T: HashImpl>(
365 &self,
366 headers: &mut dyn Iterator<Item = (&'x [u8], &'x [u8])>,
367 ) -> impl AsRef<[u8]> {
368- let mut hasher = T::new();
369+ let mut hasher = T::hasher();
370 self.canonicalize_headers(headers, &mut hasher);
371- hasher.finalize()
372+ hasher.finish()
373 }
374
375- pub fn hash_body<T: Digest + Writer>(&self, body: &[u8], l: u64) -> impl AsRef<[u8]> {
376- let mut hasher = T::new();
377+ pub fn hash_body<T: HashImpl>(&self, body: &[u8], l: u64) -> impl AsRef<[u8]> {
378+ let mut hasher = T::hasher();
379 self.canonicalize_body(
380 if l == 0 || body.is_empty() {
381 body
382 @@ -133,7 +134,7 @@ impl Canonicalization {
383 },
384 &mut hasher,
385 );
386- hasher.finalize()
387+ hasher.finish()
388 }
389
390 pub fn serialize_name(&self, writer: &mut impl Writer) {
391 diff --git a/src/dkim/sign.rs b/src/dkim/sign.rs
392index 9203449..0e664b2 100644
393--- a/src/dkim/sign.rs
394+++ b/src/dkim/sign.rs
395 @@ -76,12 +76,11 @@ mod test {
396 use std::time::{Duration, Instant};
397
398 use mail_parser::decoders::base64::base64_decode;
399- use sha2::Sha256;
400 use trust_dns_resolver::proto::op::ResponseCode;
401
402 use crate::{
403 common::{
404- crypto::{Ed25519Key, RsaKey},
405+ crypto::{Ed25519Key, RsaKey, Sha256},
406 parse::TxtRecordParser,
407 verify::DomainKey,
408 },