GNU bug report logs - #78448
30.1; mml: Produce Unobtrusive Signatures

Previous Next

Packages: gnus, emacs;

Reported by: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>

Date: Fri, 16 May 2025 03:57:02 UTC

Severity: normal

Found in version 30.1

To reply to this bug, email your comments to 78448 AT debbugs.gnu.org.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Fri, 16 May 2025 03:57:03 GMT) Full text and rfc822 format available.

Acknowledgement sent to Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org. (Fri, 16 May 2025 03:57:03 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
To: bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org
Subject: 30.1; mml: Produce Unobtrusive Signatures
Date: Thu, 15 May 2025 23:56:25 -0400
[Message part 1 (text/plain, inline)]
Package: emacs,gnus
Version: 30.1

I'm running emacs 30.1 on debian.  When sending cleartext mail, i want
to be able to produce an "Unobtrusive Signature" using OpenPGP as
described in
https://datatracker.ietf.org/doc/draft-gallagher-email-unobtrusive-signatures/

Those kinds of signatures provide end-to-end cryptographic protection
for headers (based on
https://datatracker.ietf.org/doc/draft-ietf-lamps-header-protection/ )
as well as the message body, and are less likely to inflict bad UX or
bad rendering on recipients using legacy MUAs compared to PGP/MIME (or
inline PGP, for that matter).

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

Regards,

        --dkg

[0001-mml-Pass-likely-headers-through-to-mml-sec-functions.patch (text/x-diff, inline)]
From 81c3e7f5de0bbb24bbd2a2b43a103fb83c530f6d Mon Sep 17 00:00:00 2001
From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
Date: Thu, 15 May 2025 21:49:32 -0400
Subject: [PATCH 1/3] mml: Pass likely headers through to mml-sec functions

By pre-computing the likely headers for an outbound message, and passing
them along as a tag in mml-parse, we create an opportunity to provide
Header Protection as described in
https://datatracker.ietf.org/doc/draft-ietf-lamps-header-protection/

Signed-off-by: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
---
 lisp/gnus/mml.el | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/lisp/gnus/mml.el b/lisp/gnus/mml.el
index 51d8d2c3769..11a8de7c011 100644
--- a/lisp/gnus/mml.el
+++ b/lisp/gnus/mml.el
@@ -265,6 +265,8 @@ part.  This is for the internal use, you should never modify the value.")
 	  (apply #'mml-insert-tag
 		 secure-mode
 		 `(,@tags
+                   ,"likely-headers"
+                   ,(mml-get-likely-headers)
 		   ,(if keyfile "keyfile")
 		   ,keyfile
 		   ,@(apply #'append
@@ -492,6 +494,21 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
 (declare-function libxml-parse-html-region "xml.c"
 		  (start end &optional base-url discard-comments))
 
+(defun mml-get-likely-headers ()
+  "Get likely final headers from the existing message"
+  (save-excursion
+    (save-restriction
+      (message-narrow-to-headers-or-head)
+      (let ((x (buffer-substring (point-min) (point-max))))
+        (with-temp-buffer
+          (insert x)
+          (message-remove-header "Bcc")
+          (message-remove-header message-ignored-mail-headers t)
+          (mail-encode-encoded-word-buffer)
+          (message-cleanup-headers)
+          (buffer-string)
+          )))))
+
 (defun mml-generate-mime (&optional multipart-type content-type)
   "Generate a MIME message based on the current MML document.
 MULTIPART-TYPE defaults to \"mixed\", but can also
-- 
2.47.2

[0002-mml-Enable-production-of-Unobtrusive-Signatures-via-.patch (text/x-diff, inline)]
From 75f8c5c936deafea1ee44edad5e0f530ec6c4dfc Mon Sep 17 00:00:00 2001
From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
Date: Thu, 15 May 2025 21:54:06 -0400
Subject: [PATCH 2/3] mml: Enable production of Unobtrusive Signatures via epg

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

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

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

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

Signed-off-by: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
---
 lisp/gnus/mml-sec.el |  6 ++++++
 lisp/gnus/mml2015.el | 39 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 45 insertions(+)

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

[0003-mml-Add-C-c-RET-s-u-to-make-Unobtrusive-Signature.patch (text/x-diff, inline)]
From f03c16ddedaa0b7a39692629ab61e7c3b3e06201 Mon Sep 17 00:00:00 2001
From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
Date: Thu, 15 May 2025 22:03:25 -0400
Subject: [PATCH 3/3] mml: Add C-c RET s u to make Unobtrusive Signature

This adds to the default keymap to make it relatively easy to make an
Unobtrusive Signature when sending mail.

Signed-off-by: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
---
 lisp/gnus/mml-sec.el | 5 +++++
 lisp/gnus/mml.el     | 6 ++++--
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/lisp/gnus/mml-sec.el b/lisp/gnus/mml-sec.el
index 6fb82836e9a..9b703abd5f4 100644
--- a/lisp/gnus/mml-sec.el
+++ b/lisp/gnus/mml-sec.el
@@ -285,6 +285,11 @@ Use METHOD if given.  Else use `mml-secure-method' or
   (interactive nil mml-mode)
   (mml-secure-part "pgpmime" 'sign))
 
+(defun mml-secure-sign-unobtrusive ()
+  "Add MML tags to unobtrusively sign this MML part."
+  (interactive nil mml-mode)
+  (mml-secure-part "unobtrusive" 'sign))
+
 (defun mml-secure-sign-smime ()
   "Add MML tags to S/MIME sign this MML part."
   (interactive nil mml-mode)
diff --git a/lisp/gnus/mml.el b/lisp/gnus/mml.el
index 11a8de7c011..7d849335154 100644
--- a/lisp/gnus/mml.el
+++ b/lisp/gnus/mml.el
@@ -1178,11 +1178,13 @@ If HANDLES is non-nil, use it instead reparsing the buffer."
     "s" (define-keymap
           "p" #'mml-secure-message-sign-pgpmime
           "o" #'mml-secure-message-sign-pgp
-          "s" #'mml-secure-message-sign-smime)
+          "s" #'mml-secure-message-sign-smime
+          "u" #'mml-secure-message-sign-unobtrusive)
     "S" (define-keymap
           "p" #'mml-secure-sign-pgpmime
           "o" #'mml-secure-sign-pgp
-          "s" #'mml-secure-sign-smime)
+          "s" #'mml-secure-sign-smime
+          "u" #'mml-secure-message-sign-unobtrusive)
     "c" (define-keymap
           "p" #'mml-secure-message-encrypt-pgpmime
           "o" #'mml-secure-message-encrypt-pgp
-- 
2.47.2

[signature.asc (application/pgp-signature, inline)]

Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Sat, 17 May 2025 10:17:02 GMT) Full text and rfc822 format available.

Message #8 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>,
 Eric Abrahamsen <eric <at> ericabrahamsen.net>
Cc: 78448 <at> debbugs.gnu.org
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Sat, 17 May 2025 13:15:43 +0300
> From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
> Date: Thu, 15 May 2025 23:56:25 -0400
> 
> Package: emacs,gnus
> Version: 30.1
> 
> I'm running emacs 30.1 on debian.  When sending cleartext mail, i want
> to be able to produce an "Unobtrusive Signature" using OpenPGP as
> described in
> https://datatracker.ietf.org/doc/draft-gallagher-email-unobtrusive-signatures/
> 
> Those kinds of signatures provide end-to-end cryptographic protection
> for headers (based on
> https://datatracker.ietf.org/doc/draft-ietf-lamps-header-protection/ )
> as well as the message body, and are less likely to inflict bad UX or
> bad rendering on recipients using legacy MUAs compared to PGP/MIME (or
> inline PGP, for that matter).
> 
> These three patches appear to do the trick for me.  I'm not an elisp
> guru or a gnus expert.  I'm happy to hear any feedback about how they
> could be improved.

Eric, any comments to the patch or to the issue?

In any case, Daniel, to accept a contribution this large, we will need
you to assign to the FSF the copyright for your Emacs-related changes.
If you agree to do that, I will send you the form to fill and the
instructions to go with it.

Thanks.




Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Sun, 18 May 2025 03:17:01 GMT) Full text and rfc822 format available.

Message #11 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
To: Eli Zaretskii <eliz <at> gnu.org>, Eric Abrahamsen <eric <at> ericabrahamsen.net>
Cc: 78448 <at> debbugs.gnu.org
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Sat, 17 May 2025 23:16:39 -0400
[Message part 1 (text/plain, inline)]
Hi Eli--

On Sat 2025-05-17 13:15:43 +0300, Eli Zaretskii wrote:
> In any case, Daniel, to accept a contribution this large, we will need
> you to assign to the FSF the copyright for your Emacs-related changes.
> If you agree to do that, I will send you the form to fill and the
> instructions to go with it.

I'm pretty frustrated and disappointed with the FSF these days, but i
still care about emacs, so i'm willing to make a copyright assignment
for the sake of the tooling here and the broader ecosystem around it.

Please send me the form and instructions.

       --dkg
[signature.asc (application/pgp-signature, inline)]

Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Sun, 18 May 2025 05:17:02 GMT) Full text and rfc822 format available.

Message #14 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
Cc: eric <at> ericabrahamsen.net, 78448 <at> debbugs.gnu.org
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Sun, 18 May 2025 08:16:26 +0300
> From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
> Cc: 78448 <at> debbugs.gnu.org
> Date: Sat, 17 May 2025 23:16:39 -0400
> 
> Hi Eli--
> 
> On Sat 2025-05-17 13:15:43 +0300, Eli Zaretskii wrote:
> > In any case, Daniel, to accept a contribution this large, we will need
> > you to assign to the FSF the copyright for your Emacs-related changes.
> > If you agree to do that, I will send you the form to fill and the
> > instructions to go with it.
> 
> I'm pretty frustrated and disappointed with the FSF these days, but i
> still care about emacs, so i'm willing to make a copyright assignment
> for the sake of the tooling here and the broader ecosystem around it.
> 
> Please send me the form and instructions.

Thanks, form sent off-list.




Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Tue, 20 May 2025 18:48:01 GMT) Full text and rfc822 format available.

Message #17 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
To: 78448 <at> debbugs.gnu.org
Subject: [PATCH 3/3 v2] Re: 30.1; mml: Produce Unobtrusive Signatures
Date: Tue, 20 May 2025 14:47:00 -0400
[Message part 1 (text/plain, inline)]
Attached is a revised version of patch 3, which corrects the default
keybindings.

        --dkg

[0003-mml-Add-C-c-RET-s-u-to-make-Unobtrusive-Signature.patch (text/x-diff, inline)]
From a5de7ee57e1933a0783c8346db52c569074c07f7 Mon Sep 17 00:00:00 2001
From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
Date: Thu, 15 May 2025 22:03:25 -0400
Subject: [PATCH] mml: Add C-c RET s u to make Unobtrusive Signature

This adds to the default keymap to make it relatively easy to make an
Unobtrusive Signature when sending mail.

Unobtrusive Signatures are defined on a per-message basis, and
explicitly ignored per-part, so we do not facilitate part-based signing.

Signed-off-by: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
---
 lisp/gnus/mml-sec.el | 5 +++++
 lisp/gnus/mml.el     | 3 ++-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/lisp/gnus/mml-sec.el b/lisp/gnus/mml-sec.el
index 6fb82836e9a..71913c8e8cc 100644
--- a/lisp/gnus/mml-sec.el
+++ b/lisp/gnus/mml-sec.el
@@ -399,6 +399,11 @@ Use METHOD if given.  Else use `mml-secure-method' or
    (or method mml-secure-method mml-default-sign-method)
    'encrypt))
 
+(defun mml-secure-message-sign-unobtrusive ()
+  "Add MML tag to encrypt/sign the entire message."
+  (interactive nil mml-mode)
+  (mml-secure-message "unobtrusive" 'sign))
+
 (defun mml-secure-message-sign-smime ()
   "Add MML tag to encrypt/sign the entire message."
   (interactive nil mml-mode)
diff --git a/lisp/gnus/mml.el b/lisp/gnus/mml.el
index 11a8de7c011..52c9c63833a 100644
--- a/lisp/gnus/mml.el
+++ b/lisp/gnus/mml.el
@@ -1178,7 +1178,8 @@ If HANDLES is non-nil, use it instead reparsing the buffer."
     "s" (define-keymap
           "p" #'mml-secure-message-sign-pgpmime
           "o" #'mml-secure-message-sign-pgp
-          "s" #'mml-secure-message-sign-smime)
+          "s" #'mml-secure-message-sign-smime
+          "u" #'mml-secure-message-sign-unobtrusive)
     "S" (define-keymap
           "p" #'mml-secure-sign-pgpmime
           "o" #'mml-secure-sign-pgp
-- 
2.47.2


Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Wed, 28 May 2025 12:45:02 GMT) Full text and rfc822 format available.

Message #20 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Richard Stallman <rms <at> gnu.org>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: eric <at> ericabrahamsen.net, 78448 <at> debbugs.gnu.org, dkg <at> fifthhorseman.net
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Wed, 28 May 2025 08:44:21 -0400
[[[ To any NSA and FBI agents reading my email: please consider    ]]]
[[[ whether defending the US Constitution against all enemies,     ]]]
[[[ foreign or domestic, requires you to follow Snowden's example. ]]]

  > i want
  > > to be able to produce an "Unobtrusive Signature" using OpenPGP as
  > > described in
  > > https://datatracker.ietf.org/doc/draft-gallagher-email-unobtrusive-signatures/

Can GPG do this job also?  If so, we should by default use GPG to do
this, unless the user specifies a different command to use.

-- 
Dr Richard Stallman (https://stallman.org)
Chief GNUisance of the GNU Project (https://gnu.org)
Founder, Free Software Foundation (https://fsf.org)
Internet Hall-of-Famer (https://internethalloffame.org)






Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Wed, 28 May 2025 14:14:01 GMT) Full text and rfc822 format available.

Message #23 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
To: rms <at> gnu.org, Eli Zaretskii <eliz <at> gnu.org>
Cc: eric <at> ericabrahamsen.net, 78448 <at> debbugs.gnu.org
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Wed, 28 May 2025 10:07:25 -0400
[Message part 1 (text/plain, inline)]
On Wed 2025-05-28 08:44:21 -0400, Richard Stallman wrote:
> [ dkg wrote: ]
>   > > i want to be able to produce an "Unobtrusive Signature" using
>   > > OpenPGP as described in
>   > > https://datatracker.ietf.org/doc/draft-gallagher-email-unobtrusive-signatures/
>
> Can GPG do this job also?  If so, we should by default use GPG to do
> this, unless the user specifies a different command to use.

The proposed patch series explicitly relies on emacs's epg to make the
actual signature.  epg is an emacs library to use GPG.  So i think what
you're asking for is already being done in this series.

For background: GPG typically does not handle e-mails directly.  Rather,
the mail user agent (MUA) needs to figure out how to modify the pending
e-mail message to be able to feed it into GPG as a text or binary
document to make an OpenPGP signature; then the MUA has to restructure
the message again to ship both the substance and the signature into a
single e-mail message.

Regards,

        --dkg

Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Sat, 31 May 2025 09:22:03 GMT) Full text and rfc822 format available.

Message #26 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>, eric <at> ericabrahamsen.net
Cc: 78448 <at> debbugs.gnu.org, rms <at> gnu.org
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Sat, 31 May 2025 12:21:10 +0300
Ping!  Eric, would you please chime in and comment on the patch?

> From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
> Cc: eric <at> ericabrahamsen.net, 78448 <at> debbugs.gnu.org
> Date: Wed, 28 May 2025 10:07:25 -0400
> 
> On Wed 2025-05-28 08:44:21 -0400, Richard Stallman wrote:
> > [ dkg wrote: ]
> >   > > i want to be able to produce an "Unobtrusive Signature" using
> >   > > OpenPGP as described in
> >   > > https://datatracker.ietf.org/doc/draft-gallagher-email-unobtrusive-signatures/
> >
> > Can GPG do this job also?  If so, we should by default use GPG to do
> > this, unless the user specifies a different command to use.
> 
> The proposed patch series explicitly relies on emacs's epg to make the
> actual signature.  epg is an emacs library to use GPG.  So i think what
> you're asking for is already being done in this series.
> 
> For background: GPG typically does not handle e-mails directly.  Rather,
> the mail user agent (MUA) needs to figure out how to modify the pending
> e-mail message to be able to feed it into GPG as a text or binary
> document to make an OpenPGP signature; then the MUA has to restructure
> the message again to ship both the substance and the signature into a
> single e-mail message.
> 
> Regards,
> 
>         --dkg




Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Tue, 03 Jun 2025 09:33:01 GMT) Full text and rfc822 format available.

Message #29 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Robert Pluim <rpluim <at> gmail.com>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: eric <at> ericabrahamsen.net, 78448 <at> debbugs.gnu.org, rms <at> gnu.org,
 Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Tue, 03 Jun 2025 11:32:36 +0200
>>>>> On Sat, 31 May 2025 12:21:10 +0300, Eli Zaretskii <eliz <at> gnu.org> 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 <dkg <at> fifthhorseman.net>
    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 <dkg <at> fifthhorseman.net>

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 <dkg <at> fifthhorseman.net>
    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 <dkg <at> fifthhorseman.net>
    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
-- 




Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Fri, 06 Jun 2025 20:33:02 GMT) Full text and rfc822 format available.

Message #32 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
To: Robert Pluim <rpluim <at> gmail.com>, Eli Zaretskii <eliz <at> gnu.org>
Cc: eric <at> ericabrahamsen.net, 78448 <at> debbugs.gnu.org, rms <at> gnu.org
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Fri, 06 Jun 2025 16:32:18 -0400
[Message part 1 (text/plain, inline)]
Hi Robert--

Thank you for the review!

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.

Yes, unobtrusive signatures are defined with header protection
mandatory.

Please see
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).

OK, feel fre to strip Signed-off-by when applying, that doesn't matter
to me at all.  I'm already in communication with assign <at> gnu.org about
copyright assignment.

>     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.

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

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

Thanks!  That status was unclear to me.

>     Daniel> +  (re-search-forward "^")
>
> I think thatʼs better expressed as:
>
>     (forward-line 1)

Got it, thanks.

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

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

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

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

Regards,

        --dkg

Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Mon, 09 Jun 2025 12:46:02 GMT) Full text and rfc822 format available.

Message #35 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Robert Pluim <rpluim <at> gmail.com>
To: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
Cc: eric <at> ericabrahamsen.net, 78448 <at> debbugs.gnu.org,
 Eli Zaretskii <eliz <at> gnu.org>, rms <at> gnu.org
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Mon, 09 Jun 2025 14:45:23 +0200
>>>>> On Fri, 06 Jun 2025 16:32:18 -0400, Daniel Kahn Gillmor <dkg <at> fifthhorseman.net> 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 assign <at> gnu.org 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




Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Mon, 09 Jun 2025 22:55:02 GMT) Full text and rfc822 format available.

Message #38 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
To: Robert Pluim <rpluim <at> gmail.com>
Cc: eric <at> ericabrahamsen.net, 78448 <at> debbugs.gnu.org,
 Eli Zaretskii <eliz <at> gnu.org>, rms <at> gnu.org
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Mon, 09 Jun 2025 18:54:01 -0400
[Message part 1 (text/plain, inline)]
On Mon 2025-06-09 14:45:23 +0200, Robert Pluim wrote:
> I just tried it, I needed the small patch below to get it to compile
> without warnings.
[…]
> 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

Thanks for this!  I've updated the patch series to include your
feedback, and to drop the Signed-off-by: lines that were interfering
with the project expectations.

I'm including the three revised patches here.

Please let me know if you notice anything else that needs improvement.

Regards,

     --dkg

[0001-mml-Pass-likely-headers-through-to-mml-sec-functions.patch (text/x-diff, inline)]
From 3a27bb0ce3a18eff2fd965a1f7da6a97a45a6744 Mon Sep 17 00:00:00 2001
From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
Date: Thu, 15 May 2025 21:49:32 -0400
Subject: [PATCH 1/3] mml: Pass likely headers through to mml-sec functions

By pre-computing the likely headers for an outbound message, and passing
them along as a tag in mml-parse, we create an opportunity to provide
Header Protection as described in
https://datatracker.ietf.org/doc/draft-ietf-lamps-header-protection/
---
 lisp/gnus/mml.el | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/lisp/gnus/mml.el b/lisp/gnus/mml.el
index 51d8d2c3769..8e37dbe0bf6 100644
--- a/lisp/gnus/mml.el
+++ b/lisp/gnus/mml.el
@@ -265,6 +265,8 @@ part.  This is for the internal use, you should never modify the value.")
 	  (apply #'mml-insert-tag
 		 secure-mode
 		 `(,@tags
+                   ,"likely-headers"
+                   ,(mml-get-likely-headers)
 		   ,(if keyfile "keyfile")
 		   ,keyfile
 		   ,@(apply #'append
@@ -492,6 +494,24 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
 (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
+    (save-restriction
+      (message-narrow-to-headers-or-head)
+      (let ((x (buffer-substring (point-min) (point-max))))
+        (with-temp-buffer
+          (insert x)
+          (message-remove-header "Bcc")
+          (message-remove-header message-ignored-mail-headers t)
+          (mail-encode-encoded-word-buffer)
+          (message-cleanup-headers)
+          (buffer-string)
+          )))))
+
 (defun mml-generate-mime (&optional multipart-type content-type)
   "Generate a MIME message based on the current MML document.
 MULTIPART-TYPE defaults to \"mixed\", but can also
-- 
2.47.2

[0002-mml-Enable-production-of-Unobtrusive-Signatures-via-.patch (text/x-diff, inline)]
From 9b4beb63f36c7af2f7b5b63c8fd81f2ac7bc877e Mon Sep 17 00:00:00 2001
From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
Date: Thu, 15 May 2025 21:54:06 -0400
Subject: [PATCH 2/3] mml: Enable production of Unobtrusive Signatures via epg

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

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

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

This patch only works with epg, not with mailcrypt or pgg, because epg
is what i'm familiar with and what i can easily test.
---
 lisp/gnus/mml-sec.el |  6 ++++++
 lisp/gnus/mml2015.el | 39 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 45 insertions(+)

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

[0003-mml-Add-C-c-RET-s-u-to-make-Unobtrusive-Signature.patch (text/x-diff, inline)]
From 8ec14c812328ae1b4180656f8813211d83215b42 Mon Sep 17 00:00:00 2001
From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
Date: Thu, 15 May 2025 22:03:25 -0400
Subject: [PATCH 3/3] mml: Add C-c RET s u to make Unobtrusive Signature

This adds to the default keymap to make it relatively easy to make an
Unobtrusive Signature when sending mail.

Unobtrusive Signatures are defined on a per-message basis, and
explicitly ignored per-part, so we do not facilitate part-based signing.
---
 lisp/gnus/mml-sec.el | 5 +++++
 lisp/gnus/mml.el     | 3 ++-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/lisp/gnus/mml-sec.el b/lisp/gnus/mml-sec.el
index 6fb82836e9a..71913c8e8cc 100644
--- a/lisp/gnus/mml-sec.el
+++ b/lisp/gnus/mml-sec.el
@@ -399,6 +399,11 @@ Use METHOD if given.  Else use `mml-secure-method' or
    (or method mml-secure-method mml-default-sign-method)
    'encrypt))
 
+(defun mml-secure-message-sign-unobtrusive ()
+  "Add MML tag to encrypt/sign the entire message."
+  (interactive nil mml-mode)
+  (mml-secure-message "unobtrusive" 'sign))
+
 (defun mml-secure-message-sign-smime ()
   "Add MML tag to encrypt/sign the entire message."
   (interactive nil mml-mode)
diff --git a/lisp/gnus/mml.el b/lisp/gnus/mml.el
index 8e37dbe0bf6..0667c937803 100644
--- a/lisp/gnus/mml.el
+++ b/lisp/gnus/mml.el
@@ -1181,7 +1181,8 @@ If HANDLES is non-nil, use it instead reparsing the buffer."
     "s" (define-keymap
           "p" #'mml-secure-message-sign-pgpmime
           "o" #'mml-secure-message-sign-pgp
-          "s" #'mml-secure-message-sign-smime)
+          "s" #'mml-secure-message-sign-smime
+          "u" #'mml-secure-message-sign-unobtrusive)
     "S" (define-keymap
           "p" #'mml-secure-sign-pgpmime
           "o" #'mml-secure-sign-pgp
-- 
2.47.2


Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Fri, 13 Jun 2025 17:59:03 GMT) Full text and rfc822 format available.

Message #41 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
To: Robert Pluim <rpluim <at> gmail.com>
Cc: eric <at> ericabrahamsen.net, 78448 <at> debbugs.gnu.org,
 Eli Zaretskii <eliz <at> gnu.org>, rms <at> gnu.org,
 Michael Richardson <mcr <at> sandelman.ca>
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Fri, 13 Jun 2025 12:22:14 -0400
[Message part 1 (text/plain, inline)]
On Mon 2025-06-09 18:54:01 -0400, Daniel Kahn Gillmor wrote:
> I'm including the three revised patches here.

Michael Richardson observed that I had an extra semicolon which might
cause a complaint (see the patch here, which you might decide you want
to merge down into patch 0002 when applying).

I'm grateful for his attention to detail!

   --dkg

[0004-Remove-stray-semicolon.patch (text/x-diff, inline)]
From d15a519c3c8d9cad72a827f8fba24fb79df25db5 Mon Sep 17 00:00:00 2001
From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
Date: Thu, 12 Jun 2025 18:06:22 -0400
Subject: [PATCH] Remove stray semicolon

Identified by Michael Richardson <mcr <at> sandelman.ca>
---
 lisp/gnus/mml2015.el | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lisp/gnus/mml2015.el b/lisp/gnus/mml2015.el
index bbe6cec589f..503b7a8a5fa 100644
--- a/lisp/gnus/mml2015.el
+++ b/lisp/gnus/mml2015.el
@@ -977,7 +977,7 @@ If set, it overrides the setting of `mml2015-sign-with-sender'."
                     (pgpsig-armor-to-wrapped-b64 signature)))
     (let ((boundary (mml-compute-boundary cont)))
       (goto-char (point-min))
-      (insert (format "Content-Type: multipart/mixed; boundary=\"%s\";\n"
+      (insert (format "Content-Type: multipart/mixed; boundary=\"%s\"\n"
 		      boundary))
       (insert (format "\n--%s\n" boundary))
       (goto-char (point-max))
-- 
2.47.2


Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Wed, 29 Oct 2025 16:32:01 GMT) Full text and rfc822 format available.

Message #44 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
To: 78448 <at> debbugs.gnu.org, Eli Zaretskii <eliz <at> gnu.org>
Subject: Re: 30.1; mml: Produce Unobtrusive Signatures
Date: Wed, 29 Oct 2025 12:30:30 -0400
Hey folks--

Just wondering where this work on emitting unobtrusive signatures stands
in terms of merging.

If I understand correctly, all the paperwork involving my employer and
the FSF has been completed.

I just checked, and the series looks to me like it applies cleanly to
the emacs-30 branch and to the master branch as seen on
https://git.savannah.gnu.org/git/emacs.git

Please let me know if there's anything else you'd like me to improve!

    --dkg




Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Wed, 29 Oct 2025 17:02:01 GMT) Full text and rfc822 format available.

Message #47 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>,
 Robert Pluim <rpluim <at> gmail.com>
Cc: 78448 <at> debbugs.gnu.org
Subject: Re: 30.1; mml: Produce Unobtrusive Signatures
Date: Wed, 29 Oct 2025 19:00:30 +0200
> From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
> Date: Wed, 29 Oct 2025 12:30:30 -0400
> 
> Hey folks--
> 
> Just wondering where this work on emitting unobtrusive signatures stands
> in terms of merging.
> 
> If I understand correctly, all the paperwork involving my employer and
> the FSF has been completed.

Sorry, I don't see your assignment on file yet.

Did you receive from the FSF the assignment signed both by you and by
the FSF?  If not, please ping them and CC me.

> I just checked, and the series looks to me like it applies cleanly to
> the emacs-30 branch and to the master branch as seen on
> https://git.savannah.gnu.org/git/emacs.git
> 
> Please let me know if there's anything else you'd like me to improve!

I don't use Gnus, and Robert (CC'd) was the only one who reviewed the
patches.  If Robert is happy with the last version, we can install it,
once your assignment paperwork is complete.

Thanks.




Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Sat, 15 Nov 2025 09:09:01 GMT) Full text and rfc822 format available.

Message #50 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: rpluim <at> gmail.com
Cc: 78448 <at> debbugs.gnu.org, dkg <at> fifthhorseman.net
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Sat, 15 Nov 2025 11:08:17 +0200
> Cc: 78448 <at> debbugs.gnu.org
> Date: Wed, 29 Oct 2025 19:00:30 +0200
> From: Eli Zaretskii <eliz <at> gnu.org>
> 
> > From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
> > Date: Wed, 29 Oct 2025 12:30:30 -0400
> > 
> > Hey folks--
> > 
> > Just wondering where this work on emitting unobtrusive signatures stands
> > in terms of merging.
> > 
> > If I understand correctly, all the paperwork involving my employer and
> > the FSF has been completed.
> 
> Sorry, I don't see your assignment on file yet.
> 
> Did you receive from the FSF the assignment signed both by you and by
> the FSF?  If not, please ping them and CC me.
> 
> > I just checked, and the series looks to me like it applies cleanly to
> > the emacs-30 branch and to the master branch as seen on
> > https://git.savannah.gnu.org/git/emacs.git
> > 
> > Please let me know if there's anything else you'd like me to improve!
> 
> I don't use Gnus, and Robert (CC'd) was the only one who reviewed the
> patches.  If Robert is happy with the last version, we can install it,
> once your assignment paperwork is complete.

Robert, is it okay to install these changes?  The copyright issue is
resolved for these contributions, so if you are happy with the
changes, please install them.

Thanks.




Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Sat, 15 Nov 2025 11:49:01 GMT) Full text and rfc822 format available.

Message #53 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
To: Eli Zaretskii <eliz <at> gnu.org>, rpluim <at> gmail.com
Cc: 78448 <at> debbugs.gnu.org
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Sat, 15 Nov 2025 06:48:44 -0500
[Message part 1 (text/plain, inline)]
On Sat 2025-11-15 11:08:17 +0200, Eli Zaretskii wrote:
> Robert, is it okay to install these changes?  The copyright issue is
> resolved for these contributions, so if you are happy with the
> changes, please install them.

I'm glad this has been resolved now!

I think Robert asked me for a squashed patch, so i've done that, and I
offer it here.

Thanks for shepherding this process, Eli and Robert!  Let me know if you
need anything else from me.

       --dkg

[0001-mml-generate-unobtrusive-signatures.patch (text/x-diff, inline)]
From 708acaacb3e54c21d357b44007e422c9bda142c8 Mon Sep 17 00:00:00 2001
From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
Date: Thu, 15 May 2025 21:49:32 -0400
Subject: [PATCH] mml: generate unobtrusive signatures

- Pass likely headers through to mml-sec functions

By pre-computing the likely headers for an outbound message, and passing
them along as a tag in mml-parse, we create an opportunity to provide
Header Protection as described in
https://datatracker.ietf.org/doc/draft-ietf-lamps-header-protection/

- produce Unobtrusive Signatures via epg

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

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

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

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

- Add C-c RET s u to make Unobtrusive Signature

This adds to the default keymap to make it relatively easy to make an
Unobtrusive Signature when sending mail.

Unobtrusive Signatures are defined on a per-message basis, and
explicitly ignored per-part, so we do not facilitate part-based signing.
---
 lisp/gnus/mml-sec.el | 11 +++++++++++
 lisp/gnus/mml.el     | 20 +++++++++++++++++++-
 lisp/gnus/mml2015.el | 39 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 69 insertions(+), 1 deletion(-)

diff --git a/lisp/gnus/mml-sec.el b/lisp/gnus/mml-sec.el
index 8dffcf872f3..71913c8e8cc 100644
--- a/lisp/gnus/mml-sec.el
+++ b/lisp/gnus/mml-sec.el
@@ -34,6 +34,7 @@
 (autoload 'mail-strip-quoted-names "mail-utils")
 (autoload 'mml2015-sign "mml2015")
 (autoload 'mml2015-encrypt "mml2015")
+(autoload 'mml-unobtrusive-sign "mml2015")
 (autoload 'mml1991-sign "mml1991")
 (autoload 'mml1991-encrypt "mml1991")
 (autoload 'message-fetch-field "message")
@@ -56,6 +57,7 @@
   '(("smime"     mml-smime-sign-buffer     mml-smime-sign-query)
     ("pgp"       mml-pgp-sign-buffer       list)
     ("pgpauto"   mml-pgpauto-sign-buffer  list)
+    ("unobtrusive" mml-unobtrusive-sign-buffer list)
     ("pgpmime"   mml-pgpmime-sign-buffer   list))
   "Alist of MIME signer functions.")
 
@@ -198,6 +200,10 @@ You can also customize or set `mml-signencrypt-style-alist' instead."
   (or (mml2015-sign cont)
       (error "Signing failed... inspect message logs for errors")))
 
+(defun mml-unobtrusive-sign-buffer (cont)
+  (or (mml-unobtrusive-sign cont)
+      (error "Signing failed... inspect message logs for errors")))
+
 (defun mml-pgpmime-encrypt-buffer (cont &optional sign)
   (or (mml2015-encrypt cont sign)
       (error "Encryption failed... inspect message logs for errors")))
@@ -393,6 +399,11 @@ Use METHOD if given.  Else use `mml-secure-method' or
    (or method mml-secure-method mml-default-sign-method)
    'encrypt))
 
+(defun mml-secure-message-sign-unobtrusive ()
+  "Add MML tag to encrypt/sign the entire message."
+  (interactive nil mml-mode)
+  (mml-secure-message "unobtrusive" 'sign))
+
 (defun mml-secure-message-sign-smime ()
   "Add MML tag to encrypt/sign the entire message."
   (interactive nil mml-mode)
diff --git a/lisp/gnus/mml.el b/lisp/gnus/mml.el
index e53d35146e8..972f1bce0a1 100644
--- a/lisp/gnus/mml.el
+++ b/lisp/gnus/mml.el
@@ -265,6 +265,8 @@ part.  This is for the internal use, you should never modify the value.")
 	  (apply #'mml-insert-tag
 		 secure-mode
 		 `(,@tags
+                   ,"likely-headers"
+                   ,(mml-get-likely-headers)
 		   ,(if keyfile "keyfile")
 		   ,keyfile
 		   ,@(apply #'append
@@ -492,6 +494,21 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
 (declare-function libxml-parse-html-region "xml.c"
 		  (start end &optional base-url discard-comments))
 
+(defun mml-get-likely-headers ()
+  "Get likely final headers from the existing message"
+  (save-excursion
+    (save-restriction
+      (message-narrow-to-headers-or-head)
+      (let ((x (buffer-substring (point-min) (point-max))))
+        (with-temp-buffer
+          (insert x)
+          (message-remove-header "Bcc")
+          (message-remove-header message-ignored-mail-headers t)
+          (mail-encode-encoded-word-buffer)
+          (message-cleanup-headers)
+          (buffer-string)
+          )))))
+
 (defun mml-generate-mime (&optional multipart-type content-type)
   "Generate a MIME message based on the current MML document.
 MULTIPART-TYPE defaults to \"mixed\", but can also
@@ -1161,7 +1178,8 @@ If HANDLES is non-nil, use it instead reparsing the buffer."
     "s" (define-keymap
           "p" #'mml-secure-message-sign-pgpmime
           "o" #'mml-secure-message-sign-pgp
-          "s" #'mml-secure-message-sign-smime)
+          "s" #'mml-secure-message-sign-smime
+          "u" #'mml-secure-message-sign-unobtrusive)
     "S" (define-keymap
           "p" #'mml-secure-sign-pgpmime
           "o" #'mml-secure-sign-pgp
diff --git a/lisp/gnus/mml2015.el b/lisp/gnus/mml2015.el
index a46aa68f56a..646fb018a31 100644
--- a/lisp/gnus/mml2015.el
+++ b/lisp/gnus/mml2015.el
@@ -25,6 +25,9 @@
 ;; RFC 2015 is updated by RFC 3156, this file should be compatible
 ;; with both.
 
+;; This is also capable of producing unobtrusive signatures based on
+;; draft-gallagher-email-unobtrusive-signatures
+
 ;;; Code:
 
 (eval-when-compile (require 'cl-lib))
@@ -945,6 +948,42 @@ If set, it overrides the setting of `mml2015-sign-with-sender'."
     (insert (format "--%s--\n" boundary))
     (goto-char (point-max))))
 
+;;; Unobtrusive Signatures, see:
+;;; https://datatracker.ietf.org/doc/draft-gallagher-email-unobtrusive-signatures/
+
+; convert ASCII-armored PGP SIGNATURE block to base64-encoded with FWS
+; at the start of each line:
+(defun pgpsig-armor-to-wrapped-b64 (s)
+  (string-join
+   (string-split
+    (string-trim-right
+     (string-trim-left s "-----BEGIN PGP SIGNATURE-----\n\\(?:[^\n]+\n\\)*\n")
+     "\n\\(?:=....\n\\)?-----END PGP SIGNATURE-----\n?")
+    "\n")
+   "\n "))
+
+(defun mml-unobtrusive-sign (cont)
+  (goto-char (point-min))
+  (insert (cdr (assq 'likely-headers cont)))
+  (re-search-forward "^Content-Type: [^\n]*\\(\n[ \t][^\n]*$\\)*")
+  (insert "; hp=\"clear\"")
+  (re-search-forward "^")
+  (let* ((pair (mml-secure-epg-sign 'OpenPGP t))
+	 (signature (car pair)))
+    (unless (stringp signature)
+      (error "Signature failed"))
+    (goto-char (point-min))
+    (insert (format "Sig: t=p; b=%s\n"
+                    (pgpsig-armor-to-wrapped-b64 signature)))
+    (let ((boundary (mml-compute-boundary cont)))
+      (goto-char (point-min))
+      (insert (format "Content-Type: multipart/mixed; boundary=\"%s\";\n"
+		      boundary))
+      (insert (format "\n--%s\n" boundary))
+      (goto-char (point-max))
+      (insert (format "\n--%s--\n" boundary))
+      (goto-char (point-max)))))
+
 ;;; General wrapper
 
 (autoload 'gnus-buffer-live-p "gnus-util")
-- 
2.51.0

[signature.asc (application/pgp-signature, inline)]

Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Thu, 20 Nov 2025 15:17:02 GMT) Full text and rfc822 format available.

Message #56 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Robert Pluim <rpluim <at> gmail.com>
To: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
Cc: 78448 <at> debbugs.gnu.org, Eli Zaretskii <eliz <at> gnu.org>
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Thu, 20 Nov 2025 16:16:09 +0100
>>>>> On Sat, 15 Nov 2025 06:48:44 -0500, Daniel Kahn Gillmor <dkg <at> fifthhorseman.net> said:

    Daniel> On Sat 2025-11-15 11:08:17 +0200, Eli Zaretskii wrote:
    >> Robert, is it okay to install these changes?  The copyright issue is
    >> resolved for these contributions, so if you are happy with the
    >> changes, please install them.

    Daniel> I'm glad this has been resolved now!

    Daniel> I think Robert asked me for a squashed patch, so i've done that, and I
    Daniel> offer it here.

    Daniel> Thanks for shepherding this process, Eli and Robert!  Let me know if you
    Daniel> need anything else from me.

I get byte-compiler warnings after I apply the patch, could you take a
look?

Also, we normally require a ChangeLog style commit message. I can
write one for this change and show it to you.

Thanks

Robert
-- 




Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Tue, 25 Nov 2025 20:17:23 GMT) Full text and rfc822 format available.

Message #59 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
To: Robert Pluim <rpluim <at> gmail.com>
Cc: 78448 <at> debbugs.gnu.org, Eli Zaretskii <eliz <at> gnu.org>
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Fri, 21 Nov 2025 11:01:34 -0500
[Message part 1 (text/plain, inline)]
Hi Robert--

On Thu 2025-11-20 16:16:09 +0100, Robert Pluim wrote:
> I get byte-compiler warnings after I apply the patch, could you take a
> look?

I'd be happy to look further.  What byte-compiler warnings are you
seeing?  What steps do you take to trigger those warnings?

> Also, we normally require a ChangeLog style commit message. I can
> write one for this change and show it to you.

Thanks, i'd appreciate that.  I'm used to intent-oriented commit
messages, since they explain the *why* of the change; I usually let the
diff itself shows what specifically changed.  But i'm happy to use
whatever is the preferred convention.

         --dkg
[signature.asc (application/pgp-signature, inline)]

Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Fri, 05 Dec 2025 23:17:02 GMT) Full text and rfc822 format available.

Message #62 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
To: Robert Pluim <rpluim <at> gmail.com>
Cc: 78448 <at> debbugs.gnu.org, Eli Zaretskii <eliz <at> gnu.org>
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Fri, 05 Dec 2025 18:16:01 -0500
[Message part 1 (text/plain, inline)]
Hi Robert--

On Fri 2025-11-21 11:01:34 -0500, Daniel Kahn Gillmor wrote:
> On Thu 2025-11-20 16:16:09 +0100, Robert Pluim wrote:
>> I get byte-compiler warnings after I apply the patch, could you take a
>> look?
>
> I'd be happy to look further.  What byte-compiler warnings are you
> seeing?  What steps do you take to trigger those warnings?
>
>> Also, we normally require a ChangeLog style commit message. I can
>> write one for this change and show it to you.

I think i've identified th byte-compiler warnings and cleaned them up by
forward-declaring some functions and variables.

I also identified some logic in mml.el that caused a spurious warning
(to *Messages*) when signing, based on a presumption that the elements
in mml-sign-alist matched mml-encryptsign-alist.  Since unobtrusive
signatures are only acceptable in signed-only mail, that assumption is
no longer true.  I've included a patch that avoids that error without
changing the underlying logic.

Since unobtrusive signatures are now formally adopted by the IETF's
MAILMAINT working group, i've also updated comments to point to the
specification's new landing page.

I've also updated the commit message to be in what i think is "Changelog
style".  Please let me know if you have any other concerns or
recommendations.  It would be great to get this landed in mml-mode!

All the best,

    --dkg

[0001-mml-Enable-generation-of-Unobtrusively-Signed-mail-m.patch (text/x-diff, inline)]
From 91bab92ab2e6e6ea3a56403ea3c41b01a4258451 Mon Sep 17 00:00:00 2001
From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
Date: Thu, 15 May 2025 21:49:32 -0400
Subject: [PATCH] mml: Enable generation of Unobtrusively Signed mail messages

Implement draft-ietf-mailmaint-email-unobtrusive-signatures (Bug#78448)

* lisp/gnus/mml2015.el (pgpsig-armor-to-wrapped-b64): new function.
  (mml-unobtrusive-sign): new function.
* lisp/gnus/mml.el (mml-get-likely-headers): predict
  headers for the newly composed message. (mml-parse-1): copy likely
  headers to a tag during message parsing. (mml-mode-map): add
  keybinding "C-c RET s u" to create an unobtrusively signed
  message. (mml-generate-mime-1): clean up logic to avoid invoking
  mml-signencrypt-style when message is not encrypted.
* lisp/gnus/mml-sec.el (mml-unobtrusive-sign-buffer,
  mml-secure-message-sign-unobtrusive): new functions, using likely
  headers for RFC 9788 Header Protection. (mml-sign-alist):
  Add "unobtrusive" as a signature style.

--

https://datatracker.ietf.org/doc/draft-ietf-mailmaint-email-unobtrusive-signatures/
describes a way to generate signed e-mail messages that will not cause
problems with legacy mail user agents.

This patch lets mml generate this sort of signature on outbound
signed-only mail.

Unobtrusive signatures require RFC 9788-style header protection, so this
patch enables that sort of header protection as well during the signing process.

This patch only works with epg, not with mailcrypt or pgg.
---
 lisp/gnus/mml-sec.el | 11 +++++++++
 lisp/gnus/mml.el     | 57 +++++++++++++++++++++++++++++---------------
 lisp/gnus/mml2015.el | 39 ++++++++++++++++++++++++++++++
 3 files changed, 88 insertions(+), 19 deletions(-)

diff --git a/lisp/gnus/mml-sec.el b/lisp/gnus/mml-sec.el
index 8dffcf872f3..71913c8e8cc 100644
--- a/lisp/gnus/mml-sec.el
+++ b/lisp/gnus/mml-sec.el
@@ -34,6 +34,7 @@
 (autoload 'mail-strip-quoted-names "mail-utils")
 (autoload 'mml2015-sign "mml2015")
 (autoload 'mml2015-encrypt "mml2015")
+(autoload 'mml-unobtrusive-sign "mml2015")
 (autoload 'mml1991-sign "mml1991")
 (autoload 'mml1991-encrypt "mml1991")
 (autoload 'message-fetch-field "message")
@@ -56,6 +57,7 @@
   '(("smime"     mml-smime-sign-buffer     mml-smime-sign-query)
     ("pgp"       mml-pgp-sign-buffer       list)
     ("pgpauto"   mml-pgpauto-sign-buffer  list)
+    ("unobtrusive" mml-unobtrusive-sign-buffer list)
     ("pgpmime"   mml-pgpmime-sign-buffer   list))
   "Alist of MIME signer functions.")
 
@@ -198,6 +200,10 @@ You can also customize or set `mml-signencrypt-style-alist' instead."
   (or (mml2015-sign cont)
       (error "Signing failed... inspect message logs for errors")))
 
+(defun mml-unobtrusive-sign-buffer (cont)
+  (or (mml-unobtrusive-sign cont)
+      (error "Signing failed... inspect message logs for errors")))
+
 (defun mml-pgpmime-encrypt-buffer (cont &optional sign)
   (or (mml2015-encrypt cont sign)
       (error "Encryption failed... inspect message logs for errors")))
@@ -393,6 +399,11 @@ Use METHOD if given.  Else use `mml-secure-method' or
    (or method mml-secure-method mml-default-sign-method)
    'encrypt))
 
+(defun mml-secure-message-sign-unobtrusive ()
+  "Add MML tag to encrypt/sign the entire message."
+  (interactive nil mml-mode)
+  (mml-secure-message "unobtrusive" 'sign))
+
 (defun mml-secure-message-sign-smime ()
   "Add MML tag to encrypt/sign the entire message."
   (interactive nil mml-mode)
diff --git a/lisp/gnus/mml.el b/lisp/gnus/mml.el
index e53d35146e8..3154b6d326a 100644
--- a/lisp/gnus/mml.el
+++ b/lisp/gnus/mml.el
@@ -40,10 +40,11 @@
 (autoload 'message-posting-charset "message")
 (autoload 'dnd-get-local-file-name "dnd")
 
-(autoload 'message-options-set    "message")
-(autoload 'message-narrow-to-head "message")
-(autoload 'message-in-body-p      "message")
-(autoload 'message-mail-p         "message")
+(autoload 'message-options-set     "message")
+(autoload 'message-narrow-to-head  "message")
+(autoload 'message-in-body-p       "message")
+(autoload 'message-mail-p          "message")
+(autoload 'message-cleanup-headers "message")
 
 (defvar gnus-article-mime-handles)
 (defvar gnus-newsrc-hashtb)
@@ -51,6 +52,7 @@
 (defvar message-options)
 (defvar message-posting-charset)
 (defvar message-required-mail-headers)
+(defvar message-ignored-mail-headers)
 (defvar message-required-news-headers)
 (defvar dnd-protocol-alist)
 (defvar mml-dnd-protocol-alist)
@@ -265,6 +267,8 @@ part.  This is for the internal use, you should never modify the value.")
 	  (apply #'mml-insert-tag
 		 secure-mode
 		 `(,@tags
+                   ,"likely-headers"
+                   ,(mml-get-likely-headers)
 		   ,(if keyfile "keyfile")
 		   ,keyfile
 		   ,@(apply #'append
@@ -492,6 +496,21 @@ If MML is non-nil, return the buffer up till the correspondent mml tag."
 (declare-function libxml-parse-html-region "xml.c"
 		  (start end &optional base-url discard-comments))
 
+(defun mml-get-likely-headers ()
+  "Get likely final headers from the existing message"
+  (save-excursion
+    (save-restriction
+      (message-narrow-to-headers-or-head)
+      (let ((x (buffer-substring (point-min) (point-max))))
+        (with-temp-buffer
+          (insert x)
+          (message-remove-header "Bcc")
+          (message-remove-header message-ignored-mail-headers t)
+          (mail-encode-encoded-word-buffer)
+          (message-cleanup-headers)
+          (buffer-string)
+          )))))
+
 (defun mml-generate-mime (&optional multipart-type content-type)
   "Generate a MIME message based on the current MML document.
 MULTIPART-TYPE defaults to \"mixed\", but can also
@@ -832,20 +851,19 @@ type detected."
 	    (message-options-set 'message-sender sender))
 	  (if (setq recipients (cdr (assq 'recipients cont)))
 	      (message-options-set 'message-recipients recipients))
-	  (let ((style (mml-signencrypt-style
-			(car (or sign-item encrypt-item)))))
-	    ;; check if: we're both signing & encrypting, both methods
-	    ;; are the same (why would they be different?!), and that
-	    ;; the signencrypt style allows for combined operation.
-	    (if (and sign-item encrypt-item (equal (car sign-item)
-						   (car encrypt-item))
-		     (equal style 'combined))
-		(funcall (nth 1 encrypt-item) cont t)
-	      ;; otherwise, revert to the old behavior.
-	      (when sign-item
-		(funcall (nth 1 sign-item) cont))
-	      (when encrypt-item
-		(funcall (nth 1 encrypt-item) cont)))))))))
+	  ;; check if: we're both signing & encrypting, both methods
+	  ;; are the same (why would they be different?!), and that
+	  ;; the signencrypt style allows for combined operation.
+	  (if (and sign-item encrypt-item (equal (car sign-item)
+						 (car encrypt-item))
+		   (equal 'combined (mml-signencrypt-style
+			             (car encrypt-item))))
+	      (funcall (nth 1 encrypt-item) cont t)
+	    ;; otherwise, revert to the old behavior.
+	    (when sign-item
+	      (funcall (nth 1 sign-item) cont))
+	    (when encrypt-item
+	      (funcall (nth 1 encrypt-item) cont))))))))
 
 (defun mml-compute-boundary (cont)
   "Return a unique boundary that does not exist in CONT."
@@ -1161,7 +1179,8 @@ If HANDLES is non-nil, use it instead reparsing the buffer."
     "s" (define-keymap
           "p" #'mml-secure-message-sign-pgpmime
           "o" #'mml-secure-message-sign-pgp
-          "s" #'mml-secure-message-sign-smime)
+          "s" #'mml-secure-message-sign-smime
+          "u" #'mml-secure-message-sign-unobtrusive)
     "S" (define-keymap
           "p" #'mml-secure-sign-pgpmime
           "o" #'mml-secure-sign-pgp
diff --git a/lisp/gnus/mml2015.el b/lisp/gnus/mml2015.el
index a46aa68f56a..45b64d5246f 100644
--- a/lisp/gnus/mml2015.el
+++ b/lisp/gnus/mml2015.el
@@ -25,6 +25,9 @@
 ;; RFC 2015 is updated by RFC 3156, this file should be compatible
 ;; with both.
 
+;; This is also capable of producing unobtrusive signatures based on
+;; draft-ietf-mailmaint-email-unobtrusive-signatures
+
 ;;; Code:
 
 (eval-when-compile (require 'cl-lib))
@@ -945,6 +948,42 @@ If set, it overrides the setting of `mml2015-sign-with-sender'."
     (insert (format "--%s--\n" boundary))
     (goto-char (point-max))))
 
+;;; Unobtrusive Signatures, see:
+;;; https://datatracker.ietf.org/doc/draft-ietf-mailmaint-email-unobtrusive-signatures/
+
+; convert ASCII-armored PGP SIGNATURE block to base64-encoded with FWS
+; at the start of each line:
+(defun pgpsig-armor-to-wrapped-b64 (s)
+  (string-join
+   (string-split
+    (string-trim-right
+     (string-trim-left s "-----BEGIN PGP SIGNATURE-----\n\\(?:[^\n]+\n\\)*\n")
+     "\n\\(?:=....\n\\)?-----END PGP SIGNATURE-----\n?")
+    "\n")
+   "\n "))
+
+(defun mml-unobtrusive-sign (cont)
+  (goto-char (point-min))
+  (insert (cdr (assq 'likely-headers cont)))
+  (re-search-forward "^Content-Type: [^\n]*\\(\n[ \t][^\n]*$\\)*")
+  (insert "; hp=\"clear\"")
+  (re-search-forward "^")
+  (let* ((pair (mml-secure-epg-sign 'OpenPGP t))
+	 (signature (car pair)))
+    (unless (stringp signature)
+      (error "Signature failed"))
+    (goto-char (point-min))
+    (insert (format "Sig: t=p; b=%s\n"
+                    (pgpsig-armor-to-wrapped-b64 signature)))
+    (let ((boundary (mml-compute-boundary cont)))
+      (goto-char (point-min))
+      (insert (format "Content-Type: multipart/mixed; boundary=\"%s\";\n"
+		      boundary))
+      (insert (format "\n--%s\n" boundary))
+      (goto-char (point-max))
+      (insert (format "\n--%s--\n" boundary))
+      (goto-char (point-max)))))
+
 ;;; General wrapper
 
 (autoload 'gnus-buffer-live-p "gnus-util")
-- 
2.51.0

[signature.asc (application/pgp-signature, inline)]

Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Wed, 17 Dec 2025 15:49:02 GMT) Full text and rfc822 format available.

Message #65 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
To: Robert Pluim <rpluim <at> gmail.com>
Cc: 78448 <at> debbugs.gnu.org, Eli Zaretskii <eliz <at> gnu.org>
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Wed, 17 Dec 2025 10:48:28 -0500
[Message part 1 (text/plain, inline)]
Hi Robert, Eli--

Just a nudge here for unobtrusive signatures.  It looks to me like
everything is in order:

- copyright assignment paperwork is completed
- byte-compiler warnings are resolved
- spurious Message warning is avoided
- code merged down to a single patch
- commit message is in (what i think is) changelog style

Do y'all need anything else from me to land this in emacs?

         --dkg

Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Wed, 17 Dec 2025 16:46:01 GMT) Full text and rfc822 format available.

Message #68 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
Cc: 78448 <at> debbugs.gnu.org, rpluim <at> gmail.com
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Wed, 17 Dec 2025 18:45:37 +0200
> From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
> Cc: Eli Zaretskii <eliz <at> gnu.org>, 78448 <at> debbugs.gnu.org
> Date: Wed, 17 Dec 2025 10:48:28 -0500
> 
> Hi Robert, Eli--
> 
> Just a nudge here for unobtrusive signatures.  It looks to me like
> everything is in order:
> 
> - copyright assignment paperwork is completed
> - byte-compiler warnings are resolved
> - spurious Message warning is avoided
> - code merged down to a single patch
> - commit message is in (what i think is) changelog style
> 
> Do y'all need anything else from me to land this in emacs?

I'd like Robert to review the latest version of the patch.




Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Thu, 18 Dec 2025 09:44:02 GMT) Full text and rfc822 format available.

Message #71 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Robert Pluim <rpluim <at> gmail.com>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 78448 <at> debbugs.gnu.org, Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Thu, 18 Dec 2025 10:43:13 +0100
>>>>> On Wed, 17 Dec 2025 18:45:37 +0200, Eli Zaretskii <eliz <at> gnu.org> said:

    >> From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
    >> Cc: Eli Zaretskii <eliz <at> gnu.org>, 78448 <at> debbugs.gnu.org
    >> Date: Wed, 17 Dec 2025 10:48:28 -0500
    >> 
    >> Hi Robert, Eli--
    >> 
    >> Just a nudge here for unobtrusive signatures.  It looks to me like
    >> everything is in order:
    >> 
    >> - copyright assignment paperwork is completed
    >> - byte-compiler warnings are resolved
    >> - spurious Message warning is avoided
    >> - code merged down to a single patch
    >> - commit message is in (what i think is) changelog style
    >> 
    >> Do y'all need anything else from me to land this in emacs?

    Eli> I'd like Robert to review the latest version of the patch.

Itʼs first on my list of marked messages. The holiday season starts
soon, so Iʼll get to it then.

Robert
-- 




Information forwarded to bug-gnu-emacs <at> gnu.org, bugs <at> gnus.org:
bug#78448; Package emacs,gnus. (Wed, 31 Dec 2025 17:36:02 GMT) Full text and rfc822 format available.

Message #74 received at 78448 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Kahn Gillmor <dkg <at> fifthhorseman.net>
To: Robert Pluim <rpluim <at> gmail.com>, Eli Zaretskii <eliz <at> gnu.org>
Cc: 78448 <at> debbugs.gnu.org
Subject: Re: bug#78448: 30.1; mml: Produce Unobtrusive Signatures
Date: Wed, 31 Dec 2025 12:35:40 -0500
[Message part 1 (text/plain, inline)]
On Thu 2025-12-18 10:43:13 +0100, Robert Pluim wrote:
>>>>>> On Wed, 17 Dec 2025 18:45:37 +0200, Eli Zaretskii <eliz <at> gnu.org> said:
>     Eli> I'd like Robert to review the latest version of the patch.
>
> Itʼs first on my list of marked messages. The holiday season starts
> soon, so Iʼll get to it then.

Just a gentle nudge, hoping to move this along.  Robert, please let me
know if i can do anything to help with the review.

Wishing you all a happy new year, if you're into this whole Gregorian
calendar thing.

         --dkg

This bug report was last modified 8 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.