]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[crypto] Add cmsdetach script for detaching encrypted data from CMS messages
authorMichael Brown <mcb30@ipxe.org>
Wed, 28 Aug 2024 15:09:55 +0000 (16:09 +0100)
committerMichael Brown <mcb30@ipxe.org>
Wed, 28 Aug 2024 15:17:14 +0000 (16:17 +0100)
The openssl toolchain does not currently seem to support creating CMS
envelopedData or authEnvelopedData messages with detached encrypted
data.

Add a standalone tool "cmsdetach" that can be used to detach the
encrypted data from a CMS message.  For example:

  openssl cms -encrypt -binary -aes-256-gcm -recip client.crt \
              -in bootfile -outform DER -out bootfile.cms

  cmsdetach bootfile.cms --data bootfile.dat --envelope bootfile.env

Signed-off-by: Michael Brown <mcb30@ipxe.org>
contrib/crypto/cmsdetach [new file with mode: 0755]

diff --git a/contrib/crypto/cmsdetach b/contrib/crypto/cmsdetach
new file mode 100755 (executable)
index 0000000..0075009
--- /dev/null
@@ -0,0 +1,80 @@
+#!/usr/bin/env python3
+
+"""Detach CMS encrypted data.
+
+Detach encrypted data from a CMS envelopedData or authEnvelopedData
+message into a separate file.
+"""
+
+import argparse
+
+import asn1
+
+# Parse command-line arguments
+#
+parser = argparse.ArgumentParser(
+    description=__doc__,
+    formatter_class=argparse.RawDescriptionHelpFormatter,
+)
+parser.add_argument("-d", "--data", metavar="FILE",
+                    help="Write detached data (without envelope) to FILE")
+parser.add_argument("-e", "--envelope", metavar="FILE",
+                    help="Write envelope (without data) to FILE")
+parser.add_argument("-o", "--overwrite", action="store_true",
+                    help="Overwrite output files")
+parser.add_argument("file", help="Input envelope file")
+args = parser.parse_args()
+if args.data is None and args.envelope is None:
+    parser.error("at least one of --data and --envelope is required")
+outmode = "wb" if args.overwrite else "xb"
+
+# Create decoder
+#
+decoder = asn1.Decoder()
+with open(args.file, mode="rb") as fh:
+    decoder.start(fh.read())
+
+# Create encoder
+#
+encoder = asn1.Encoder()
+encoder.start()
+
+# Detach encrypted data
+#
+data = None
+datastack = [
+    asn1.Numbers.Sequence, 0, asn1.Numbers.Sequence, asn1.Numbers.Sequence
+]
+stack = []
+while stack or not decoder.eof():
+    if decoder.eof():
+        encoder.leave()
+        decoder.leave()
+        stack.pop()
+    else:
+        tag = decoder.peek()
+        if tag.typ == asn1.Types.Constructed:
+            encoder.enter(nr=tag.nr, cls=tag.cls)
+            decoder.enter()
+            stack.append(tag.nr)
+        else:
+            (tag, value) = decoder.read()
+            if stack == datastack and tag.nr == 0:
+                data = value
+            else:
+                encoder.write(value, nr=tag.nr, cls=tag.cls)
+envelope = encoder.output()
+if data is None:
+    parser.error("Input file does not contain any encrypted data")
+
+# Write envelope (without data), if applicable
+#
+if args.envelope:
+    with open(args.envelope, mode=outmode) as fh:
+        fh.write(envelope)
+
+# Write data (without envelope), if applicable
+#
+if args.data:
+    with open(args.data, mode=outmode) as fh:
+        fh.write(data)