move tag back into the header
authorPhilipp Gesang <philipp.gesang@intra2net.com>
Fri, 17 Mar 2017 15:02:31 +0000 (16:02 +0100)
committerPhilipp Gesang <philipp.gesang@intra2net.com>
Fri, 17 Mar 2017 15:02:44 +0000 (16:02 +0100)
Since we seek back to write the final header it makes little
sense to append the tag to the ciphertext regardless.

deltatar/crypto.py

index f7ca6e3..a8807b6 100755 (executable)
@@ -53,7 +53,7 @@ from cryptography.hazmat.backends import default_backend
 
 
 __all__ = [ "hdr_make", "hdr_read", "hdr_fmt", "hdr_fmt_pretty"
-          , "I2N_HDR_SIZE", "I2N_TLR_SIZE_TAG" ]
+          , "I2N_HDR_SIZE" ]
 
 
 ###############################################################################
@@ -82,11 +82,12 @@ I2N_HDR_SIZE_PARAMVERSION   = 2     # 12
 I2N_HDR_SIZE_NACL           = 16    # 28
 I2N_HDR_SIZE_IV             = 12    # 40
 I2N_HDR_SIZE_CTSIZE         = 8     # 48
-I2N_TLR_SIZE_TAG            = 16 # GCM auth tag, appended to data
+I2N_HDR_SIZE_TAG            = 16    # 64 GCM auth tag
 
 I2N_HDR_SIZE = I2N_HDR_SIZE_MAGIC        + I2N_HDR_SIZE_VERSION \
              + I2N_HDR_SIZE_PARAMVERSION + I2N_HDR_SIZE_NACL    \
-             + I2N_HDR_SIZE_IV           + I2N_HDR_SIZE_CTSIZE # = 48
+             + I2N_HDR_SIZE_IV           + I2N_HDR_SIZE_CTSIZE  \
+             + I2N_HDR_SIZE_TAG # = 64
 
 # precalculate offsets since Python can’t do constant folding over names
 HDR_OFF_VERSION      = I2N_HDR_SIZE_MAGIC
@@ -94,7 +95,7 @@ HDR_OFF_PARAMVERSION = HDR_OFF_VERSION      + I2N_HDR_SIZE_VERSION
 HDR_OFF_NACL         = HDR_OFF_PARAMVERSION + I2N_HDR_SIZE_PARAMVERSION
 HDR_OFF_IV           = HDR_OFF_NACL         + I2N_HDR_SIZE_NACL
 HDR_OFF_CTSIZE       = HDR_OFF_IV           + I2N_HDR_SIZE_IV
-HDR_CTSIZE_DUMMY     = 0xffffFFFFffffFFFF
+HDR_OFF_TAG          = HDR_OFF_CTSIZE       + I2N_HDR_SIZE_CTSIZE
 
 FMT_UINT16_LE = "<H"
 FMT_UINT64_LE = "<Q"
@@ -104,7 +105,8 @@ FMT_I2N_HDR   = ("<"     # host byte order
                  "H"     # paramversion
                  "16s"   # sodium chloride
                  "12s"   # iv
-                 "Q")    # size
+                 "Q"     # size
+                 "16s")  # GCM tag
 
 # aes+gcm
 AES_GCM_IV_LEN   = 12
@@ -142,7 +144,7 @@ SCRYPT_NaCl_LEN  = 16
 def hdr_read (data):
 
     try:
-        mag, version, paramversion, nacl, iv, ctsize = \
+        mag, version, paramversion, nacl, iv, ctsize, tag = \
             struct.unpack (FMT_I2N_HDR, data)
     except Exception as exn:
         return False, "error reading header from [%r]: %s" % (data, str (exn))
@@ -157,6 +159,7 @@ def hdr_read (data):
         ,         "nacl" : nacl
         ,           "iv" : iv
         ,       "ctsize" : ctsize
+        ,          "tag" : tag
         }
 
 
