Commit
Author: Mauro D [mauro@stalw.art]
Committer: GitHub [noreply@github.com] Tue, 13 Dec 2022 18:15:37 +0000
Hash: 83bef35e5f1489166a72fd4e5ae1ff6b09f80323
Timestamp: Tue, 13 Dec 2022 18:15:37 +0000 (1 year ago)

+227 -225 +/-9 browse
Merge pull request #6 from InstantDomain/writer
Merge pull request #6 from InstantDomain/writer

Replace usage of io::Write with custom Writer trait
1diff --git a/src/arc/headers.rs b/src/arc/headers.rs
2index 384c053..007c5d9 100644
3--- a/src/arc/headers.rs
4+++ b/src/arc/headers.rs
5 @@ -8,10 +8,11 @@
6 * except according to those terms.
7 */
8
9- use std::io;
10-
11 use crate::{
12- common::{crypto::Algorithm, headers::HeaderWriter},
13+ common::{
14+ crypto::Algorithm,
15+ headers::{HeaderWriter, Writer},
16+ },
17 dkim::Canonicalization,
18 AuthenticationResults,
19 };
20 @@ -19,44 +20,44 @@ use crate::{
21 use super::{ArcSet, ChainValidation, Seal, Signature};
22
23 impl<'x> Signature<'x> {
24- pub(crate) fn write(&self, mut writer: impl io::Write, as_header: bool) -> io::Result<()> {
25+ pub(crate) fn write(&self, writer: &mut impl Writer, as_header: bool) {
26 let (header, new_line) = match self.ch {
27 Canonicalization::Relaxed if !as_header => (&b"arc-message-signature:"[..], &b" "[..]),
28 _ => (&b"ARC-Message-Signature: "[..], &b"\r\n\t"[..]),
29 };
30- writer.write_all(header)?;
31- writer.write_all(b"i=")?;
32- writer.write_all(self.i.to_string().as_bytes())?;
33- writer.write_all(b"; a=")?;
34- writer.write_all(match self.a {
35+ writer.write(header);
36+ writer.write(b"i=");
37+ writer.write(self.i.to_string().as_bytes());
38+ writer.write(b"; a=");
39+ writer.write(match self.a {
40 Algorithm::RsaSha256 => b"rsa-sha256",
41 Algorithm::RsaSha1 => b"rsa-sha1",
42 Algorithm::Ed25519Sha256 => b"ed25519-sha256",
43- })?;
44+ });
45 for (tag, value) in [(&b"; s="[..], &self.s), (&b"; d="[..], &self.d)] {
46- writer.write_all(tag)?;
47- writer.write_all(value.as_bytes())?;
48+ writer.write(tag);
49+ writer.write(value.as_bytes());
50 }
51- writer.write_all(b"; c=")?;
52- self.ch.serialize_name(&mut writer)?;
53- writer.write_all(b"/")?;
54- self.cb.serialize_name(&mut writer)?;
55+ writer.write(b"; c=");
56+ self.ch.serialize_name(writer);
57+ writer.write(b"/");
58+ self.cb.serialize_name(writer);
59
60- writer.write_all(b";")?;
61- writer.write_all(new_line)?;
62+ writer.write(b";");
63+ writer.write(new_line);
64
65 let mut bw = 1;
66 for (num, h) in self.h.iter().enumerate() {
67 if bw + h.len() + 1 >= 76 {
68- writer.write_all(new_line)?;
69+ writer.write(new_line);
70 bw = 1;
71 }
72 if num > 0 {
73- bw += writer.write(b":")?;
74+ writer.write_len(b":", &mut bw);
75 } else {
76- bw += writer.write(b"h=")?;
77+ writer.write_len(b"h=", &mut bw);
78 }
79- bw += writer.write(h.as_bytes())?;
80+ writer.write_len(h.as_bytes(), &mut bw);
81 }
82
83 for (tag, value) in [
84 @@ -66,133 +67,126 @@ impl<'x> Signature<'x> {
85 ] {
86 if value > 0 {
87 let value = value.to_string();
88- bw += writer.write(b";")?;
89+ writer.write_len(b";", &mut bw);
90 if bw + tag.len() + value.len() >= 76 {
91- writer.write_all(new_line)?;
92+ writer.write(new_line);
93 bw = 1;
94 } else {
95- bw += writer.write(b" ")?;
96+ writer.write_len(b" ", &mut bw);
97 }
98
99- bw += writer.write(tag)?;
100- bw += writer.write(value.as_bytes())?;
101+ writer.write_len(tag, &mut bw);
102+ writer.write_len(value.as_bytes(), &mut bw);
103 }
104 }
105
106 for (tag, value) in [(&b"; bh="[..], &self.bh), (&b"; b="[..], &self.b)] {
107- bw += writer.write(tag)?;
108+ writer.write_len(tag, &mut bw);
109 for &byte in value {
110- bw += writer.write(&[byte])?;
111+ writer.write_len(&[byte], &mut bw);
112 if bw >= 76 {
113- writer.write_all(new_line)?;
114+ writer.write(new_line);
115 bw = 1;
116 }
117 }
118 }
119
120- writer.write_all(b";")?;
121+ writer.write(b";");
122 if as_header {
123- writer.write_all(b"\r\n")?;
124+ writer.write(b"\r\n");
125 }
126- Ok(())
127 }
128 }
129
130 impl<'x> Seal<'x> {
131- pub(crate) fn write(&self, mut writer: impl io::Write, as_header: bool) -> io::Result<()> {
132+ pub(crate) fn write(&self, writer: &mut impl Writer, as_header: bool) {
133 let (header, new_line) = if !as_header {
134 (&b"arc-seal:"[..], &b" "[..])
135 } else {
136 (&b"ARC-Seal: "[..], &b"\r\n\t"[..])
137 };
138
139- writer.write_all(header)?;
140- writer.write_all(b"i=")?;
141- writer.write_all(self.i.to_string().as_bytes())?;
142- writer.write_all(b"; a=")?;
143- writer.write_all(match self.a {
144+ writer.write(header);
145+ writer.write(b"i=");
146+ writer.write(self.i.to_string().as_bytes());
147+ writer.write(b"; a=");
148+ writer.write(match self.a {
149 Algorithm::RsaSha256 => b"rsa-sha256",
150 Algorithm::RsaSha1 => b"rsa-sha1",
151 Algorithm::Ed25519Sha256 => b"ed25519-sha256",
152- })?;
153+ });
154 for (tag, value) in [(&b"; s="[..], &self.s), (&b"; d="[..], &self.d)] {
155- writer.write_all(tag)?;
156- writer.write_all(value.as_bytes())?;
157+ writer.write(tag);
158+ writer.write(value.as_bytes());
159 }
160- writer.write_all(b"; cv=")?;
161- writer.write_all(match self.cv {
162+ writer.write(b"; cv=");
163+ writer.write(match self.cv {
164 ChainValidation::None => b"none",
165 ChainValidation::Fail => b"fail",
166 ChainValidation::Pass => b"pass",
167- })?;
168+ });
169
170- writer.write_all(b";")?;
171- writer.write_all(new_line)?;
172+ writer.write(b";");
173+ writer.write(new_line);
174
175 let mut bw = 1;
176 if self.t > 0 {
177- bw += writer.write(b"t=")?;
178- bw += writer.write(self.t.to_string().as_bytes())?;
179- bw += writer.write(b"; ")?;
180+ writer.write_len(b"t=", &mut bw);
181+ writer.write_len(self.t.to_string().as_bytes(), &mut bw);
182+ writer.write_len(b"; ", &mut bw);
183 }
184
185- bw += writer.write(b"b=")?;
186+ writer.write_len(b"b=", &mut bw);
187 for &byte in &self.b {
188- bw += writer.write(&[byte])?;
189+ writer.write_len(&[byte], &mut bw);
190 if bw >= 76 {
191- writer.write_all(new_line)?;
192+ writer.write(new_line);
193 bw = 1;
194 }
195 }
196
197- writer.write_all(b";")?;
198+ writer.write(b";");
199 if as_header {
200- writer.write_all(b"\r\n")?;
201+ writer.write(b"\r\n");
202 }
203- Ok(())
204 }
205 }
206
207 impl<'x> AuthenticationResults<'x> {
208- pub(crate) fn write(
209- &self,
210- mut writer: impl io::Write,
211- i: u32,
212- as_header: bool,
213- ) -> io::Result<()> {
214- writer.write_all(if !as_header {
215+ pub(crate) fn write(&self, writer: &mut impl Writer, i: u32, as_header: bool) {
216+ writer.write(if !as_header {
217 b"arc-authentication-results:"
218 } else {
219 b"ARC-Authentication-Results: "
220- })?;
221- writer.write_all(b"i=")?;
222- writer.write_all(i.to_string().as_bytes())?;
223- writer.write_all(b"; ")?;
224- writer.write_all(self.hostname.as_bytes())?;
225+ });
226+ writer.write(b"i=");
227+ writer.write(i.to_string().as_bytes());
228+ writer.write(b"; ");
229+ writer.write(self.hostname.as_bytes());
230 if !as_header {
231 let mut last_is_space = false;
232 for &ch in self.auth_results.as_bytes() {
233 if !ch.is_ascii_whitespace() {
234 if last_is_space {
235- writer.write_all(&[b' '])?;
236+ writer.write(&[b' ']);
237 last_is_space = false;
238 }
239- writer.write_all(&[ch])?;
240+ writer.write(&[ch]);
241 } else {
242 last_is_space = true;
243 }
244 }
245 } else {
246- writer.write_all(self.auth_results.as_bytes())?;
247+ writer.write(self.auth_results.as_bytes());
248 }
249- writer.write_all(b"\r\n")
250+ writer.write(b"\r\n");
251 }
252 }
253
254 impl<'x> HeaderWriter for ArcSet<'x> {
255- fn write_header(&self, mut writer: impl io::Write) -> io::Result<()> {
256- self.seal.write(&mut writer, true)?;
257- self.signature.write(&mut writer, true)?;
258- self.results.write(&mut writer, self.seal.i, true)
259+ fn write_header(&self, writer: &mut impl Writer) {
260+ self.seal.write(writer, true);
261+ self.signature.write(writer, true);
262+ self.results.write(writer, self.seal.i, true);
263 }
264 }
265 diff --git a/src/arc/seal.rs b/src/arc/seal.rs
266index 17602c1..80c2cab 100644
267--- a/src/arc/seal.rs
268+++ b/src/arc/seal.rs
269 @@ -14,8 +14,9 @@ use mail_builder::encoders::base64::base64_encode;
270 use sha2::{Digest, Sha256};
271
272 use crate::{
273- common::crypto::SigningKey, dkim::Canonicalization, ArcOutput, AuthenticatedMessage,
274- AuthenticationResults, DkimResult, Error,
275+ common::{crypto::SigningKey, headers::Writer},
276+ dkim::Canonicalization,
277+ ArcOutput, AuthenticatedMessage, AuthenticationResults, DkimResult, Error,
278 };
279
280 use super::{ArcSet, ChainValidation, Seal, Signature};
281 @@ -89,7 +90,7 @@ impl<'x> ArcSet<'x> {
282 }
283
284 // Add signature to hash
285- self.signature.write(&mut header_hasher, false)?;
286+ self.signature.write(&mut header_hasher, false);
287
288 // Sign
289 let b = with_key.sign(&header_hasher.finalize())?;
290 @@ -107,14 +108,14 @@ impl<'x> ArcSet<'x> {
291 ]
292 }),
293 &mut header_hasher,
294- )?;
295+ );
296 }
297
298 // Hash ARC headers for the current instance
299- self.results.write(&mut header_hasher, self.seal.i, false)?;
300- self.signature.write(&mut header_hasher, false)?;
301+ self.results.write(&mut header_hasher, self.seal.i, false);
302+ self.signature.write(&mut header_hasher, false);
303 header_hasher.write_all(b"\r\n")?;
304- self.seal.write(&mut header_hasher, false)?;
305+ self.seal.write(&mut header_hasher, false);
306
307 // Seal
308 let b = with_key.sign(&header_hasher.finalize())?;
309 @@ -173,8 +174,8 @@ impl<'x> Signature<'x> {
310 pub(crate) fn canonicalize(
311 &self,
312 message: &'x AuthenticatedMessage<'x>,
313- header_hasher: impl Write,
314- body_hasher: impl Write,
315+ header_hasher: &mut impl Writer,
316+ body_hasher: &mut impl Writer,
317 ) -> crate::Result<(usize, Vec<Cow<'x, str>>)> {
318 let mut headers = Vec::with_capacity(self.h.len());
319 let mut found_headers = vec![false; self.h.len()];
320 @@ -194,8 +195,8 @@ impl<'x> Signature<'x> {
321
322 let body_len = message.body.len();
323 self.ch
324- .canonicalize_headers(&mut headers.into_iter().rev(), header_hasher)?;
325- self.cb.canonicalize_body(message.body, body_hasher)?;
326+ .canonicalize_headers(&mut headers.into_iter().rev(), header_hasher);
327+ self.cb.canonicalize_body(message.body, body_hasher);
328
329 // Add any missing headers
330 signed_headers.reverse();
331 diff --git a/src/common/auth_results.rs b/src/common/auth_results.rs
332index 3e727ca..dcfaa4d 100644
333--- a/src/common/auth_results.rs
334+++ b/src/common/auth_results.rs
335 @@ -8,7 +8,7 @@
336 * except according to those terms.
337 */
338
339- use std::{borrow::Cow, fmt::Write, io, net::IpAddr};
340+ use std::{borrow::Cow, fmt::Write, net::IpAddr};
341
342 use mail_builder::encoders::base64::base64_encode;
343
344 @@ -17,7 +17,7 @@ use crate::{
345 ReceivedSpf, SpfOutput, SpfResult,
346 };
347
348- use super::headers::HeaderWriter;
349+ use super::headers::{HeaderWriter, Writer};
350
351 impl<'x> AuthenticationResults<'x> {
352 pub fn new(hostname: &'x str) -> Self {
353 @@ -118,23 +118,23 @@ impl<'x> AuthenticationResults<'x> {
354 }
355
356 impl<'x> HeaderWriter for AuthenticationResults<'x> {
357- fn write_header(&self, mut writer: impl io::Write) -> io::Result<()> {
358- writer.write_all(b"Authentication-Results: ")?;
359- writer.write_all(self.hostname.as_bytes())?;
360+ fn write_header(&self, writer: &mut impl Writer) {
361+ writer.write(b"Authentication-Results: ");
362+ writer.write(self.hostname.as_bytes());
363 if !self.auth_results.is_empty() {
364- writer.write_all(self.auth_results.as_bytes())?;
365+ writer.write(self.auth_results.as_bytes());
366 } else {
367- writer.write_all(b"; none")?;
368+ writer.write(b"; none");
369 }
370- writer.write_all(b"\r\n")
371+ writer.write(b"\r\n");
372 }
373 }
374
375 impl HeaderWriter for ReceivedSpf {
376- fn write_header(&self, mut writer: impl io::Write) -> io::Result<()> {
377- writer.write_all(b"Received-SPF: ")?;
378- writer.write_all(self.received_spf.as_bytes())?;
379- writer.write_all(b"\r\n")
380+ fn write_header(&self, writer: &mut impl Writer) {
381+ writer.write(b"Received-SPF: ");
382+ writer.write(self.received_spf.as_bytes());
383+ writer.write(b"\r\n");
384 }
385 }
386
387 diff --git a/src/common/crypto.rs b/src/common/crypto.rs
388index 6c5b17b..e37d859 100644
389--- a/src/common/crypto.rs
390+++ b/src/common/crypto.rs
391 @@ -11,8 +11,10 @@ use sha2::{digest::Digest, Sha256};
392
393 use crate::{dkim::Canonicalization, Error, Result};
394
395+ use super::headers::Writer;
396+
397 pub trait SigningKey {
398- type Hasher: Digest + AssociatedOid + io::Write;
399+ type Hasher: Digest + Writer;
400
401 fn sign(&self, data: &Output<Self::Hasher>) -> Result<Vec<u8>>;
402
403 @@ -159,23 +161,23 @@ impl VerifyingKey for RsaPublicKey {
404 ) -> Result<()> {
405 match algorithm {
406 Algorithm::RsaSha256 => {
407- let hash = canonicalization
408- .hash_headers::<Sha256>(headers)
409- .unwrap_or_default();
410+ let hash = canonicalization.hash_headers::<Sha256>(headers);
411 self.inner
412 .verify(
413 PaddingScheme::new_pkcs1v15_sign::<Sha256>(),
414- &hash,
415+ hash.as_ref(),
416 signature,
417 )
418 .map_err(|_| Error::FailedVerification)
419 }
420 Algorithm::RsaSha1 => {
421- let hash = canonicalization
422- .hash_headers::<Sha1>(headers)
423- .unwrap_or_default();
424+ let hash = canonicalization.hash_headers::<Sha1>(headers);
425 self.inner
426- .verify(PaddingScheme::new_pkcs1v15_sign::<Sha1>(), &hash, signature)
427+ .verify(
428+ PaddingScheme::new_pkcs1v15_sign::<Sha1>(),
429+ hash.as_ref(),
430+ signature,
431+ )
432 .map_err(|_| Error::FailedVerification)
433 }
434 Algorithm::Ed25519Sha256 => Err(Error::IncompatibleAlgorithms),
435 @@ -199,12 +201,10 @@ impl VerifyingKey for Ed25519PublicKey {
436 return Err(Error::IncompatibleAlgorithms);
437 }
438
439- let hash = canonicalization
440- .hash_headers::<Sha256>(headers)
441- .unwrap_or_default();
442+ let hash = canonicalization.hash_headers::<Sha256>(headers);
443 self.inner
444 .verify_strict(
445- &hash,
446+ hash.as_ref(),
447 &ed25519_dalek::Signature::from_bytes(signature)
448 .map_err(|err| Error::CryptoError(err.to_string()))?,
449 )
450 @@ -212,6 +212,18 @@ impl VerifyingKey for Ed25519PublicKey {
451 }
452 }
453
454+ impl Writer for Sha1 {
455+ fn write(&mut self, buf: &[u8]) {
456+ self.update(buf);
457+ }
458+ }
459+
460+ impl Writer for Sha256 {
461+ fn write(&mut self, buf: &[u8]) {
462+ self.update(buf);
463+ }
464+ }
465+
466 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
467 #[repr(u64)]
468 pub enum HashAlgorithm {
469 diff --git a/src/common/headers.rs b/src/common/headers.rs
470index d2673eb..0b5370b 100644
471--- a/src/common/headers.rs
472+++ b/src/common/headers.rs
473 @@ -9,7 +9,6 @@
474 */
475
476 use std::{
477- io,
478 iter::{Enumerate, Peekable},
479 slice::Iter,
480 };
481 @@ -266,14 +265,29 @@ impl<'x> Iterator for HeaderParser<'x> {
482 }
483
484 pub trait HeaderWriter: Sized {
485- fn write_header(&self, writer: impl io::Write) -> io::Result<()>;
486+ fn write_header(&self, writer: &mut impl Writer);
487 fn to_header(&self) -> String {
488 let mut buf = Vec::new();
489- self.write_header(&mut buf).unwrap();
490+ self.write_header(&mut buf);
491 String::from_utf8(buf).unwrap()
492 }
493 }
494
495+ pub trait Writer {
496+ fn write(&mut self, buf: &[u8]);
497+
498+ fn write_len(&mut self, buf: &[u8], len: &mut usize) {
499+ self.write(buf);
500+ *len += buf.len();
501+ }
502+ }
503+
504+ impl Writer for Vec<u8> {
505+ fn write(&mut self, buf: &[u8]) {
506+ self.extend(buf);
507+ }
508+ }
509+
510 const FROM: u64 = (b'f' as u64) | (b'r' as u64) << 8 | (b'o' as u64) << 16 | (b'm' as u64) << 24;
511 const DKIM: u64 = (b'd' as u64)
512 | (b'k' as u64) << 8
513 diff --git a/src/common/message.rs b/src/common/message.rs
514index fe144c3..dd1ab6f 100644
515--- a/src/common/message.rs
516+++ b/src/common/message.rs
517 @@ -147,10 +147,9 @@ impl<'x> AuthenticatedMessage<'x> {
518 // Calculate body hashes
519 for (cb, ha, l, bh) in &mut message.body_hashes {
520 *bh = match ha {
521- HashAlgorithm::Sha256 => cb.hash_body::<Sha256>(message.body, *l),
522- HashAlgorithm::Sha1 => cb.hash_body::<Sha1>(message.body, *l),
523- }
524- .unwrap_or_default();
525+ HashAlgorithm::Sha256 => cb.hash_body::<Sha256>(message.body, *l).as_ref().to_vec(),
526+ HashAlgorithm::Sha1 => cb.hash_body::<Sha1>(message.body, *l).as_ref().to_vec(),
527+ };
528 }
529
530 // Sort ARC headers
531 diff --git a/src/dkim/canonicalize.rs b/src/dkim/canonicalize.rs
532index bf727d3..143fd6e 100644
533--- a/src/dkim/canonicalize.rs
534+++ b/src/dkim/canonicalize.rs
535 @@ -8,19 +8,16 @@
536 * except according to those terms.
537 */
538
539- use std::{
540- borrow::Cow,
541- io::{self},
542- };
543+ use std::borrow::Cow;
544
545 use sha1::Digest;
546
547- use crate::common::headers::HeaderIterator;
548+ use crate::common::headers::{HeaderIterator, Writer};
549
550 use super::{Canonicalization, Signature};
551
552 impl Canonicalization {
553- pub fn canonicalize_body(&self, body: &[u8], mut hasher: impl io::Write) -> io::Result<()> {
554+ pub fn canonicalize_body(&self, body: &[u8], hasher: &mut impl Writer) {
555 let mut crlf_seq = 0;
556
557 match self {
558 @@ -31,7 +28,7 @@ impl Canonicalization {
559 match ch {
560 b' ' | b'\t' => {
561 while crlf_seq > 0 {
562- let _ = hasher.write(b"\r\n")?;
563+ hasher.write(b"\r\n");
564 crlf_seq -= 1;
565 }
566 }
567 @@ -41,15 +38,15 @@ impl Canonicalization {
568 b'\r' => {}
569 _ => {
570 while crlf_seq > 0 {
571- let _ = hasher.write(b"\r\n")?;
572+ hasher.write(b"\r\n");
573 crlf_seq -= 1;
574 }
575
576 if last_ch == b' ' || last_ch == b'\t' {
577- let _ = hasher.write(b" ")?;
578+ hasher.write(b" ");
579 }
580
581- let _ = hasher.write(&[ch])?;
582+ hasher.write(&[ch]);
583 }
584 }
585
586 @@ -65,78 +62,70 @@ impl Canonicalization {
587 b'\r' => {}
588 _ => {
589 while crlf_seq > 0 {
590- let _ = hasher.write(b"\r\n")?;
591+ hasher.write(b"\r\n");
592 crlf_seq -= 1;
593 }
594- let _ = hasher.write(&[ch])?;
595+ hasher.write(&[ch]);
596 }
597 }
598 }
599 }
600 }
601
602- hasher.write_all(b"\r\n")
603+ hasher.write(b"\r\n");
604 }
605
606 pub fn canonicalize_headers<'x>(
607 &self,
608 headers: &mut dyn Iterator<Item = (&'x [u8], &'x [u8])>,
609- mut hasher: impl io::Write,
610- ) -> io::Result<()> {
611+ hasher: &mut impl Writer,
612+ ) {
613 match self {
614 Canonicalization::Relaxed => {
615 for (name, value) in headers {
616 for &ch in name {
617 if !ch.is_ascii_whitespace() {
618- let _ = hasher.write(&[ch.to_ascii_lowercase()])?;
619+ hasher.write(&[ch.to_ascii_lowercase()]);
620 }
621 }
622- let _ = hasher.write(b":")?;
623- let mut bytes_written = 0;
624+ hasher.write(b":");
625+ let mut bw = 0;
626 let mut last_ch = 0;
627
628 for &ch in value {
629 if !ch.is_ascii_whitespace() {
630- if [b' ', b'\t'].contains(&last_ch) && bytes_written > 0 {
631- bytes_written += hasher.write(b" ")?;
632+ if [b' ', b'\t'].contains(&last_ch) && bw > 0 {
633+ hasher.write_len(b" ", &mut bw);
634 }
635- bytes_written += hasher.write(&[ch])?;
636+ hasher.write_len(&[ch], &mut bw);
637 }
638 last_ch = ch;
639 }
640 if last_ch == b'\n' {
641- let _ = hasher.write(b"\r\n");
642+ hasher.write(b"\r\n");
643 }
644 }
645 }
646 Canonicalization::Simple => {
647 for (name, value) in headers {
648- let _ = hasher.write(name)?;
649- let _ = hasher.write(b":")?;
650- let _ = hasher.write(value)?;
651+ hasher.write(name);
652+ hasher.write(b":");
653+ hasher.write(value);
654 }
655 }
656 }
657-
658- Ok(())
659 }
660
661- pub fn hash_headers<'x, T>(
662+ pub fn hash_headers<'x, T: Digest + Writer>(
663 &self,
664 headers: &mut dyn Iterator<Item = (&'x [u8], &'x [u8])>,
665- ) -> io::Result<Vec<u8>>
666- where
667- T: Digest + io::Write,
668- {
669+ ) -> impl AsRef<[u8]> {
670 let mut hasher = T::new();
671- self.canonicalize_headers(headers, &mut hasher)?;
672- Ok(hasher.finalize().to_vec())
673+ self.canonicalize_headers(headers, &mut hasher);
674+ hasher.finalize()
675 }
676
677- pub fn hash_body<T>(&self, body: &[u8], l: u64) -> io::Result<Vec<u8>>
678- where
679- T: Digest + io::Write,
680- {
681+ pub fn hash_body<T: Digest + Writer>(&self, body: &[u8], l: u64) -> impl AsRef<[u8]> {
682 let mut hasher = T::new();
683 self.canonicalize_body(
684 if l == 0 || body.is_empty() {
685 @@ -145,15 +134,15 @@ impl Canonicalization {
686 &body[..std::cmp::min(l as usize, body.len())]
687 },
688 &mut hasher,
689- )?;
690- Ok(hasher.finalize().to_vec())
691+ );
692+ hasher.finalize()
693 }
694
695- pub fn serialize_name(&self, mut writer: impl io::Write) -> io::Result<()> {
696- writer.write_all(match self {
697+ pub fn serialize_name(&self, writer: &mut impl Writer) {
698+ writer.write(match self {
699 Canonicalization::Relaxed => b"relaxed",
700 Canonicalization::Simple => b"simple",
701- })
702+ });
703 }
704 }
705
706 @@ -162,9 +151,9 @@ impl<'x> Signature<'x> {
707 pub(crate) fn canonicalize(
708 &self,
709 message: &'x [u8],
710- header_hasher: impl io::Write,
711- body_hasher: impl io::Write,
712- ) -> crate::Result<(usize, Vec<Cow<'x, str>>)> {
713+ header_hasher: &mut impl Writer,
714+ body_hasher: &mut impl Writer,
715+ ) -> (usize, Vec<Cow<'x, str>>) {
716 let mut headers_it = HeaderIterator::new(message);
717 let mut headers = Vec::with_capacity(self.h.len());
718 let mut found_headers = vec![false; self.h.len()];
719 @@ -188,8 +177,8 @@ impl<'x> Signature<'x> {
720 .unwrap_or_default();
721 let body_len = body.len();
722 self.ch
723- .canonicalize_headers(&mut headers.into_iter().rev(), header_hasher)?;
724- self.cb.canonicalize_body(body, body_hasher)?;
725+ .canonicalize_headers(&mut headers.into_iter().rev(), header_hasher);
726+ self.cb.canonicalize_body(body, body_hasher);
727
728 // Add any missing headers
729 signed_headers.reverse();
730 @@ -199,7 +188,7 @@ impl<'x> Signature<'x> {
731 }
732 }
733
734- Ok((body_len, signed_headers))
735+ (body_len, signed_headers)
736 }
737 }
738
739 @@ -272,11 +261,8 @@ mod test {
740 let mut body = Vec::new();
741
742 canonicalization
743- .canonicalize_headers(&mut parsed_headers.clone().into_iter(), &mut headers)
744- .unwrap();
745- canonicalization
746- .canonicalize_body(raw_body, &mut body)
747- .unwrap();
748+ .canonicalize_headers(&mut parsed_headers.clone().into_iter(), &mut headers);
749+ canonicalization.canonicalize_body(raw_body, &mut body);
750
751 assert_eq!(expected_headers, String::from_utf8(headers).unwrap());
752 assert_eq!(expected_body, String::from_utf8(body).unwrap());
753 diff --git a/src/dkim/headers.rs b/src/dkim/headers.rs
754index 880d302..4f1bd53 100644
755--- a/src/dkim/headers.rs
756+++ b/src/dkim/headers.rs
757 @@ -8,89 +8,86 @@
758 * except according to those terms.
759 */
760
761- use std::{
762- fmt::{Display, Formatter},
763- io,
764- };
765+ use std::fmt::{Display, Formatter};
766
767- use crate::common::headers::HeaderWriter;
768+ use crate::common::headers::{HeaderWriter, Writer};
769
770 use super::{Algorithm, Canonicalization, HashAlgorithm, Signature};
771
772 impl<'x> Signature<'x> {
773- pub(crate) fn write(&self, mut writer: impl io::Write, as_header: bool) -> io::Result<()> {
774+ pub(crate) fn write(&self, writer: &mut impl Writer, as_header: bool) {
775 let (header, new_line) = match self.ch {
776 Canonicalization::Relaxed if !as_header => (&b"dkim-signature:"[..], &b" "[..]),
777 _ => (&b"DKIM-Signature: "[..], &b"\r\n\t"[..]),
778 };
779- writer.write_all(header)?;
780- writer.write_all(b"v=1; a=")?;
781- writer.write_all(match self.a {
782+ writer.write(header);
783+ writer.write(b"v=1; a=");
784+ writer.write(match self.a {
785 Algorithm::RsaSha256 => b"rsa-sha256",
786 Algorithm::RsaSha1 => b"rsa-sha1",
787 Algorithm::Ed25519Sha256 => b"ed25519-sha256",
788- })?;
789+ });
790 for (tag, value) in [(&b"; s="[..], &self.s), (&b"; d="[..], &self.d)] {
791- writer.write_all(tag)?;
792- writer.write_all(value.as_bytes())?;
793+ writer.write(tag);
794+ writer.write(value.as_bytes());
795 }
796- writer.write_all(b"; c=")?;
797- self.ch.serialize_name(&mut writer)?;
798- writer.write_all(b"/")?;
799- self.cb.serialize_name(&mut writer)?;
800+ writer.write(b"; c=");
801+ self.ch.serialize_name(writer);
802+ writer.write(b"/");
803+ self.cb.serialize_name(writer);
804
805 if let Some(atps) = &self.atps {
806- writer.write_all(b"; atps=")?;
807- writer.write_all(atps.as_bytes())?;
808- writer.write_all(b"; atpsh=")?;
809- writer.write_all(match self.atpsh {
810+ writer.write(b"; atps=");
811+ writer.write(atps.as_bytes());
812+ writer.write(b"; atpsh=");
813+ writer.write(match self.atpsh {
814 Some(HashAlgorithm::Sha256) => b"sha256",
815 Some(HashAlgorithm::Sha1) => b"sha1",
816 _ => b"none",
817- })?;
818+ });
819 }
820 if self.r {
821- writer.write_all(b"; r=y")?;
822+ writer.write(b"; r=y");
823 }
824
825- writer.write_all(b";")?;
826- writer.write_all(new_line)?;
827+ writer.write(b";");
828+ writer.write(new_line);
829
830 let mut bw = 1;
831 for (num, h) in self.h.iter().enumerate() {
832 if bw + h.len() + 1 >= 76 {
833- writer.write_all(new_line)?;
834+ writer.write(new_line);
835 bw = 1;
836 }
837 if num > 0 {
838- bw += writer.write(b":")?;
839+ writer.write_len(b":", &mut bw);
840 } else {
841- bw += writer.write(b"h=")?;
842+ writer.write_len(b"h=", &mut bw);
843 }
844- bw += writer.write(h.as_bytes())?;
845+ writer.write_len(h.as_bytes(), &mut bw);
846 }
847
848 if !self.i.is_empty() {
849 if bw + self.i.len() + 3 >= 76 {
850- writer.write_all(b";")?;
851- writer.write_all(new_line)?;
852+ writer.write(b";");
853+ writer.write(new_line);
854 bw = 1;
855 } else {
856- bw += writer.write(b"; ")?;
857+ writer.write_len(b"; ", &mut bw);
858 }
859- bw += writer.write(b"i=")?;
860+ writer.write_len(b"i=", &mut bw);
861
862 for &ch in self.i.as_bytes().iter() {
863 match ch {
864 0..=0x20 | b';' | 0x7f..=u8::MAX => {
865- bw += writer.write(format!("={:02X}", ch).as_bytes())?;
866+ writer.write_len(format!("={:02X}", ch).as_bytes(), &mut bw);
867 }
868 _ => {
869- bw += writer.write(&[ch])?;
870+ writer.write_len(&[ch], &mut bw);
871 }
872 }
873 if bw >= 76 {
874- writer.write_all(new_line)?;
875+ writer.write(new_line);
876 bw = 1;
877 }
878 }
879 @@ -103,48 +100,47 @@ impl<'x> Signature<'x> {
880 ] {
881 if value > 0 {
882 let value = value.to_string();
883- bw += writer.write(b";")?;
884+ writer.write_len(b";", &mut bw);
885 if bw + tag.len() + value.len() >= 76 {
886- writer.write_all(new_line)?;
887+ writer.write(new_line);
888 bw = 1;
889 } else {
890- bw += writer.write(b" ")?;
891+ writer.write_len(b" ", &mut bw);
892 }
893
894- bw += writer.write(tag)?;
895- bw += writer.write(value.as_bytes())?;
896+ writer.write_len(tag, &mut bw);
897+ writer.write_len(value.as_bytes(), &mut bw);
898 }
899 }
900
901 for (tag, value) in [(&b"; bh="[..], &self.bh), (&b"; b="[..], &self.b)] {
902- bw += writer.write(tag)?;
903+ writer.write_len(tag, &mut bw);
904 for &byte in value {
905- bw += writer.write(&[byte])?;
906+ writer.write_len(&[byte], &mut bw);
907 if bw >= 76 {
908- writer.write_all(new_line)?;
909+ writer.write(new_line);
910 bw = 1;
911 }
912 }
913 }
914
915- writer.write_all(b";")?;
916+ writer.write(b";");
917 if as_header {
918- writer.write_all(b"\r\n")?;
919+ writer.write(b"\r\n");
920 }
921- Ok(())
922 }
923 }
924
925 impl<'x> HeaderWriter for Signature<'x> {
926- fn write_header(&self, writer: impl io::Write) -> io::Result<()> {
927- self.write(writer, true)
928+ fn write_header(&self, writer: &mut impl Writer) {
929+ self.write(writer, true);
930 }
931 }
932
933 impl<'x> Display for Signature<'x> {
934 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
935 let mut buf = Vec::new();
936- self.write(&mut buf, false).map_err(|_| std::fmt::Error)?;
937+ self.write(&mut buf, false);
938 f.write_str(&String::from_utf8(buf).map_err(|_| std::fmt::Error)?)
939 }
940 }
941 diff --git a/src/dkim/sign.rs b/src/dkim/sign.rs
942index c9fdb10..a10d5c0 100644
943--- a/src/dkim/sign.rs
944+++ b/src/dkim/sign.rs
945 @@ -52,7 +52,7 @@ impl<'x> Signature<'x> {
946
947 // Canonicalize headers and body
948 let (body_len, signed_headers) =
949- self.canonicalize(message, &mut header_hasher, &mut body_hasher)?;
950+ self.canonicalize(message, &mut header_hasher, &mut body_hasher);
951
952 if signed_headers.is_empty() {
953 return Err(Error::NoHeadersFound);
954 @@ -68,7 +68,7 @@ impl<'x> Signature<'x> {
955 }
956
957 // Add signature to hash
958- self.write(&mut header_hasher, false)?;
959+ self.write(&mut header_hasher, false);
960
961 // Sign
962 let b = with_key.sign(&header_hasher.finalize())?;
963 @@ -442,7 +442,7 @@ GMot/L2x0IYyMLAz6oLWh2hm7zwtb0CgOrPo1ke44hFYnfc=
964 expect: Result<(), super::Error>,
965 ) -> Vec<DkimOutput<'x>> {
966 let mut message = Vec::with_capacity(message_.len() + 100);
967- signature.write(&mut message, true).unwrap();
968+ signature.write(&mut message, true);
969 message.extend_from_slice(message_.as_bytes());
970
971 let message = AuthenticatedMessage::parse(&message).unwrap();