If the user supplies a secret key like the ones found in
https://www.ietf.org/id/draft-dkg-lamps-samples-01.html, then
email-print-mime-structure will try to use that for decryption of
CMS-encrypted (S/MIME) message parts.

Signed-off-by: Daniel Kahn Gillmor <d...@fifthhorseman.net>
---
 debian/control                      |  2 ++
 email-print-mime-structure          | 12 ++++++++++--
 email-print-mime-structure.1.pod    | 17 ++++++++++++++---
 tests/email-print-mime-structure.sh |  6 ++++++
 4 files changed, 32 insertions(+), 5 deletions(-)

diff --git a/debian/control b/debian/control
index f04ce79..d2e07da 100644
--- a/debian/control
+++ b/debian/control
@@ -12,6 +12,7 @@ Build-Depends:
  gpg-agent <!nocheck>,
  gpgsm <!nocheck>,
  mypy <!nocheck>,
+ openssl <!nocheck>,
  perl,
  python3 <!nocheck>,
  python3-argcomplete,
@@ -54,6 +55,7 @@ Suggests:
  gpg,
  gpg-agent,
  gpgsm,
+ openssl,
 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 e82d56e..4de0789 100755
--- a/email-print-mime-structure
+++ b/email-print-mime-structure
@@ -83,7 +83,7 @@ class MimePrinter(object):
         print(f'{prefix}{z.get_content_type()}{cset}{disposition}{fname} 
{nbytes:d} bytes')
         cryptopayload:Optional[Message] = None
         try_pgp_decrypt:bool = self.args.pgpkey or self.args.use_gpg_agent
-        try_cms_decrypt:bool = self.args.use_gpg_agent
+        try_cms_decrypt:bool = self.args.cmskey or self.args.use_gpg_agent
 
         if try_pgp_decrypt and \
            (parent is not None) and \
@@ -116,6 +116,12 @@ class MimePrinter(object):
             if cryptopayload is None and self.args.use_gpg_agent:
                 cryptopayload = self.pipe_decrypt(ciphertext, ['gpg', 
'--batch', '--decrypt'])
         elif flavor == EncType.SMIME:
+            if self.args.cmskey:
+                for keyname in self.args.cmskey:
+                    cmd = ['openssl', 'smime', '-decrypt', '-inform', 'DER', 
'-inkey', keyname]
+                    cryptopayload = self.pipe_decrypt(ciphertext, cmd)
+                    if cryptopayload:
+                        return cryptopayload
             if self.args.use_gpg_agent:
                 cryptopayload = self.pipe_decrypt(ciphertext, ['gpgsm', 
'--batch', '--decrypt'])
         if cryptopayload is None:
@@ -175,7 +181,9 @@ 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')
+                        help='OpenPGP Transferable Secret Key for decrypting 
PGP/MIME')
+    parser.add_argument('--cmskey', metavar='KEYFILE', action='append',
+                        help='X.509 Private Key for decrypting S/MIME')
     parser.add_argument('--use-gpg-agent', action='store_true',
                         help='Ask local GnuPG installation for decryption')
     parser.add_argument('--no-use-gpg-agent', action='store_false',
diff --git a/email-print-mime-structure.1.pod b/email-print-mime-structure.1.pod
index f109997..037c1a9 100644
--- a/email-print-mime-structure.1.pod
+++ b/email-print-mime-structure.1.pod
@@ -32,15 +32,26 @@ key.
 OpenPGP secret keys listed in B<--pgpkey=> are used ephemerally, and
 do not interact with any local GnuPG keyring.
 
+=item B<--cmskey=>I<KEYFILE>
+
+I<KEYFILE> should name a PEM- or DER-encoded X.509 private key that is
+not password-protected.  If an S/MIME-encrypted message that uses CMS
+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 such key.
+
+X.509 private keys listed in B<--cmskey=> are used ephemerally, and do
+not interact with any local GnuPG keyring.
+
 =item B<--use-gpg-agent>
 
 If this flag is present, and B<email-print-mime-structure> encounters
 a PGP/MIME- or S/MIME-encrypted part, it will try to decrypt the part
 using the secret keys found in the local installation of GnuPG.
 
-If both B<--pgpkey=>I<KEYFILE> and B<--use-gpg-agent> are
-supplied, I<KEYFILE> arguments will be tried before falling back to
-GnuPG.
+If B<--use-gpg-agent> is supplied along with either
+B<--pgpkey=>I<KEYFILE> or B<--cmskey=>I<KEYFILE> arguments, the
+I<KEYFILE> arguments will be tried before falling back to GnuPG.
 
 If B<email-print-mime-structure> has been asked to decrypt parts with
 either B<--pgpkey=>I<KEYFILE> or with B<--use-gpg-agent>, and it
diff --git a/tests/email-print-mime-structure.sh 
b/tests/email-print-mime-structure.sh
index 5cd34b2..8c646d0 100755
--- a/tests/email-print-mime-structure.sh
+++ b/tests/email-print-mime-structure.sh
@@ -22,6 +22,12 @@ for eml in tests/email-print-mime-structure/*.eml; do
         GNUPGHOME="$testgpghome" test_eml "$base" --use-gpg-agent
         rm -rf "$testgpghome"
     elif [ -e "$p12key" ]; then
+        printf "Testing %s (OpenSSL)\n" "${eml##*/}"
+        grep -v ^- < "$p12key" | base64 -d | \
+            openssl pkcs12 -nocerts -nodes -passin pass: -passout pass: -out 
"$base.pemkey"
+        test_eml "$base" --cmskey "$base.pemkey"
+        rm -f "$base.pemkey"
+
         testgpghome=$(mktemp -d) 
         printf "Testing %s (GnuPG S/MIME)\n" "${eml##*/}"
         gpgsm --pinentry-mode=loopback --passphrase-fd 4 4<<<'' 
--homedir="$testgpghome" --batch --quiet --import <"$p12key"
-- 
2.24.0

Reply via email to