From: Philipp Gesang Date: Tue, 9 May 2017 08:59:28 +0000 (+0200) Subject: unit test crypto file counter wraparound X-Git-Tag: v2.2~7^2~118 X-Git-Url: http://developer.intra2net.com/git/?a=commitdiff_plain;h=770173c52c29f9461bba56922cf354170f91afa8;p=python-delta-tar unit test crypto file counter wraparound After the file counter reaches UINT_MAX, it wraps around and a new fixed part must be created. The file counter is 32 bit unsigned integer so it needs to be lowered to make bounds testing feasible. --- diff --git a/deltatar/crypto.py b/deltatar/crypto.py index e33de7a..d01c20d 100755 --- a/deltatar/crypto.py +++ b/deltatar/crypto.py @@ -804,6 +804,20 @@ class Decrypt (Crypto): ############################################################################### +## testing helpers +############################################################################### + +def _testing_set_AES_GCM_IV_CNT_MAX (vow, n): + """ + Adapt upper file counter bound for testing IV logic. Completely unsafe. + """ + assert vow == "I am fully aware that this will void my warranty." + global AES_GCM_IV_CNT_MAX + r = AES_GCM_IV_CNT_MAX + AES_GCM_IV_CNT_MAX = n + return r + +############################################################################### ## freestanding invocation ############################################################################### diff --git a/testing/test_crypto.py b/testing/test_crypto.py index 30ea1d5..aad35f7 100644 --- a/testing/test_crypto.py +++ b/testing/test_crypto.py @@ -324,6 +324,71 @@ class AESGCMTest (CryptoLayerTest): for i in range (5): addobj (i) + def test_crypto_aes_gcm_enc_multiobj_cnt_wrap (self): + """ + Test behavior when the file counter tops out. + + Artificially lower the maximum possible file counter. Considering + invalid (0) and reserved (1, 2) values, the least possible file counter + for normal objects is 3. Starting from that, the header of the (max - + 3)rd object must have both a different IV fixed part and a counter. + """ + minimum = 3 + new_max = 8 + old_max = crypto._testing_set_AES_GCM_IV_CNT_MAX \ + ("I am fully aware that this will void my warranty.", + new_max) + cnksiz = 1 << 10 + password = str (os.urandom (42)) + encryptor = crypto.Encrypt (TEST_VERSION, + TEST_PARAMVERSION, + password=password, + nacl=TEST_STATIC_NACL, + strict_ivs=True) + + last_iv = None + last_cnt = minimum + + def addobj (i, wrap=False): + nonlocal last_iv + nonlocal last_cnt + pt = fill_mod (1 << 14, off=i) + header_dummy = encryptor.next ("%s_%d" % (TEST_DUMMY_FILENAME, i)) + + off = 0 + ct = b"" + while off < len (pt): + upto = min (off + cnksiz, len (pt)) + cnk = encryptor.process (pt [off:upto]) + ct += cnk + off += cnksiz + cnk, header, fixed = encryptor.done (header_dummy) + this_iv = crypto.hdr_read (header) ["iv"] + if last_iv is not None: + this_fixed, this_cnt = struct.unpack (crypto.FMT_I2N_IV, this_iv) + last_fixed, last_cnt = struct.unpack (crypto.FMT_I2N_IV, last_iv) + if wrap is False: + assert last_fixed == this_fixed + assert last_cnt == this_cnt - 1 + else: + assert last_fixed != this_fixed + assert this_cnt == minimum + last_iv = this_iv + ct += cnk + + assert len (pt) == len (ct) + + for i in range (minimum, new_max + 1): addobj (i) # counter range: [3, 8] + addobj (i + 1, True) # counter wraps to 3 + + for j in range (i + 2, i + new_max - 1): addobj (j) # counter range: [4, 8] + addobj (j + 1, True) # counter wraps to 3 again + + _ = crypto._testing_set_AES_GCM_IV_CNT_MAX \ + ("I am fully aware that this will void my warranty.", + old_max) + + def test_crypto_aes_gcm_dec_multicnk (self): cnksiz = 1 << 10 orig_pt = fill_mod (1 << 14)