Commit
Author: Dirkjan Ochtman [dirkjan@ochtman.nl]
Hash: c099b6ba6095f1b8cb67965f2dcd2b0ce757fbd3
Timestamp: Tue, 13 Dec 2022 10:38:07 +0000 (1 year ago)

+218 -220 +/-9 browse
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..0879594 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,9 +161,7 @@ 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 @@ -171,9 +171,7 @@ impl VerifyingKey for RsaPublicKey {
415 .map_err(|_| Error::FailedVerification)
416 }
417 Algorithm::RsaSha1 => {
418- let hash = canonicalization
419- .hash_headers::<Sha1>(headers)
420- .unwrap_or_default();
421+ let hash = canonicalization.hash_headers::<Sha1>(headers);
422 self.inner
423 .verify(PaddingScheme::new_pkcs1v15_sign::<Sha1>(), &hash, signature)
424 .map_err(|_| Error::FailedVerification)
425 @@ -199,9 +197,7 @@ impl VerifyingKey for Ed25519PublicKey {
426 return Err(Error::IncompatibleAlgorithms);
427 }
428
429- let hash = canonicalization
430- .hash_headers::<Sha256>(headers)
431- .unwrap_or_default();
432+ let hash = canonicalization.hash_headers::<Sha256>(headers);
433 self.inner
434 .verify_strict(
435 &hash,
436 @@ -212,6 +208,18 @@ impl VerifyingKey for Ed25519PublicKey {
437 }
438 }
439
440+ impl Writer for Sha1 {
441+ fn write(&mut self, buf: &[u8]) {
442+ self.update(buf);
443+ }
444+ }
445+
446+ impl Writer for Sha256 {
447+ fn write(&mut self, buf: &[u8]) {
448+ self.update(buf);
449+ }
450+ }
451+
452 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
453 #[repr(u64)]
454 pub enum HashAlgorithm {
455 diff --git a/src/common/headers.rs b/src/common/headers.rs
456index d2673eb..0b5370b 100644
457--- a/src/common/headers.rs
458+++ b/src/common/headers.rs
459 @@ -9,7 +9,6 @@
460 */
461
462 use std::{
463- io,
464 iter::{Enumerate, Peekable},
465 slice::Iter,
466 };
467 @@ -266,14 +265,29 @@ impl<'x> Iterator for HeaderParser<'x> {
468 }
469
470 pub trait HeaderWriter: Sized {
471- fn write_header(&self, writer: impl io::Write) -> io::Result<()>;
472+ fn write_header(&self, writer: &mut impl Writer);
473 fn to_header(&self) -> String {
474 let mut buf = Vec::new();
475- self.write_header(&mut buf).unwrap();
476+ self.write_header(&mut buf);
477 String::from_utf8(buf).unwrap()
478 }
479 }
480
481+ pub trait Writer {
482+ fn write(&mut self, buf: &[u8]);
483+
484+ fn write_len(&mut self, buf: &[u8], len: &mut usize) {
485+ self.write(buf);
486+ *len += buf.len();
487+ }
488+ }
489+
490+ impl Writer for Vec<u8> {
491+ fn write(&mut self, buf: &[u8]) {
492+ self.extend(buf);
493+ }
494+ }
495+
496 const FROM: u64 = (b'f' as u64) | (b'r' as u64) << 8 | (b'o' as u64) << 16 | (b'm' as u64) << 24;
497 const DKIM: u64 = (b'd' as u64)
498 | (b'k' as u64) << 8
499 diff --git a/src/common/message.rs b/src/common/message.rs
500index fe144c3..b7423c2 100644
501--- a/src/common/message.rs
502+++ b/src/common/message.rs
503 @@ -149,8 +149,7 @@ impl<'x> AuthenticatedMessage<'x> {
504 *bh = match ha {
505 HashAlgorithm::Sha256 => cb.hash_body::<Sha256>(message.body, *l),
506 HashAlgorithm::Sha1 => cb.hash_body::<Sha1>(message.body, *l),
507- }
508- .unwrap_or_default();
509+ };
510 }
511
512 // Sort ARC headers
513 diff --git a/src/dkim/canonicalize.rs b/src/dkim/canonicalize.rs
514index bf727d3..49e0300 100644
515--- a/src/dkim/canonicalize.rs
516+++ b/src/dkim/canonicalize.rs
517 @@ -8,19 +8,16 @@
518 * except according to those terms.
519 */
520
521- use std::{
522- borrow::Cow,
523- io::{self},
524- };
525+ use std::borrow::Cow;
526
527 use sha1::Digest;
528
529- use crate::common::headers::HeaderIterator;
530+ use crate::common::headers::{HeaderIterator, Writer};
531
532 use super::{Canonicalization, Signature};
533
534 impl Canonicalization {
535- pub fn canonicalize_body(&self, body: &[u8], mut hasher: impl io::Write) -> io::Result<()> {
536+ pub fn canonicalize_body(&self, body: &[u8], hasher: &mut impl Writer) {
537 let mut crlf_seq = 0;
538
539 match self {
540 @@ -31,7 +28,7 @@ impl Canonicalization {
541 match ch {
542 b' ' | b'\t' => {
543 while crlf_seq > 0 {
544- let _ = hasher.write(b"\r\n")?;
545+ hasher.write(b"\r\n");
546 crlf_seq -= 1;
547 }
548 }
549 @@ -41,15 +38,15 @@ impl Canonicalization {
550 b'\r' => {}
551 _ => {
552 while crlf_seq > 0 {
553- let _ = hasher.write(b"\r\n")?;
554+ hasher.write(b"\r\n");
555 crlf_seq -= 1;
556 }
557
558 if last_ch == b' ' || last_ch == b'\t' {
559- let _ = hasher.write(b" ")?;
560+ hasher.write(b" ");
561 }
562
563- let _ = hasher.write(&[ch])?;
564+ hasher.write(&[ch]);
565 }
566 }
567
568 @@ -65,78 +62,70 @@ impl Canonicalization {
569 b'\r' => {}
570 _ => {
571 while crlf_seq > 0 {
572- let _ = hasher.write(b"\r\n")?;
573+ hasher.write(b"\r\n");
574 crlf_seq -= 1;
575 }
576- let _ = hasher.write(&[ch])?;
577+ hasher.write(&[ch]);
578 }
579 }
580 }
581 }
582 }
583
584- hasher.write_all(b"\r\n")
585+ hasher.write(b"\r\n");
586 }
587
588 pub fn canonicalize_headers<'x>(
589 &self,
590 headers: &mut dyn Iterator<Item = (&'x [u8], &'x [u8])>,
591- mut hasher: impl io::Write,
592- ) -> io::Result<()> {
593+ hasher: &mut impl Writer,
594+ ) {
595 match self {
596 Canonicalization::Relaxed => {
597 for (name, value) in headers {
598 for &ch in name {
599 if !ch.is_ascii_whitespace() {
600- let _ = hasher.write(&[ch.to_ascii_lowercase()])?;
601+ hasher.write(&[ch.to_ascii_lowercase()]);
602 }
603 }
604- let _ = hasher.write(b":")?;
605- let mut bytes_written = 0;
606+ hasher.write(b":");
607+ let mut bw = 0;
608 let mut last_ch = 0;
609
610 for &ch in value {
611 if !ch.is_ascii_whitespace() {
612- if [b' ', b'\t'].contains(&last_ch) && bytes_written > 0 {
613- bytes_written += hasher.write(b" ")?;
614+ if [b' ', b'\t'].contains(&last_ch) && bw > 0 {
615+ hasher.write_len(b" ", &mut bw);
616 }
617- bytes_written += hasher.write(&[ch])?;
618+ hasher.write_len(&[ch], &mut bw);
619 }
620 last_ch = ch;
621 }
622 if last_ch == b'\n' {
623- let _ = hasher.write(b"\r\n");
624+ hasher.write(b"\r\n");
625 }
626 }
627 }
628 Canonicalization::Simple => {
629 for (name, value) in headers {
630- let _ = hasher.write(name)?;
631- let _ = hasher.write(b":")?;
632- let _ = hasher.write(value)?;
633+ hasher.write(name);
634+ hasher.write(b":");
635+ hasher.write(value);
636 }
637 }
638 }
639-
640- Ok(())
641 }
642
643- pub fn hash_headers<'x, T>(
644+ pub fn hash_headers<'x, T: Digest + Writer>(
645 &self,
646 headers: &mut dyn Iterator<Item = (&'x [u8], &'x [u8])>,
647- ) -> io::Result<Vec<u8>>
648- where
649- T: Digest + io::Write,
650- {
651+ ) -> Vec<u8> {
652 let mut hasher = T::new();
653- self.canonicalize_headers(headers, &mut hasher)?;
654- Ok(hasher.finalize().to_vec())
655+ self.canonicalize_headers(headers, &mut hasher);
656+ hasher.finalize().to_vec()
657 }
658
659- pub fn hash_body<T>(&self, body: &[u8], l: u64) -> io::Result<Vec<u8>>
660- where
661- T: Digest + io::Write,
662- {
663+ pub fn hash_body<T: Digest + Writer>(&self, body: &[u8], l: u64) -> Vec<u8> {
664 let mut hasher = T::new();
665 self.canonicalize_body(
666 if l == 0 || body.is_empty() {
667 @@ -145,15 +134,15 @@ impl Canonicalization {
668 &body[..std::cmp::min(l as usize, body.len())]
669 },
670 &mut hasher,
671- )?;
672- Ok(hasher.finalize().to_vec())
673+ );
674+ hasher.finalize().to_vec()
675 }
676
677- pub fn serialize_name(&self, mut writer: impl io::Write) -> io::Result<()> {
678- writer.write_all(match self {
679+ pub fn serialize_name(&self, writer: &mut impl Writer) {
680+ writer.write(match self {
681 Canonicalization::Relaxed => b"relaxed",
682 Canonicalization::Simple => b"simple",
683- })
684+ });
685 }
686 }
687
688 @@ -162,9 +151,9 @@ impl<'x> Signature<'x> {
689 pub(crate) fn canonicalize(
690 &self,
691 message: &'x [u8],
692- header_hasher: impl io::Write,
693- body_hasher: impl io::Write,
694- ) -> crate::Result<(usize, Vec<Cow<'x, str>>)> {
695+ header_hasher: &mut impl Writer,
696+ body_hasher: &mut impl Writer,
697+ ) -> (usize, Vec<Cow<'x, str>>) {
698 let mut headers_it = HeaderIterator::new(message);
699 let mut headers = Vec::with_capacity(self.h.len());
700 let mut found_headers = vec![false; self.h.len()];
701 @@ -188,8 +177,8 @@ impl<'x> Signature<'x> {
702 .unwrap_or_default();
703 let body_len = body.len();
704 self.ch
705- .canonicalize_headers(&mut headers.into_iter().rev(), header_hasher)?;
706- self.cb.canonicalize_body(body, body_hasher)?;
707+ .canonicalize_headers(&mut headers.into_iter().rev(), header_hasher);
708+ self.cb.canonicalize_body(body, body_hasher);
709
710 // Add any missing headers
711 signed_headers.reverse();
712 @@ -199,7 +188,7 @@ impl<'x> Signature<'x> {
713 }
714 }
715
716- Ok((body_len, signed_headers))
717+ (body_len, signed_headers)
718 }
719 }
720
721 @@ -272,11 +261,8 @@ mod test {
722 let mut body = Vec::new();
723
724 canonicalization
725- .canonicalize_headers(&mut parsed_headers.clone().into_iter(), &mut headers)
726- .unwrap();
727- canonicalization
728- .canonicalize_body(raw_body, &mut body)
729- .unwrap();
730+ .canonicalize_headers(&mut parsed_headers.clone().into_iter(), &mut headers);
731+ canonicalization.canonicalize_body(raw_body, &mut body);
732
733 assert_eq!(expected_headers, String::from_utf8(headers).unwrap());
734 assert_eq!(expected_body, String::from_utf8(body).unwrap());
735 diff --git a/src/dkim/headers.rs b/src/dkim/headers.rs
736index 880d302..4f1bd53 100644
737--- a/src/dkim/headers.rs
738+++ b/src/dkim/headers.rs
739 @@ -8,89 +8,86 @@
740 * except according to those terms.
741 */
742
743- use std::{
744- fmt::{Display, Formatter},
745- io,
746- };
747+ use std::fmt::{Display, Formatter};
748
749- use crate::common::headers::HeaderWriter;
750+ use crate::common::headers::{HeaderWriter, Writer};
751
752 use super::{Algorithm, Canonicalization, HashAlgorithm, Signature};
753
754 impl<'x> Signature<'x> {
755- pub(crate) fn write(&self, mut writer: impl io::Write, as_header: bool) -> io::Result<()> {
756+ pub(crate) fn write(&self, writer: &mut impl Writer, as_header: bool) {
757 let (header, new_line) = match self.ch {
758 Canonicalization::Relaxed if !as_header => (&b"dkim-signature:"[..], &b" "[..]),
759 _ => (&b"DKIM-Signature: "[..], &b"\r\n\t"[..]),
760 };
761- writer.write_all(header)?;
762- writer.write_all(b"v=1; a=")?;
763- writer.write_all(match self.a {
764+ writer.write(header);
765+ writer.write(b"v=1; a=");
766+ writer.write(match self.a {
767 Algorithm::RsaSha256 => b"rsa-sha256",
768 Algorithm::RsaSha1 => b"rsa-sha1",
769 Algorithm::Ed25519Sha256 => b"ed25519-sha256",
770- })?;
771+ });
772 for (tag, value) in [(&b"; s="[..], &self.s), (&b"; d="[..], &self.d)] {
773- writer.write_all(tag)?;
774- writer.write_all(value.as_bytes())?;
775+ writer.write(tag);
776+ writer.write(value.as_bytes());
777 }
778- writer.write_all(b"; c=")?;
779- self.ch.serialize_name(&mut writer)?;
780- writer.write_all(b"/")?;
781- self.cb.serialize_name(&mut writer)?;
782+ writer.write(b"; c=");
783+ self.ch.serialize_name(writer);
784+ writer.write(b"/");
785+ self.cb.serialize_name(writer);
786
787 if let Some(atps) = &self.atps {
788- writer.write_all(b"; atps=")?;
789- writer.write_all(atps.as_bytes())?;
790- writer.write_all(b"; atpsh=")?;
791- writer.write_all(match self.atpsh {
792+ writer.write(b"; atps=");
793+ writer.write(atps.as_bytes());
794+ writer.write(b"; atpsh=");
795+ writer.write(match self.atpsh {
796 Some(HashAlgorithm::Sha256) => b"sha256",
797 Some(HashAlgorithm::Sha1) => b"sha1",
798 _ => b"none",
799- })?;
800+ });
801 }
802 if self.r {
803- writer.write_all(b"; r=y")?;
804+ writer.write(b"; r=y");
805 }
806
807- writer.write_all(b";")?;
808- writer.write_all(new_line)?;
809+ writer.write(b";");
810+ writer.write(new_line);
811
812 let mut bw = 1;
813 for (num, h) in self.h.iter().enumerate() {
814 if bw + h.len() + 1 >= 76 {
815- writer.write_all(new_line)?;
816+ writer.write(new_line);
817 bw = 1;
818 }
819 if num > 0 {
820- bw += writer.write(b":")?;
821+ writer.write_len(b":", &mut bw);
822 } else {
823- bw += writer.write(b"h=")?;
824+ writer.write_len(b"h=", &mut bw);
825 }
826- bw += writer.write(h.as_bytes())?;
827+ writer.write_len(h.as_bytes(), &mut bw);
828 }
829
830 if !self.i.is_empty() {
831 if bw + self.i.len() + 3 >= 76 {
832- writer.write_all(b";")?;
833- writer.write_all(new_line)?;
834+ writer.write(b";");
835+ writer.write(new_line);
836 bw = 1;
837 } else {
838- bw += writer.write(b"; ")?;
839+ writer.write_len(b"; ", &mut bw);
840 }
841- bw += writer.write(b"i=")?;
842+ writer.write_len(b"i=", &mut bw);
843
844 for &ch in self.i.as_bytes().iter() {
845 match ch {
846 0..=0x20 | b';' | 0x7f..=u8::MAX => {
847- bw += writer.write(format!("={:02X}", ch).as_bytes())?;
848+ writer.write_len(format!("={:02X}", ch).as_bytes(), &mut bw);
849 }
850 _ => {
851- bw += writer.write(&[ch])?;
852+ writer.write_len(&[ch], &mut bw);
853 }
854 }
855 if bw >= 76 {
856- writer.write_all(new_line)?;
857+ writer.write(new_line);
858 bw = 1;
859 }
860 }
861 @@ -103,48 +100,47 @@ impl<'x> Signature<'x> {
862 ] {
863 if value > 0 {
864 let value = value.to_string();
865- bw += writer.write(b";")?;
866+ writer.write_len(b";", &mut bw);
867 if bw + tag.len() + value.len() >= 76 {
868- writer.write_all(new_line)?;
869+ writer.write(new_line);
870 bw = 1;
871 } else {
872- bw += writer.write(b" ")?;
873+ writer.write_len(b" ", &mut bw);
874 }
875
876- bw += writer.write(tag)?;
877- bw += writer.write(value.as_bytes())?;
878+ writer.write_len(tag, &mut bw);
879+ writer.write_len(value.as_bytes(), &mut bw);
880 }
881 }
882
883 for (tag, value) in [(&b"; bh="[..], &self.bh), (&b"; b="[..], &self.b)] {
884- bw += writer.write(tag)?;
885+ writer.write_len(tag, &mut bw);
886 for &byte in value {
887- bw += writer.write(&[byte])?;
888+ writer.write_len(&[byte], &mut bw);
889 if bw >= 76 {
890- writer.write_all(new_line)?;
891+ writer.write(new_line);
892 bw = 1;
893 }
894 }
895 }
896
897- writer.write_all(b";")?;
898+ writer.write(b";");
899 if as_header {
900- writer.write_all(b"\r\n")?;
901+ writer.write(b"\r\n");
902 }
903- Ok(())
904 }
905 }
906
907 impl<'x> HeaderWriter for Signature<'x> {
908- fn write_header(&self, writer: impl io::Write) -> io::Result<()> {
909- self.write(writer, true)
910+ fn write_header(&self, writer: &mut impl Writer) {
911+ self.write(writer, true);
912 }
913 }
914
915 impl<'x> Display for Signature<'x> {
916 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
917 let mut buf = Vec::new();
918- self.write(&mut buf, false).map_err(|_| std::fmt::Error)?;
919+ self.write(&mut buf, false);
920 f.write_str(&String::from_utf8(buf).map_err(|_| std::fmt::Error)?)
921 }
922 }
923 diff --git a/src/dkim/sign.rs b/src/dkim/sign.rs
924index c9fdb10..a10d5c0 100644
925--- a/src/dkim/sign.rs
926+++ b/src/dkim/sign.rs
927 @@ -52,7 +52,7 @@ impl<'x> Signature<'x> {
928
929 // Canonicalize headers and body
930 let (body_len, signed_headers) =
931- self.canonicalize(message, &mut header_hasher, &mut body_hasher)?;
932+ self.canonicalize(message, &mut header_hasher, &mut body_hasher);
933
934 if signed_headers.is_empty() {
935 return Err(Error::NoHeadersFound);
936 @@ -68,7 +68,7 @@ impl<'x> Signature<'x> {
937 }
938
939 // Add signature to hash
940- self.write(&mut header_hasher, false)?;
941+ self.write(&mut header_hasher, false);
942
943 // Sign
944 let b = with_key.sign(&header_hasher.finalize())?;
945 @@ -442,7 +442,7 @@ GMot/L2x0IYyMLAz6oLWh2hm7zwtb0CgOrPo1ke44hFYnfc=
946 expect: Result<(), super::Error>,
947 ) -> Vec<DkimOutput<'x>> {
948 let mut message = Vec::with_capacity(message_.len() + 100);
949- signature.write(&mut message, true).unwrap();
950+ signature.write(&mut message, true);
951 message.extend_from_slice(message_.as_bytes());
952
953 let message = AuthenticatedMessage::parse(&message).unwrap();