add crypto.py option to output cnf-compatible scrypt object
authorPhilipp Gesang <philipp.gesang@intra2net.com>
Fri, 23 Jun 2017 08:35:08 +0000 (10:35 +0200)
committerThomas Jarosch <thomas.jarosch@intra2net.com>
Mon, 2 Apr 2018 11:34:09 +0000 (13:34 +0200)
deltatar/crypto.py

index 5841830..18b72f5 100755 (executable)
@@ -126,6 +126,7 @@ first object doesn’t necessarily apply to any of the subsequent objects.
 
 """
 
+import base64
 import binascii
 import bisect
 import ctypes
@@ -1233,6 +1234,14 @@ SELF               = None
 PDTCRYPT_DEFAULT_VER  = 1
 PDTCRYPT_DEFAULT_PVER = 1
 
+# scrypt hashing output control
+PDTCRYPT_SCRYPT_INTRANATOR = 0
+PDTCRYPT_SCRYPT_PARAMETERS = 1
+
+PDTCRYPT_SCRYPT_FORMAT = \
+    { "i2n"    : PDTCRYPT_SCRYPT_INTRANATOR
+    , "params" : PDTCRYPT_SCRYPT_PARAMETERS }
+
 
 class PDTDecryptionError (Exception):
     """Decryption failed."""
@@ -1492,14 +1501,15 @@ def mode_depdtcrypt (mode, secret, ins, outs):
     return 0
 
 
-def mode_scrypt (pw, ins=None, nacl=None):
+def mode_scrypt (pw, ins=None, nacl=None, fmt=PDTCRYPT_SCRYPT_INTRANATOR):
     hsh = None
+    paramversion = PDTCRYPT_DEFAULT_PVER
     if ins is not None:
         hsh, nacl, version, paramversion = scrypt_hashsource (pw, ins)
         defs = ENCRYPTION_PARAMETERS.get(paramversion, None)
     else:
         nacl    = binascii.unhexlify (nacl)
-        defs    = ENCRYPTION_PARAMETERS.get(PDTCRYPT_DEFAULT_PVER, None)
+        defs    = ENCRYPTION_PARAMETERS.get(paramversion, None)
         version = PDTCRYPT_DEFAULT_VER
 
     kdfname, params = defs ["kdf"]
@@ -1508,16 +1518,26 @@ def mode_scrypt (pw, ins=None, nacl=None):
         hsh, _void = kdf (pw, nacl)
 
     import json
-    out = json.dumps ({ "salt"          : binascii.hexlify (nacl).decode ()
-                      , "hash"          : binascii.hexlify (hsh).decode ()
-                      , "version"       : version
-                      , "scrypt_params" : { "N"     : params ["N"]
-                                          , "r"     : params ["r"]
-                                          , "p"     : params ["p"]
-                                          , "dkLen" : params ["dkLen"] } })
+
+    if fmt == PDTCRYPT_SCRYPT_INTRANATOR:
+        out = json.dumps ({ "salt"          : base64.b64encode (nacl).decode ()
+                          , "key"           : base64.b64encode (hsh) .decode ()
+                          , "paramversion"  : paramversion })
+    elif fmt == PDTCRYPT_SCRYPT_PARAMETERS:
+        out = json.dumps ({ "salt"          : binascii.hexlify (nacl).decode ()
+                          , "key"           : binascii.hexlify (hsh) .decode ()
+                          , "version"       : version
+                          , "scrypt_params" : { "N"     : params ["N"]
+                                              , "r"     : params ["r"]
+                                              , "p"     : params ["p"]
+                                              , "dkLen" : params ["dkLen"] } })
+    else:
+        raise RuntimeError ("bad scrypt output scheme %r" % fmt)
+
     print (out)
 
 
+
 def usage (err=False):
     out = print
     if err is True:
@@ -1529,6 +1549,7 @@ def usage (err=False):
     out ("       %s              [ { -n | --nacl } { SALT } ]" % indent)
     out ("       %s              [ { -o | --out } { - | DESTINATION } ]" % indent)
     out ("       %s              [ -D | --no-decrypt ] [ -S | --split ]" % indent)
+    out ("       %s              [ -f | --format ]" % indent)
     out ("")
     out ("\twhere")
     out ("\t\tSUBCOMMAND      main mode: { process | scrypt }")
@@ -1545,6 +1566,7 @@ def usage (err=False):
     out ("\t\t-S              split into files at object boundaries; this")
     out ("\t\t                requires DESTINATION to refer to directory")
     out ("\t\t-D              PDT header and ciphertext passthrough")
+    out ("\t\t-f              format of SCRYPT hash output (“default” or “parameters”)")
     out ("")
     out ("\tinstead of filenames, “-” may used to specify stdin / stdout")
     out ("")
@@ -1560,11 +1582,12 @@ def bail (msg):
 
 def parse_argv (argv):
     global SELF
-    mode      = PDTCRYPT_DECRYPT
-    secret    = None
-    insspec   = None
-    outsspec  = None
-    nacl      = None
+    mode          = PDTCRYPT_DECRYPT
+    secret        = None
+    insspec       = None
+    outsspec      = None
+    nacl          = None
+    scrypt_format = None
 
     argvi = iter (argv)
     SELF  = os.path.basename (next (argvi))
@@ -1633,6 +1656,14 @@ def parse_argv (argv):
                 if arg in [ "-n", "--nacl", "--salt" ]:
                     nacl = checked_arg ()
                     if PDTCRYPT_VERBOSE is True: noise ("PDT: salt key with %s" % nacl)
+                elif arg in [ "-f", "--format" ]:
+                    arg = checked_arg ()
+                    try:
+                        scrypt_format = PDTCRYPT_SCRYPT_FORMAT [arg]
+                    except KeyError:
+                        bail ("ERROR: invalid scrypt output format %s" % arg)
+                    if PDTCRYPT_VERBOSE is True:
+                        noise ("PDT: scrypt output format “%s”" % scrypt_format)
                 else:
                     bail ("ERROR: unexpected positional argument “%s”" % arg)
 
@@ -1670,7 +1701,8 @@ def parse_argv (argv):
         ins = deptdcrypt_mk_stream (PDTCRYPT_SOURCE, insspec or "-")
 
     if subcommand == PDTCRYPT_SUB_SCRYPT:
-        return True, partial (mode_scrypt, secret [1].encode (), ins, nacl)
+        return True, partial (mode_scrypt, secret [1].encode (), ins, nacl,
+                              fmt=scrypt_format)
 
     if mode & PDTCRYPT_SPLIT: # destination must be directory
         if outsspec is None or outsspec == "-":