iv = None # current IV
fixed = None # accu for 64 bit fixed parts of IV
used_ivs = None # tracks IVs
- strict_ivs = False # if True, panic on duplicate object IV
+ strict_ivs = False # if True, panic on duplicate or non-consecutive object IV
password = None
paramversion = None
insecure = False # allow plaintext parameters
return out
- def next (self, password, paramversion, nacl, iv):
+ def next (self, password, paramversion, nacl):
"""
Prepare for encrypting another object: Reset the data counters and
change the configuration in case one of the variable parameters differs
- from the last object. Also check the IV for duplicates and error out
- if strict checking was requested.
+ from the last object.
"""
self.ctsize = 0
self.ptsize = 0
self.stats ["obj"] += 1
- self.check_duplicate_iv (iv)
-
if ( self.paramversion != paramversion
or self.password != password
or self.nacl != nacl):
insecure=self.insecure)
- def check_duplicate_iv (self, iv):
- """
- Add an IV (the 12 byte representation as in the header) to the list. With
- strict checking enabled, this will throw a ``DuplicateIV``. Depending on
- the context, this may indicate a serious error (IV reuse).
- """
- if self.strict_ivs is True and iv in self.used_ivs:
- raise DuplicateIV ("iv %s was reused" % iv_fmt (iv))
- # vi has not been used before; add to collection
- self.used_ivs.add (iv)
-
-
def counters (self):
"""
Access the data counters.
return self.used_ivs
+ def reset_last_iv (self):
+ """
+ Implemented only for decryptor; no-op otherwise.
+ """
+ pass
+
+
class Encrypt (Crypto):
lastinfo = None
paramenc = None
def __init__ (self, version, paramversion, password=None, key=None, nacl=None,
- counter=AES_GCM_IV_CNT_DATA, strict_ivs=True, insecure=False):
+ counter=AES_GCM_IV_CNT_DATA, strict_ivs=False, insecure=False):
"""
The ctor will throw immediately if one of the parameters does not conform
to our expectations.
``AES_GCM_IV_CNT_INDEX`` are unique in each backup set
and cannot be reused even with different fixed parts.
:type strict_ivs: bool
- :type insecure: bool, whether to permit passthrough mode
+ :param strict_ivs: Enable paranoid tracking of IVs.
+ :type insecure: bool
+ :param insecure: whether to permit passthrough mode
*Security considerations*: The ``class Encrypt`` handle guarantees that
all random parts (first eight bytes) of the IVs used for encrypting
% self.paramversion)
hdrdum = hdr_make_dummy (filename)
self.lastinfo = (filename, hdrdum)
- super().next (self.password, self.paramversion, self.nacl, self.iv)
+
+ self.check_duplicate_iv (self.iv)
+
+ super().next (self.password, self.paramversion, self.nacl)
self.set_object_counter (self.cnt + 1)
return hdrdum
+ def check_duplicate_iv (self, iv):
+ """
+ Add an IV (the 12 byte representation as in the header) to the list. With
+ strict checking enabled, this will throw a ``DuplicateIV``. Depending on
+ the context, this may indicate a serious error (IV reuse).
+
+ IVs are only tracked in strict_ivs mode.
+ """
+ if self.strict_ivs is False:
+ return
+
+ if iv in self.used_ivs:
+ raise DuplicateIV ("iv %s was reused" % iv_fmt (iv))
+ # vi has not been used before; add to collection
+ self.used_ivs.add (iv)
+
+
def done (self, cmpdata):
"""
Complete encryption of an object. After this has been called, attempts
hdr_ctsize = -1
def __init__ (self, password=None, key=None, counter=None, fixedparts=None,
- strict_ivs=False, insecure=False):
+ strict_ivs=True, insecure=False):
"""
Sanitizing ctor for the decryption context. ``fixedparts`` specifies a
list of IV fixed parts accepted during decryption. If a fixed part is
``AES_GCM_IV_CNT_INDEX`` are unique in each backup set
and cannot be reused even with different fixed parts.
:type fixedparts: bytes list
+ :type strict_ivs: bool
+ :param strict_ivs: fail if IVs of decrypted objects are not linearly
+ increasing
:type insecure: bool
:param insecure: whether to process objects encrypted in
passthrough mode (*``paramversion`` < 1*)
+
+ *Security considerations*: The ``strict_ivs`` setting protects against
+ ciphertext reordering and injection attacks. For this to work it relies
+ on a property of how the object counters are created during encryption.
+ If multiple ``Encrypt`` handles have been used during encryption, this
+ is property is unlikely to apply as it would require manual management
+ of counters across Encrypt handles. In these cases it may thus be
+ necessary to disable the ```strict_ivs`` protection.
"""
if password is None and key is None \
or password is not None and key is not None :
return i != len (self.fixed) and self.fixed [i] == fixed
+ def reset_last_iv (self):
+ """
+ Force a new IV sequence start. The last IV counter will be set from the
+ next IV encountered and the check for consecutive IVs will be suppressed.
+
+ The intended use is backup volume boundaries or handling batches of
+ objects encrypted with ``Encrypt`` handles initialized with different
+ initial counter values.
+ """
+ self.last_iv = None
+
def check_consecutive_iv (self, iv):
"""
Check whether the counter part of the given IV is indeed the successor
if self.strict_ivs is True \
and self.last_iv is not None \
and self.last_iv [0] == fixed \
- and self.last_iv [1] != cnt - 1:
+ and self.last_iv [1] + 1 != cnt:
raise NonConsecutiveIV ("iv %s counter not successor of "
"last object (expected %d, found %d)"
- % (iv_fmt (iv), self.last_iv [1], cnt))
+ % (iv_fmt (iv), self.last_iv [1] + 1, cnt))
self.last_iv = (fixed, cnt)
self.hdr_ctsize = ctsize
- super().next (self.password, paramversion, nacl, iv)
+ super().next (self.password, paramversion, nacl)
if self.fixed is not None and self.valid_fixed_part (iv) is False:
raise InvalidIVFixedPart ("iv %s has invalid fixed part"
% iv_fmt (iv))
+
self.check_consecutive_iv (iv)
self.tag = tag