Add simple decryption capability for email-print-mime-structure, so
that it can do stuff like this:

$ email-print-mime-structure --pgpkey al...@openpgp.example.sec.asc < msg.eml
└┬╴multipart/encrypted 2190 bytes
 ├─╴application/pgp-encrypted 11 bytes
 └─╴application/octet-stream 1613 bytes
  ↧ (decrypts to)
  └─╴text/plain 425 bytes
$

At the moment, it only works with keys that can be found in the
filesystem, and when the pgpy module is installed.

Possible future work:

  - try using gpg to do the decryption from whatever gpg's system
    capabilities are

I've added python3-pgpy to the list of Recommends, since it is not a
hard dependency.

Signed-off-by: Daniel Kahn Gillmor <d...@fifthhorseman.net>
---
 debian/control                   |  1 +
 email-print-mime-structure       | 34 ++++++++++++++++++++++++++++++++
 email-print-mime-structure.1.pod |  8 ++++++++
 3 files changed, 43 insertions(+)

diff --git a/debian/control b/debian/control
index 6d3a54f..fc2bccc 100644
--- a/debian/control
+++ b/debian/control
@@ -39,6 +39,7 @@ Recommends:
  devscripts,
  git,
  notmuch,
+ python3-pgpy,
 Architecture: all
 Description: collection of scripts for manipulating e-mail on Debian
  This package provides a collection of scripts for manipulating e-mail
diff --git a/email-print-mime-structure b/email-print-mime-structure
index 33579a7..eb513b3 100755
--- a/email-print-mime-structure
+++ b/email-print-mime-structure
@@ -38,6 +38,11 @@ from typing import Optional, Union, List, Tuple, Any
 from email.charset import Charset
 from email.message import Message
 
+try:
+    import pgpy #type: ignore
+except ImportError:
+    pgpy = None
+
 class MimePrinter(object):
     def __init__(self, args:Namespace):
         self.args = args
@@ -66,6 +71,33 @@ class MimePrinter(object):
 
         print(f'{prefix}{z.get_content_type()}{cset}{disposition}{fname} 
{nbytes:d} bytes')
 
+        if self.args.pgpkey and \
+           (parent is not None) and \
+           (parent.get_content_type().lower() == 'multipart/encrypted') and \
+           (str(parent.get_param('protocol')).lower() == 
'application/pgp-encrypted') and \
+           (num == 2):
+            if pgpy is None:
+                logging.warning(f'Python module pgpy is not available, not 
decrypting (try "apt install python3-pgpy")')
+            else:
+                cryptopayload:Optional[Message] = None
+                keyname:str
+                for keyname in self.args.pgpkey:
+                    try:
+                        key:pgpy.PGPKey
+                        key, _ = pgpy.PGPKey.from_file(keyname)
+                        msg:pgpy.PGPMessage = 
pgpy.PGPMessage.from_blob(z.get_payload())
+                        msg = key.decrypt(msg)
+                        cryptopayload = email.message_from_bytes(msg.message)
+                        break
+                    except:
+                        pass
+                if cryptopayload is None:
+                    logging.warning(f'Unable to decrypt')
+                else:
+                    newprefix = prefix[:-3] + ' '
+                    print(f'{newprefix}↧ (decrypts to)')
+                    self.print_tree(cryptopayload, newprefix + '└', z, 0)
+
     def print_tree(self, z:Message, prefix:str, parent:Optional[Message], 
num:int) -> None:
         if (z.is_multipart()):
             self.print_part(z, prefix+'┬╴', parent, num)
@@ -88,6 +120,8 @@ class MimePrinter(object):
 def main() -> None:
     parser:ArgumentParser = ArgumentParser(description='Read RFC2822 MIME 
message from stdin and emit a tree diagram to stdout.',
                                            epilog="Example: 
email-print-mime-structure < message.eml")
+    parser.add_argument('--pgpkey', metavar='KEYFILE', action='append',
+                        help='OpenPGP Transferable Secret Key for decrypting')
     args:Namespace = parser.parse_args()
     msg:Union[Message, str, int, Any] = email.message_from_file(sys.stdin)
 
diff --git a/email-print-mime-structure.1.pod b/email-print-mime-structure.1.pod
index 03a8e29..209c725 100644
--- a/email-print-mime-structure.1.pod
+++ b/email-print-mime-structure.1.pod
@@ -21,6 +21,14 @@ something like "cat -n".
 
 =over 4
 
+=item B<--pgpkey=>I<KEYFILE>
+
+I<KEYFILE> should name an OpenPGP transferable secret key that is not
+password-protected.  If a PGP/MIME-encrypted message is found on
+standard input, this key will be tried for decryption.  May be used
+multiple times if you want to try decrypting with more than one secret
+key.
+
 =item B<--help>, B<-h>
 
 Show usage instructions.
-- 
2.24.0.rc1

Reply via email to