Subject: bug#78448: 30.1; mml: Produce Unobtrusive Signatures

From: Robert Pluim <[email-masked]>
Date: Mon, 09 Jun 2025 14:45:23 +0200

>>>>> On Fri, 06 Jun 2025 16:32:18 -0400, Daniel Kahn Gillmor <[email-masked]> said:

    Daniel> Hi Robert--
    Daniel> Thank you for the review!

    Daniel> On Tue 2025-06-03 11:32:36 +0200, Robert Pluim wrote:
    Daniel> By pre-computing the likely headers for an outbound message, and passing
    Daniel> them along as a tag in mml-parse, we create an opportunity to provide
    Daniel> Header Protection as described in
    Daniel> https://datatracker.ietf.org/doc/draft-ietf-lamps-header-protection/
    >> 
    >> Is this necessary for unobtrusive signatures to work? If itʼs to
    >> enable future functionality Iʼd rather leave it out.

    Daniel> Yes, unobtrusive signatures are defined with header protection
    Daniel> mandatory.

OK, itʼs just that the wording sounded like future work.

    Daniel> Please see
    Daniel> https://datatracker.ietf.org/doc/draft-gallagher-email-unobtrusive-signatures/

    >> We donʼt use Signed-off-by (and I think a change of this size probably
    >> requires copyright assignment).

    Daniel> OK, feel fre to strip Signed-off-by when applying, that doesn't matter
    Daniel> to me at all.  I'm already in communication with [email-masked] about
    Daniel> copyright assignment.

OK. Itʼs just slightly annoying, because our default commit hooks
refuse Signed-off-by. But Iʼll probably need to edit the commit
message anyway.

    Daniel> +(defun mml-get-likely-headers ()
    Daniel> +  "Get likely final headers from the existing message"
    Daniel> +  (save-excursion
    Daniel> +    (save-restriction
    Daniel> +      (message-narrow-to-headers-or-head)
    Daniel> +      (let ((x (buffer-substring (point-min) (point-max))))
    Daniel> +        (with-temp-buffer
    Daniel> +          (insert x)
    Daniel> +          (message-remove-header "Bcc")
    Daniel> +          (message-remove-header message-ignored-mail-headers t)
    Daniel> +          (mail-encode-encoded-word-buffer)
    Daniel> +          (message-cleanup-headers)
    Daniel> +          (buffer-string)
    Daniel> +          )))))
    Daniel> +
    >> 
    >> `with-output-to-string' is what youʼre looking for here, I think.

    Daniel> with-output-to-string seems to transform stdout (standard output) to a
    Daniel> string.  But i don't see what i'm doing above as using stdout.  I thnk
    Daniel> they're all manipulatig a buffer, which is why i've wrapped them in the
    Daniel> various staging functions (save-excursion, save-restriction,
    Daniel> with-temp-buffer).  Which part should be replaced with
    Daniel> with-output-to-string?

You could use it instead of `with-temp-buffer', but itʼs not a major
point.

    >> Also, what is the chance that this will end up inserting certain
    >> headers twice?

    Daniel> For a signed-only e-mail with header-protection, all of the headers that
    Daniel> the generating MUA knows about will appear twice in the message: Once in
    Daniel> the outer message header section (outside of the cryptographic
    Daniel> envelope), and once in the cryptographic payload (so that they're
    Daniel> covered by the signature).

Thanks, Iʼd missed that the headers were repeated in a separate
part. I donʼt think it will be an issue.

    Daniel> It's certainly possible to trim down the message size in the future by
    Daniel> dropping header fields that we expect to be ignored in transit by MTAs.
    Daniel> But i wouldn't try to do that until we're confident that most receiving
    Daniel> MUAs will handle the signed header fields in the Cryptographic Payload.

    Daniel> In practice, the cost of all duplicated header fields in aggregate is
    Daniel> small compared to, say, a single attached jpg, a boilerplate disclaimer,
    Daniel> or (depending on the algorithm) even the signature itself.  So I'm not
    Daniel> particularly concerned about size.

I just tried it, I needed the small patch below to get it to compile
without warnings.

What goes out on the wire to gmail looks correct, and whatʼs stored
there is correct as well. I canʼt see the Sig header in Gnus, but
maybe thatʼs Gnus not parsing the message correctly :-)

Robert
-- 
diff --git a/lisp/gnus/mml.el b/lisp/gnus/mml.el
index 972f1bce0a1..cfe4709f275 100644
--- a/lisp/gnus/mml.el
+++ b/lisp/gnus/mml.el
@@ -494,6 +494,9 @@ mml-inhibit-compute-boundary
 (declare-function libxml-parse-html-region "xml.c"
 		  (start end &optional base-url discard-comments))
 
+(defvar message-ignored-mail-headers)
+(declare-function message-cleanup-headers "message")
+
 (defun mml-get-likely-headers ()
   "Get likely final headers from the existing message"
   (save-excursion