export scrypt hashing functionality
authorPhilipp Gesang <philipp.gesang@intra2net.com>
Thu, 11 May 2017 08:50:30 +0000 (10:50 +0200)
committerThomas Jarosch <thomas.jarosch@intra2net.com>
Mon, 2 Apr 2018 11:34:08 +0000 (13:34 +0200)
deltatar/crypto.py

index e4a121a..f2dd19c 100755 (executable)
@@ -90,6 +90,7 @@ import cryptography
 
 
 __all__ = [ "hdr_make", "hdr_read", "hdr_fmt", "hdr_fmt_pretty"
+          , "scrypt_hashfile"
           , "PDTCRYPT_HDR_SIZE", "AES_GCM_IV_CNT_DATA"
           , "AES_GCM_IV_CNT_INFOFILE", "AES_GCM_IV_CNT_INDEX"
           ]
@@ -440,6 +441,57 @@ def kdf_by_version (paramversion=None, defs=None):
     return partial (fn, params)
 
 
+###############################################################################
+## SCRYPT hashing
+###############################################################################
+
+def scrypt_hashsource (pw, ins):
+    """
+    Calculate the SCRYPT hash from the password and the information contained
+    in the first header found in ``ins``.
+
+    This does not validate whether the first object is encrypted correctly.
+    """
+    hdr = None
+    try:
+        hdr = hdr_read_stream (ins)
+    except EndOfFile as exn:
+        noise ("PDT: malformed input: end of file reading first object header")
+        noise ("PDT:")
+        return 1
+
+    nacl = hdr ["nacl"]
+    pver = hdr ["paramversion"]
+    if PDTCRYPT_VERBOSE is True:
+        noise ("PDT: salt of first object          : %s" % binascii.hexlify (nacl))
+        noise ("PDT: parameter version of archive  : %d" % pver)
+
+    try:
+        defs = ENCRYPTION_PARAMETERS.get(pver, None)
+        kdfname, params = defs ["kdf"]
+        if kdfname != "scrypt":
+            noise ("PDT: input is not an SCRYPT archive")
+            noise ("")
+            return 1
+        kdf = kdf_by_version (None, defs)
+    except ValueError as exn:
+        noise ("PDT: object has unknown parameter version %d" % pver)
+
+    hsh, _void = kdf (pw, nacl)
+
+    return hsh, nacl, params
+
+
+def scrypt_hashfile (pw, fname):
+    with deptdcrypt_mk_stream (PDTCRYPT_SOURCE, fname  or "-") as ins:
+        hsh, _void, _void = scrypt_hashsource (pw, ins)
+        return hsh
+
+
+###############################################################################
+## AES-GCM context
+###############################################################################
+
 class Crypto (object):
     """
     Encryption context to remain alive throughout an entire tarfile pass.
@@ -1129,34 +1181,7 @@ def mode_depdtcrypt (mode, pw, ins, outs):
 
 
 def mode_scrypt (pw, ins):
-    hdr = None
-    try:
-        hdr = hdr_read_stream (ins)
-    except EndOfFile as exn:
-        noise ("PDT: malformed input: end of file reading first object header")
-        noise ("PDT:")
-        return 1
-    finally:
-        ins.close ()
-
-    nacl = hdr ["nacl"]
-    pver = hdr ["paramversion"]
-    if PDTCRYPT_VERBOSE is True:
-        noise ("PDT: salt of first object          : %s" % binascii.hexlify (nacl))
-        noise ("PDT: parameter version of archive  : %d" % pver)
-
-    try:
-        defs = ENCRYPTION_PARAMETERS.get(pver, None)
-        kdfname, params = defs ["kdf"]
-        if kdfname != "scrypt":
-            noise ("PDT: input is not an SCRYPT archive")
-            noise ("")
-            return 1
-        kdf = kdf_by_version (None, defs)
-    except ValueError as exn:
-        noise ("PDT: object has unknown parameter version %d" % pver)
-
-    hsh, _void = kdf (pw, nacl)
+    hsh, nacl, params = scrypt_hashsource (pw, ins)
 
     import json
     out = json.dumps ({ "salt"          : str (binascii.hexlify (nacl))
@@ -1266,9 +1291,7 @@ def parse_argv (argv):
             else:
                 raise Unreachable
 
-    if pw is not None:
-        pw = pw.encode ()
-    else:
+    if pw is None:
         if subcommand == PDTCRYPT_SUB_SCRYPT:
             noise ("ERROR: scrypt hash mode requested but no password given")
             noise ("")
@@ -1281,10 +1304,10 @@ def parse_argv (argv):
             raise Unreachable
 
     # default to stdout
-    ins  = deptdcrypt_mk_stream (PDTCRYPT_SOURCE, insspec  or "-")
+    ins = deptdcrypt_mk_stream (PDTCRYPT_SOURCE, insspec  or "-")
 
     if subcommand == PDTCRYPT_SUB_SCRYPT:
-        return True, partial (mode_scrypt, pw, ins)
+        return True, partial (mode_scrypt, pw.encode (), ins)
 
     if mode & PDTCRYPT_SPLIT: # destination must be directory
         if outsspec is None or outsspec == "-":