@@ -168,16 +171,14 @@ def hdr_read_stream (instr):
     return True, hdr_read (data)
 
 
-def hdr_from_params (version, paramversion, nacl, iv, ctsize=None):
-    if ctsize is None:
-        ctsize = HDR_CTSIZE_DUMMY # dummy, overwritten later
+def hdr_from_params (version, paramversion, nacl, iv, ctsize, tag):
     buf  = bytearray (I2N_HDR_SIZE)
     bufv = memoryview (buf)
 
     try:
         struct.pack_into (FMT_I2N_HDR, bufv, 0,
                           I2N_HDR_MAGIC,
-                          version, paramversion, nacl, iv, ctsize)
+                          version, paramversion, nacl, iv, ctsize, tag)
     except Exception as exn:
         return False, "error writing header: %s" % str (exn)
 
@@ -198,7 +199,7 @@ def hdr_make (hdr):
     return hdr_from_params (version=hdr.get("version"),
                             paramversion=hdr.get("paramversion"),
                             nacl=hdr.get("nacl"), iv=hdr.get("iv"),
-                            ctsize=hdr.get("ctsize"))
+                            ctsize=hdr.get("ctsize"), tag=hdr.get("tag"))
 
 
 HDR_FMT = "I2n_header { version: %d, paramversion: %d, nacl: %s[%d]," \
@@ -306,10 +307,7 @@ class Crypto (object):
         key_parms = (password, nacl, N, r, p, dkLen)
         global KEY_MEMO
         if key_parms not in KEY_MEMO:
-            print(">> new key, memoize parms %s" % repr (key_parms))
             KEY_MEMO [key_parms] = pylibscrypt.scrypt (password, nacl, N, r, p, dkLen)
-        else:
-            print(">> use memoized key for parms %s" % repr (key_parms))
         self.key = KEY_MEMO [key_parms]
 
         if pfx is not None:
@@ -349,13 +347,13 @@ class Encrypt (Crypto):
 
 
     def next (self, filename, version, paramversion, nacl):
-        iv = self.iv_make()
+        self.iv = self.iv_make()
         self.curobj = (filename, version, paramversion, nacl)
         self.cnt += 1
         aad = "%s" % filename
         self.aes = Cipher \
                         ( algorithms.AES (self.key)
-                        , modes.GCM (iv)
+                        , modes.GCM (self.iv)
                         , backend = default_backend ()) \
                         .encryptor ()
         self.aes.authenticate_additional_data (str.encode (aad))
@@ -365,14 +363,15 @@ class Encrypt (Crypto):
 
     def done (self, cmpdata, ctsize):
         if cmpdata != self.hdrdum:
-            raise "XXX bad sync for writing header" ## we need to converge on a sensible error handling strategy
-        data, tag = self.aes.finalize () ## XXX we could also put the tag in the header
+            raise Exception ("XXX bad sync for writing header") ## we need to converge on a sensible error handling strategy
+        data = self.aes.finalize ()
         ctsize += len (data)
         (filename, version, paramversion, nacl) = self.curobj
-        ok, hdr = hdr_from_params (version, paramversion, nacl, iv, ctsize)
+        ok, hdr = hdr_from_params (version, paramversion, nacl, self.iv,
+                                   ctsize, self.aes.tag)
         if ok is False:
-            raise "XXX error constructing header" ## we need to converge on a sensible error handling strategy
-        return data, tag, hdr
+            raise Exception ("XXX error constructing header: %r" % hdr) ## we need to converge on a sensible error handling strategy
+        return data, hdr
 
 
 class Decrypt (Crypto):
@@ -385,7 +384,6 @@ class Decrypt (Crypto):
 
     def next (self, hdr):
         self.cnt += 1
-        print("I2N: got header “%s”" % crypto.hdr_fmt (hdr))
         iv = hdr ["iv"]
         self.aes = Cipher \
                         ( algorithms.AES (key)