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

From: Robert Pluim <[email-masked]>
Date: Tue, 03 Jun 2025 11:32:36 +0200

>>>>> On Sat, 31 May 2025 12:21:10 +0300, Eli Zaretskii <[email-masked]> said:

    Eli> Ping!  Eric, would you please chime in and comment on the
    Eli> patch?

Iʼm not Eric, but I have comments and questions below

    Daniel> These three patches appear to do the trick for me.  I'm not an elisp
    Daniel> guru or a gnus expert.  I'm happy to hear any feedback about how they
    Daniel> could be improved.

    Daniel> Regards,

    Daniel>         --dkg

    Daniel> From 81c3e7f5de0bbb24bbd2a2b43a103fb83c530f6d Mon Sep 17 00:00:00 2001
    Daniel> From: Daniel Kahn Gillmor <[email-masked]>
    Daniel> Date: Thu, 15 May 2025 21:49:32 -0400
    Daniel> Subject: [PATCH 1/3] mml: Pass likely headers through to mml-sec functions

    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> Signed-off-by: Daniel Kahn Gillmor <[email-masked]>

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

    Daniel> ---
    Daniel>  lisp/gnus/mml.el | 17 +++++++++++++++++
    Daniel>  1 file changed, 17 insertions(+)

    Daniel> diff --git a/lisp/gnus/mml.el b/lisp/gnus/mml.el
    Daniel> index 51d8d2c3769..11a8de7c011 100644
    Daniel> --- a/lisp/gnus/mml.el
    Daniel> +++ b/lisp/gnus/mml.el
    Daniel> @@ -265,6 +265,8 @@ part.  This is for the internal use, you should never modify the value.")
    Daniel>  	  (apply #'mml-insert-tag
    Daniel>  		 secure-mode
    Daniel>  		 `(,@tags
    Daniel> +                   ,"likely-headers"
    Daniel> +                   ,(mml-get-likely-headers)
    Daniel>  		   ,(if keyfile "keyfile")
    Daniel>  		   ,keyfile
    Daniel>  		   ,@(apply #'append
    Daniel> @@ -492,6 +494,21 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
    Daniel>  (declare-function libxml-parse-html-region "xml.c"
    Daniel>  		  (start end &optional base-url discard-comments))
 
    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>  (defun mml-generate-mime (&optional multipart-type content-type)
    Daniel>    "Generate a MIME message based on the current MML document.
    Daniel>  MULTIPART-TYPE defaults to \"mixed\", but can also
    Daniel> -- 
    Daniel> 2.47.2

    Daniel> From 75f8c5c936deafea1ee44edad5e0f530ec6c4dfc Mon Sep 17 00:00:00 2001
    Daniel> From: Daniel Kahn Gillmor <[email-masked]>
    Daniel> Date: Thu, 15 May 2025 21:54:06 -0400
    Daniel> Subject: [PATCH 2/3] mml: Enable production of Unobtrusive Signatures via epg

    Daniel> https://datatracker.ietf.org/doc/draft-gallagher-email-invisible-signatures/
    Daniel> describes a mechanism to produce cleartext signatures over MIME messages
    Daniel> that are less likely to cause problems than traditional PGP/MIME.

    Daniel> With this patch, it's possible to produce those signatures with:

    Daniel>    (mml-secure-message "unobtrusive" 'sign)

    Daniel> This patch only works with epg, not with mailcrypt or pgg, because epg
    Daniel> is what i'm familiar with and what i can easily test.

pgg is marked obsolete, and I think mailcrypt is external, so thatʼs
ok.

    Daniel> Signed-off-by: Daniel Kahn Gillmor <[email-masked]>
    Daniel> ---
    Daniel>  lisp/gnus/mml-sec.el |  6 ++++++
    Daniel>  lisp/gnus/mml2015.el | 39 +++++++++++++++++++++++++++++++++++++++
    Daniel>  2 files changed, 45 insertions(+)

    Daniel> diff --git a/lisp/gnus/mml-sec.el b/lisp/gnus/mml-sec.el
    Daniel> index 8dffcf872f3..6fb82836e9a 100644
    Daniel> --- a/lisp/gnus/mml-sec.el
    Daniel> +++ b/lisp/gnus/mml-sec.el
    Daniel> @@ -34,6 +34,7 @@
    Daniel>  (autoload 'mail-strip-quoted-names "mail-utils")
    Daniel>  (autoload 'mml2015-sign "mml2015")
    Daniel>  (autoload 'mml2015-encrypt "mml2015")
    Daniel> +(autoload 'mml-unobtrusive-sign "mml2015")
    Daniel>  (autoload 'mml1991-sign "mml1991")
    Daniel>  (autoload 'mml1991-encrypt "mml1991")
    Daniel>  (autoload 'message-fetch-field "message")
    Daniel> @@ -56,6 +57,7 @@
    Daniel>    '(("smime"     mml-smime-sign-buffer     mml-smime-sign-query)
    Daniel>      ("pgp"       mml-pgp-sign-buffer       list)
    Daniel>      ("pgpauto"   mml-pgpauto-sign-buffer  list)
    Daniel> +    ("unobtrusive" mml-unobtrusive-sign-buffer list)
    Daniel>      ("pgpmime"   mml-pgpmime-sign-buffer   list))
    Daniel>    "Alist of MIME signer functions.")
 
    Daniel> @@ -198,6 +200,10 @@ You can also customize or set `mml-signencrypt-style-alist' instead."
    Daniel>    (or (mml2015-sign cont)
    Daniel>        (error "Signing failed... inspect message logs for errors")))
 
    Daniel> +(defun mml-unobtrusive-sign-buffer (cont)
    Daniel> +  (or (mml-unobtrusive-sign cont)
    Daniel> +      (error "Signing failed... inspect message logs for errors")))
    Daniel> +
    Daniel>  (defun mml-pgpmime-encrypt-buffer (cont &optional sign)
    Daniel>    (or (mml2015-encrypt cont sign)
    Daniel>        (error "Encryption failed... inspect message logs for errors")))
    Daniel> diff --git a/lisp/gnus/mml2015.el b/lisp/gnus/mml2015.el
    Daniel> index a46aa68f56a..646fb018a31 100644
    Daniel> --- a/lisp/gnus/mml2015.el
    Daniel> +++ b/lisp/gnus/mml2015.el
    Daniel> @@ -25,6 +25,9 @@
    Daniel>  ;; RFC 2015 is updated by RFC 3156, this file should be compatible
    Daniel>  ;; with both.
 
    Daniel> +;; This is also capable of producing unobtrusive signatures based on
    Daniel> +;; draft-gallagher-email-unobtrusive-signatures
    Daniel> +
    Daniel>  ;;; Code:
 
    Daniel>  (eval-when-compile (require 'cl-lib))
    Daniel> @@ -945,6 +948,42 @@ If set, it overrides the setting of `mml2015-sign-with-sender'."
    Daniel>      (insert (format "--%s--\n" boundary))
    Daniel>      (goto-char (point-max))))
 
    Daniel> +;;; Unobtrusive Signatures, see:
    Daniel> +;;; https://datatracker.ietf.org/doc/draft-gallagher-email-unobtrusive-signatures/
    Daniel> +
    Daniel> +; convert ASCII-armored PGP SIGNATURE block to base64-encoded with FWS
    Daniel> +; at the start of each line:
    Daniel> +(defun pgpsig-armor-to-wrapped-b64 (s)
    Daniel> +  (string-join
    Daniel> +   (string-split
    Daniel> +    (string-trim-right
    Daniel> +     (string-trim-left s "-----BEGIN PGP SIGNATURE-----\n\\(?:[^\n]+\n\\)*\n")
    Daniel> +     "\n\\(?:=....\n\\)?-----END PGP SIGNATURE-----\n?")
    Daniel> +    "\n")
    Daniel> +   "\n "))
    Daniel> +
    Daniel> +(defun mml-unobtrusive-sign (cont)
    Daniel> +  (goto-char (point-min))
    Daniel> +  (insert (cdr (assq 'likely-headers cont)))
    Daniel> +  (re-search-forward "^Content-Type: [^\n]*\\(\n[ \t][^\n]*$\\)*")
    Daniel> +  (insert "; hp=\"clear\"")
    Daniel> +  (re-search-forward "^")

I think thatʼs better expressed as:

    (forward-line 1)

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

Robert
--