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

Previous Next

Packages: emacs, gnus;

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


This bug report was last modified today.

Previous Next


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