implement passthrough mode in crypto.py
authorPhilipp Gesang <philipp.gesang@intra2net.com>
Mon, 24 Apr 2017 09:37:23 +0000 (11:37 +0200)
committerThomas Jarosch <thomas.jarosch@intra2net.com>
Mon, 2 Apr 2018 11:34:08 +0000 (13:34 +0200)
When invoked with --no-decrypt, write object headers and
ciphertext to output. Combined with --split this allows
extracting encrypted objects from the archive.

deltatar/crypto.py

index 5b1c004..3339878 100755 (executable)
@@ -774,6 +774,32 @@ def noise (*a, **b):
     print (file=sys.stderr, *a, **b)
 
 
+class PassthroughDecryptor (object):
+
+    curhdr = None # write current header on first data write
+
+    def __init__ (self):
+        if PDTCRYPT_VERBOSE is True:
+            noise ("PDT: no encryption; data passthrough")
+
+    def next (self, hdr):
+        ok, curhdr = hdr_make (hdr)
+        if ok is False:
+            raise PDTDecryptionError ("bad header %r" % hdr)
+        self.curhdr = curhdr
+
+    def done (self):
+        if self.curhdr is not None:
+            return self.curhdr
+        return b""
+
+    def process (self, d):
+        if self.curhdr is not None:
+            d = self.curhdr + d
+            self.curhdr = None
+        return d
+
+
 def depdtcrypt (mode, pw, ins, outs):
     """
     Remove PDTCRYPT layer from obj encrypted with pw. Used on a Deltatar
@@ -781,13 +807,17 @@ def depdtcrypt (mode, pw, ins, outs):
     """
     ctleft     = -1              # length of ciphertext to consume
     ctcurrent  = 0               # total ciphertext of current object
-    decr       = Decrypt (pw, strict_ivs=PDTCRYPT_STRICTIVS)  # decryptor
     total_obj  = 0               # total number of objects read
     total_pt   = 0               # total plaintext bytes
     total_ct   = 0               # total ciphertext bytes
     total_read = 0               # total bytes read
     outfile    = None            # Python file object for output
 
+    if mode & PDTCRYPT_DECRYPT:  # decryptor
+        decr = Decrypt (pw, strict_ivs=PDTCRYPT_STRICTIVS)
+    else:
+        decr = PassthroughDecryptor ()
+
     def nextout (_):
         """Dummy for non-split mode: output file does not vary."""
         return outs
@@ -887,6 +917,7 @@ def depdtcrypt (mode, pw, ins, outs):
                 noise (reduce (lambda a, e: (a + "\n" if a else "") + "PDT:\t· " + e,
                                pretty.splitlines (), ""))
             ctcurrent = ctleft = hdr ["ctsize"]
+
             decr.next (hdr)
 
             total_obj += 1 # used in file counter with split mode
@@ -1058,7 +1089,7 @@ def parse_argv (argv):
             usage (err=True)
             raise Unreachable
     else:
-        outs = deptdcrypt_mk_stream (PDTCRYPT_SINK  , outsspec or "-")
+        outs = deptdcrypt_mk_stream (PDTCRYPT_SINK, outsspec or "-")
     return mode, pw, ins, outs
 
 
@@ -1075,6 +1106,14 @@ def main (argv):
         noise ("PDT: Did you specify the correct password?")
         noise ("")
         return 1
+    except PDTSplitError as exn:
+        noise ("PDT: Split operation failed:")
+        noise ("PDT:")
+        noise ("PDT:    “%s”" % exn)
+        noise ("PDT:")
+        noise ("PDT: Hint: target directory should to be empty.")
+        noise ("")
+        return 1
 
     if PDTCRYPT_VERBOSE is True:
         noise ("PDT: decryption successful"                 )