Repository: lettre/lettre (language: rust)

Issue:
Bug in DKIM body-hash computation with mimebodies
With email bodies of type multipart and singlepart the `dkim` module incorrectly computes the bodyhash. This seems to be because these body types includes the `Content-Type` header directly and the empty line after the header block, both of which are included in the hash computation.


Discussion / hints:

Fix patch:
diff --git a/src/message/mimebody.rs b/src/message/mimebody.rs
index a78f7b055..35bf54f6e 100644
--- a/src/message/mimebody.rs
+++ b/src/message/mimebody.rs
@@ -17,6 +17,16 @@ pub(super) enum Part {
     Multi(MultiPart),
 }
 
+impl Part {
+    #[cfg(feature = "dkim")]
+    pub(super) fn format_body(&self, out: &mut Vec<u8>) {
+        match self {
+            Part::Single(part) => part.format_body(out),
+            Part::Multi(part) => part.format_body(out),
+        }
+    }
+}
+
 impl EmailFormat for Part {
     fn format(&self, out: &mut Vec<u8>) {
         match self {
@@ -132,6 +142,12 @@ impl SinglePart {
         self.format(&mut out);
         out
     }
+
+    /// Format only the signlepart body
+    fn format_body(&self, out: &mut Vec<u8>) {
+        out.extend_from_slice(&self.body);
+        out.extend_from_slice(b"\r\n");
+    }
 }
 
 impl EmailFormat for SinglePart {
@@ -139,8 +155,7 @@ impl EmailFormat for SinglePart {
         write!(out, "{}", self.headers)
             .expect("A Write implementation panicked while formatting headers");
         out.extend_from_slice(b"\r\n");
-        out.extend_from_slice(&self.body);
-        out.extend_from_slice(b"\r\n");
+        self.format_body(out);
     }
 }
 
@@ -373,14 +388,9 @@ impl MultiPart {
         self.format(&mut out);
         out
     }
-}
-
-impl EmailFormat for MultiPart {
-    fn format(&self, out: &mut Vec<u8>) {
-        write!(out, "{}", self.headers)
-            .expect("A Write implementation panicked while formatting headers");
-        out.extend_from_slice(b"\r\n");
 
+    /// Format only the multipart body
+    fn format_body(&self, out: &mut Vec<u8>) {
         let boundary = self.boundary();
 
         for part in &self.parts {
@@ -396,6 +406,15 @@ impl EmailFormat for MultiPart {
     }
 }
 
+impl EmailFormat for MultiPart {
+    fn format(&self, out: &mut Vec<u8>) {
+        write!(out, "{}", self.headers)
+            .expect("A Write implementation panicked while formatting headers");
+        out.extend_from_slice(b"\r\n");
+        self.format_body(out);
+    }
+}
+
 #[cfg(test)]
 mod test {
     use pretty_assertions::assert_eq;
diff --git a/src/message/mod.rs b/src/message/mod.rs
index 6ba9912c2..01f31daac 100644
--- a/src/message/mod.rs
+++ b/src/message/mod.rs
@@ -525,7 +525,7 @@ impl Message {
     pub(crate) fn body_raw(&self) -> Vec<u8> {
         let mut out = Vec::new();
         match &self.body {
-            MessageBody::Mime(p) => p.format(&mut out),
+            MessageBody::Mime(p) => p.format_body(&mut out),
             MessageBody::Raw(r) => out.extend_from_slice(r),
         };
         out.extend_from_slice(b"\r\n");


Test patch: