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

Full log


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


This bug report was last modified 2 days ago.

Previous Next


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