From e24d6a8de5dac892e4dc7eea2c6b4777c19cd5fe Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 6 Apr 2017 17:56:09 +0200 Subject: [PATCH] rework crypto.py unittests for revised encryption Main changes: - Adjust usage to revised encryption handler. - Adapt to header format. - Adjust to changes in error passing (above all ``hdr_read()``). - Remove Scrypt or tag tests, these interfaces are no longer available. --- testing/test_crypto.py | 368 ++++++++++++++++++++---------------------------- 1 files changed, 153 insertions(+), 215 deletions(-) diff --git a/testing/test_crypto.py b/testing/test_crypto.py index 8f54387..c0d960b 100644 --- a/testing/test_crypto.py +++ b/testing/test_crypto.py @@ -12,13 +12,16 @@ import cryptography def b(s): return s.encode("UTF-8") -TEST_PLAINTEXT = b("gentlemen don’t read each other’s mail") -TEST_PASSPHRASE = b"test1234" -TEST_AES_GCM_AAD = b"authenticated plain text" - -CRYPTO_NACL_SIZE = 12 +CRYPTO_NACL_SIZE = 16 CRYPTO_KEY_SIZE = 16 -CRYPTO_TAG_SIZE = 16 + +TEST_PLAINTEXT = b("gentlemen don’t read each other’s mail") +TEST_PASSPHRASE = b"test1234" +TEST_AES_GCM_AAD = b"authenticated plain text" +TEST_DUMMY_FILENAME = "insurance-file.txt" +TEST_VERSION = 1 +TEST_PARAMVERSION = 1 +TEST_STATIC_NACL = os.urandom (CRYPTO_NACL_SIZE) def faux_hdr (ctsize=1337, iv=None): return \ @@ -29,6 +32,8 @@ def faux_hdr (ctsize=1337, iv=None): , "iv" : iv or binascii.unhexlify(b"0011223344556677" b"8899aabb") , "ctsize" : ctsize + , "tag" : binascii.unhexlify(b"deadbeefbadb100d" + b"b1eedc0ffeedea15") } @@ -51,227 +56,182 @@ class CryptoLayerTest (unittest.TestCase): class AESGCMTest (CryptoLayerTest): - def test_crypto_aes_gcm_enc_simple (self): - NaCl = os.urandom (CRYPTO_NACL_SIZE) - key = os.urandom (CRYPTO_KEY_SIZE) - enc = crypto.AES_GCM_context (crypto.ENCRYPT, key, TEST_AES_GCM_AAD) - - - def test_crypto_aes_gcm_enc_tag_retrieve (self): - NaCl = os.urandom (CRYPTO_NACL_SIZE) - key = os.urandom (CRYPTO_KEY_SIZE) - enc = crypto.AES_GCM_context (crypto.ENCRYPT, key, TEST_AES_GCM_AAD) - ok, ct = enc.process_chunk (TEST_PLAINTEXT) - if ok is False or ct is None: - raise "error encrypting chunk [%s]" % TEST_PLAINTEXT - ok, ct, tag = enc.done () - if ok is False or ct is None: - raise "error finalizing encryption" - if not tag: - raise "no tag received upon completing the encryption" - - - def test_crypto_aes_gcm_enc_tag_size (self): - NaCl = os.urandom (CRYPTO_NACL_SIZE) - key = os.urandom (CRYPTO_KEY_SIZE) - enc = crypto.AES_GCM_context (crypto.ENCRYPT, key, TEST_AES_GCM_AAD) - ok, ct = enc.process_chunk (TEST_PLAINTEXT) - if ok is False or ct is None: - raise "error encrypting chunk [%s]" % TEST_PLAINTEXT - ok, ct, tag = enc.done () - if ok is False or ct is None: - raise "error finalizing encryption" - if not tag: - raise "no tag received upon completing the encryption" - assert len (tag) == CRYPTO_TAG_SIZE + def test_crypto_aes_gcm_enc_ctor (self): + password = str (os.urandom (42)) + encryptor = crypto.Encrypt (password, TEST_VERSION, + TEST_PARAMVERSION, nacl=TEST_STATIC_NACL) + + + def test_crypto_aes_gcm_enc_header_size (self): + password = str (os.urandom (42)) + encryptor = crypto.Encrypt (password, TEST_VERSION, + TEST_PARAMVERSION, + nacl=TEST_STATIC_NACL) + + header_dummy = encryptor.next (TEST_DUMMY_FILENAME) + assert len (header_dummy) == crypto.PDTCRYPT_HDR_SIZE + _ = encryptor.process (TEST_PLAINTEXT) + _, header, _ = encryptor.done (header_dummy) + assert len (header) == crypto.PDTCRYPT_HDR_SIZE def test_crypto_aes_gcm_enc_chunk_size (self): - NaCl = os.urandom (CRYPTO_NACL_SIZE) - key = os.urandom (CRYPTO_KEY_SIZE) - enc = crypto.AES_GCM_context (crypto.ENCRYPT, key, TEST_AES_GCM_AAD) - ok, ct = enc.process_chunk (TEST_PLAINTEXT) - if ok is False or ct is None: - raise "error encrypting chunk [%s]" % TEST_PLAINTEXT - assert len (ct) == len (TEST_PLAINTEXT) - ok, ct, tag = enc.done () - if ok is False or ct is None: - raise "error finalizing encryption" - if not tag: - raise "no tag received upon completing the encryption" - assert len (ct) == 0 + password = str (os.urandom (42)) + encryptor = crypto.Encrypt (password, TEST_VERSION, + TEST_PARAMVERSION, + nacl=TEST_STATIC_NACL) + + header_dummy = encryptor.next (TEST_DUMMY_FILENAME) + ciphertext = encryptor.process (TEST_PLAINTEXT) + assert len (ciphertext) == len (TEST_PLAINTEXT) + rest, header, fixed = encryptor.done (header_dummy) + assert len (rest) == 0 def test_crypto_aes_gcm_dec_simple (self): - NaCl = os.urandom (CRYPTO_NACL_SIZE) - key = os.urandom (CRYPTO_KEY_SIZE) - enc = crypto.AES_GCM_context (crypto.ENCRYPT, key, TEST_AES_GCM_AAD) - iv = enc.iv - dec = crypto.AES_GCM_context (crypto.DECRYPT, key, TEST_AES_GCM_AAD, iv = iv) - ok, ct = enc.process_chunk (TEST_PLAINTEXT) - ok, _, tag = enc.done () - ok, pt = dec.process_chunk (ct) - ok, _, _ = dec.done (tag) - assert pt == TEST_PLAINTEXT - - - def test_crypto_aes_gcm_dec_missing_tag (self): - NaCl = os.urandom (CRYPTO_NACL_SIZE) - key = os.urandom (CRYPTO_KEY_SIZE) - enc = crypto.AES_GCM_context (crypto.ENCRYPT, key, TEST_AES_GCM_AAD) - iv = enc.iv - dec = crypto.AES_GCM_context (crypto.DECRYPT, key, TEST_AES_GCM_AAD, iv = iv) - ok, ct = enc.process_chunk (TEST_PLAINTEXT) - ok, _, tag = enc.done () - ok, pt = dec.process_chunk (ct) - with pytest.raises (ValueError): - ok, _, _ = dec.done () + password = str (os.urandom (42)) + encryptor = crypto.Encrypt (password, TEST_VERSION, + TEST_PARAMVERSION, + nacl=TEST_STATIC_NACL) + + header_dummy = encryptor.next (TEST_DUMMY_FILENAME) + ciphertext = encryptor.process (TEST_PLAINTEXT) + rest, header, fixed = encryptor.done (header_dummy) + ciphertext += rest + + decryptor = crypto.Decrypt (password, fixedparts=fixed) + decryptor.next (header) + plaintext = decryptor.process (ciphertext) + ok, rest = decryptor.done () + plaintext += rest + + assert ok + assert plaintext == TEST_PLAINTEXT def test_crypto_aes_gcm_dec_bad_tag (self): - NaCl = os.urandom (CRYPTO_NACL_SIZE) - key = os.urandom (CRYPTO_KEY_SIZE) - enc = crypto.AES_GCM_context (crypto.ENCRYPT, key, TEST_AES_GCM_AAD) - iv = enc.iv - dec = crypto.AES_GCM_context (crypto.DECRYPT, key, TEST_AES_GCM_AAD, iv = iv) - ok, ct = enc.process_chunk (TEST_PLAINTEXT) - ok, _, tag = enc.done () - ok, pt = dec.process_chunk (ct) - with pytest.raises (cryptography.exceptions.InvalidTag): - tag = tag[1:] + b"X" - ok, _, _ = dec.done (tag) + password = str (os.urandom (42)) + encryptor = crypto.Encrypt (password, TEST_VERSION, + TEST_PARAMVERSION, + nacl=TEST_STATIC_NACL) + + header_dummy = encryptor.next (TEST_DUMMY_FILENAME) + ciphertext = encryptor.process (TEST_PLAINTEXT) + ciphertext2, header, fixed = encryptor.done (header_dummy) + + mut_header = bytearray (header) + mut_header_vw = memoryview (mut_header) + # replace one byte in the tag part of the header + second_byte = mut_header_vw [crypto.HDR_OFF_TAG + 2] + mut_header_vw [crypto.HDR_OFF_TAG + 2] = (second_byte + 0x2a) % 256 + header = bytes (mut_header) + + decryptor = crypto.Decrypt (password, fixedparts=fixed) + decryptor.next (header) + plaintext = decryptor.process (ciphertext) + ok, err = decryptor.done () + + assert ok is False + assert err == "InvalidTag()" def test_crypto_aes_gcm_enc_multicnk (self): cnksiz = 1 << 10 - orig_pt = fill_mod (1 << 14) - NaCl = os.urandom (CRYPTO_NACL_SIZE) - key = os.urandom (CRYPTO_KEY_SIZE) - enc = crypto.AES_GCM_context (crypto.ENCRYPT, key, TEST_AES_GCM_AAD) - iv = enc.iv - dec = crypto.AES_GCM_context (crypto.DECRYPT, key, TEST_AES_GCM_AAD, iv = iv) + pt = fill_mod (1 << 14) + password = str (os.urandom (42)) + encryptor = crypto.Encrypt (password, TEST_VERSION, + TEST_PARAMVERSION, + nacl=TEST_STATIC_NACL) + header_dummy = encryptor.next (TEST_DUMMY_FILENAME) off = 0 ct = b"" - while off < len (orig_pt): - upto = min (off + cnksiz, len (orig_pt)) - ok, cnk = enc.process_chunk (orig_pt [off:upto]) - assert ok + while off < len (pt): + upto = min (off + cnksiz, len (pt)) + cnk = encryptor.process (pt [off:upto]) ct += cnk off += cnksiz - ok, _, tag = enc.done () - assert ok - assert tag - assert len (ct) == len (orig_pt) + cnk, header, fixed = encryptor.done (header_dummy) + ct += cnk + + assert len (pt) == len (ct) def test_crypto_aes_gcm_dec_multicnk (self): - cnksiz = 1 << 10 - orig_pt = fill_mod (1 << 14) - NaCl = os.urandom (CRYPTO_NACL_SIZE) - key = os.urandom (CRYPTO_KEY_SIZE) - enc = crypto.AES_GCM_context (crypto.ENCRYPT, key, TEST_AES_GCM_AAD) - iv = enc.iv - dec = crypto.AES_GCM_context (crypto.DECRYPT, key, TEST_AES_GCM_AAD, iv = iv) + cnksiz = 1 << 10 + orig_pt = fill_mod (1 << 14) + password = str (os.urandom (42)) + encryptor = crypto.Encrypt (password, TEST_VERSION, + TEST_PARAMVERSION, + nacl=TEST_STATIC_NACL) + header_dummy = encryptor.next (TEST_DUMMY_FILENAME) off = 0 ct = b"" while off < len (orig_pt): upto = min (off + cnksiz, len (orig_pt)) - ok, cnk = enc.process_chunk (orig_pt [off:upto]) + cnk = encryptor.process (orig_pt [off:upto]) ct += cnk off += cnksiz - ok, _, tag = enc.done () + cnk, header, fixed = encryptor.done (header_dummy) + ct += cnk + decryptor = crypto.Decrypt (password, fixedparts=fixed) + decryptor.next (header) off = 0 - pt = b"" + pt = b"" while off < len (orig_pt): upto = min (off + cnksiz, len (orig_pt)) - ok, cnk = dec.process_chunk (ct [off:upto]) + cnk = decryptor.process (ct [off:upto]) pt += cnk off += cnksiz - ok, _, _ = dec.done (tag) + ok, cnk = decryptor.done () + assert ok + + pt += cnk assert pt == orig_pt def test_crypto_aes_gcm_dec_multicnk_bad_tag (self): - cnksiz = 1 << 10 - orig_pt = fill_mod (1 << 14) - NaCl = os.urandom (CRYPTO_NACL_SIZE) - key = os.urandom (CRYPTO_KEY_SIZE) - enc = crypto.AES_GCM_context (crypto.ENCRYPT, key, TEST_AES_GCM_AAD) - iv = enc.iv - dec = crypto.AES_GCM_context (crypto.DECRYPT, key, TEST_AES_GCM_AAD, iv = iv) + cnksiz = 1 << 10 + orig_pt = fill_mod (1 << 14) + password = str (os.urandom (42)) + encryptor = crypto.Encrypt (password, TEST_VERSION, + TEST_PARAMVERSION, + nacl=TEST_STATIC_NACL) + header_dummy = encryptor.next (TEST_DUMMY_FILENAME) off = 0 ct = b"" while off < len (orig_pt): upto = min (off + cnksiz, len (orig_pt)) - ok, cnk = enc.process_chunk (orig_pt [off:upto]) + cnk = encryptor.process (orig_pt [off:upto]) ct += cnk off += cnksiz - ok, _, tag = enc.done () - + cnk, header, fixed = encryptor.done (header_dummy) + ct += cnk + + mut_header = bytearray (header) + mut_header_vw = memoryview (mut_header) + # replace one byte in the tag part of the header + second_byte = mut_header_vw [crypto.HDR_OFF_TAG + 2] + mut_header_vw [crypto.HDR_OFF_TAG + 2] = (second_byte + 0x2a) % 256 + header = bytes (mut_header) + + decryptor = crypto.Decrypt (password, fixedparts=fixed) + decryptor.next (header) off = 0 - pt = b"" + pt = b"" while off < len (orig_pt): upto = min (off + cnksiz, len (orig_pt)) - ok, cnk = dec.process_chunk (ct [off:upto]) + cnk = decryptor.process (ct [off:upto]) pt += cnk off += cnksiz - with pytest.raises (cryptography.exceptions.InvalidTag): - tag = b"Y" + tag[:-1] - ok, _, _ = dec.done (tag) - - - def test_crypto_aes_gcm_dec_multicnk_missing_tag (self): - cnksiz = 1 << 10 - orig_pt = fill_mod (1 << 14) - NaCl = os.urandom (CRYPTO_NACL_SIZE) - key = os.urandom (CRYPTO_KEY_SIZE) - enc = crypto.AES_GCM_context (crypto.ENCRYPT, key, TEST_AES_GCM_AAD) - iv = enc.iv - dec = crypto.AES_GCM_context (crypto.DECRYPT, key, TEST_AES_GCM_AAD, iv = iv) - - off = 0 - ct = b"" - while off < len (orig_pt): - upto = min (off + cnksiz, len (orig_pt)) - ok, cnk = enc.process_chunk (orig_pt [off:upto]) - ct += cnk - off += cnksiz - ok, _, tag = enc.done () - - off = 0 - pt = b"" - while off < len (orig_pt): - upto = min (off + cnksiz, len (orig_pt)) - ok, cnk = dec.process_chunk (ct [off:upto]) - pt += cnk - off += cnksiz - - with pytest.raises (ValueError): - ok, _, _ = dec.done () - - -class ScryptTest (CryptoLayerTest): - - nacl_len = 16 - nacl = binascii.unhexlify(b"0011223344556677" - b"8899aabbccddeeff") - -# def test_scrypt_keygen (self): -# nacl, k = crypto.scrypt_derive (TEST_PASSPHRASE, self.nacl) -# assert len (k) == CRYPTO_KEY_SIZE -# assert nacl == self.nacl - - ## excessively slow, so disabled -# def test_scrypt_keygen_salt_random (self): -# _, salted_a = crypto.scrypt_derive (TEST_PASSPHRASE, None) -# _, salted_b = crypto.scrypt_derive (TEST_PASSPHRASE, None) -# assert salted_a != salted_b + ok, err = decryptor.done () + assert ok is False + assert err == "InvalidTag()" class HeaderTest (CryptoLayerTest): @@ -280,7 +240,7 @@ class HeaderTest (CryptoLayerTest): meta = faux_hdr() ok, hdr = crypto.hdr_make (meta) assert ok - assert len (hdr) == crypto.I2N_HDR_SIZE + assert len (hdr) == crypto.PDTCRYPT_HDR_SIZE def test_crypto_fmt_hdr_make_useless (self): @@ -292,9 +252,10 @@ class HeaderTest (CryptoLayerTest): def test_crypto_fmt_hdr_read (self): meta = faux_hdr() ok, hdr = crypto.hdr_make (meta) - assert ok - ok, mmeta = crypto.hdr_read (hdr) - assert ok + assert ok is True + assert hdr is not None + mmeta = crypto.hdr_read (hdr) + assert mmeta is not None for k in meta: if meta [k] != mmeta [k]: raise "header mismatch after reading: expected %r, got %r" \ @@ -304,54 +265,31 @@ class HeaderTest (CryptoLayerTest): def test_crypto_fmt_hdr_read_trailing_garbage (self): meta = faux_hdr() ok, hdr = crypto.hdr_make (meta) + ok, hdr = crypto.hdr_make (meta) + assert ok is True + assert hdr is not None hdr += b"-junk" - ok, msg = crypto.hdr_read (hdr) - assert ok is False - assert msg.startswith ("error reading header from") + with pytest.raises (crypto.InvalidHeader): + _ = crypto.hdr_read (hdr) def test_crypto_fmt_hdr_read_leading_garbage (self): meta = faux_hdr() ok, hdr = crypto.hdr_make (meta) + ok, hdr = crypto.hdr_make (meta) + assert ok is True + assert hdr is not None hdr = b"junk-" + hdr - ok, msg = crypto.hdr_read (hdr) - assert ok is False - assert msg.startswith ("error reading header from") + with pytest.raises (crypto.InvalidHeader): + _ = crypto.hdr_read (hdr) def test_crypto_fmt_hdr_inner_garbage (self): meta = faux_hdr() ok, hdr = crypto.hdr_make (meta) - hdr = hdr[:len(hdr)//2] + b"junk-" + hdr[len(hdr)//2:] - ok, msg = crypto.hdr_read (hdr) - assert ok is False - assert msg.startswith ("error reading header from") - -class TagTest (CryptoLayerTest): - - def test_crypto_tag_fmt (self): - key = os.urandom (CRYPTO_KEY_SIZE) - enc = crypto.AES_GCM_context (crypto.ENCRYPT, key, TEST_AES_GCM_AAD) - ok, _ = enc.process_chunk (TEST_PLAINTEXT) - assert ok - ok, _, tag = enc.done () - assert ok - assert tag - tagged = crypto.tag_fmt (tag) - assert len (tagged) == CRYPTO_TAG_SIZE - - - def test_crypto_tag_read (self): - key = os.urandom (CRYPTO_KEY_SIZE) - enc = crypto.AES_GCM_context (crypto.ENCRYPT, key, TEST_AES_GCM_AAD) - ok, _ = enc.process_chunk (TEST_PLAINTEXT) - assert ok - ok, _, tag = enc.done () - assert ok - assert tag - tagged = crypto.tag_fmt (tag) - (ok, ttag) = crypto.tag_read (tagged) assert ok - assert tag == ttag + data = hdr[:len(hdr)//2] + b"junk-" + hdr[len(hdr)//2:] + with pytest.raises (crypto.InvalidHeader): + _ = crypto.hdr_read (data) -- 1.7.1