|
| 1 | +"""Round-trip and helper tests for hardcoded_key_decryptor. |
| 2 | +
|
| 3 | +Each cipher/mode should satisfy: strip_pkcs7(decrypt(encrypt(x))) == x. |
| 4 | +""" |
| 5 | +import base64 |
| 6 | + |
| 7 | +import pytest |
| 8 | + |
| 9 | +import hardcoded_key_decryptor as hkd |
| 10 | + |
| 11 | +# Example key material (not real recovered keys — just fixed test vectors). |
| 12 | +DES_KEY = bytes.fromhex("B43F84D110B4E991") |
| 13 | +DES_IV = bytes.fromhex("01D8AEE649AD9227") |
| 14 | +# RC2 block size is 8 bytes (so the CBC IV is 8 bytes). Modern `cryptography` |
| 15 | +# only accepts 128-bit (16-byte) RC2 keys, so use one here. |
| 16 | +RC2_KEY = bytes.fromhex("0102030405060708090A0B0C0D0E0F10") |
| 17 | +RC2_IV = bytes.fromhex("1112131415161718") |
| 18 | + |
| 19 | +TEXTS = [ |
| 20 | + "", # empty -> a full block of padding |
| 21 | + "a", |
| 22 | + "8bytes!!", # exactly one block |
| 23 | + "RiverDragon#Storm25", |
| 24 | + "unicode: café ☕ 日本語", |
| 25 | + "x" * 100, # spans many blocks |
| 26 | +] |
| 27 | + |
| 28 | + |
| 29 | +@pytest.mark.parametrize("text", TEXTS) |
| 30 | +def test_des_cbc_roundtrip(text): |
| 31 | + ct = hkd.encrypt(text.encode("utf-8"), DES_KEY, DES_IV, "des", "cbc") |
| 32 | + pt = hkd.strip_pkcs7(hkd.decrypt(ct, DES_KEY, DES_IV, "des", "cbc")) |
| 33 | + assert pt.decode("utf-8") == text |
| 34 | + |
| 35 | + |
| 36 | +@pytest.mark.parametrize("text", TEXTS) |
| 37 | +def test_rc2_cbc_roundtrip(text): |
| 38 | + ct = hkd.encrypt(text.encode("utf-8"), RC2_KEY, RC2_IV, "rc2", "cbc") |
| 39 | + pt = hkd.strip_pkcs7(hkd.decrypt(ct, RC2_KEY, RC2_IV, "rc2", "cbc")) |
| 40 | + assert pt.decode("utf-8") == text |
| 41 | + |
| 42 | + |
| 43 | +@pytest.mark.parametrize("text", TEXTS) |
| 44 | +def test_des_ecb_roundtrip(text): |
| 45 | + # ECB ignores the IV, but the engine still accepts one. |
| 46 | + ct = hkd.encrypt(text.encode("utf-8"), DES_KEY, DES_IV, "des", "ecb") |
| 47 | + pt = hkd.strip_pkcs7(hkd.decrypt(ct, DES_KEY, DES_IV, "des", "ecb")) |
| 48 | + assert pt.decode("utf-8") == text |
| 49 | + |
| 50 | + |
| 51 | +def test_ciphertext_is_base64_recoverable(): |
| 52 | + """The base64 the tool prints must decode back through fix_b64 + decrypt.""" |
| 53 | + text = "RiverDragon#Storm25" |
| 54 | + ct = hkd.encrypt(text.encode("utf-8"), DES_KEY, DES_IV, "des", "cbc") |
| 55 | + b64 = base64.b64encode(ct).decode() |
| 56 | + pt = hkd.strip_pkcs7( |
| 57 | + hkd.decrypt(hkd.fix_b64(b64), DES_KEY, DES_IV, "des", "cbc") |
| 58 | + ) |
| 59 | + assert pt.decode("utf-8") == text |
| 60 | + |
| 61 | + |
| 62 | +def test_pkcs7_add_then_strip_is_identity(): |
| 63 | + for n in range(0, 33): |
| 64 | + data = b"A" * n |
| 65 | + padded = hkd.add_pkcs7(data) |
| 66 | + assert len(padded) % hkd.BLOCK_SIZE == 0 |
| 67 | + assert 1 <= (padded[-1]) <= hkd.BLOCK_SIZE |
| 68 | + assert hkd.strip_pkcs7(padded) == data |
| 69 | + |
| 70 | + |
| 71 | +def test_parse_hex_accepts_common_formats(): |
| 72 | + expected = bytes([0xB4, 0x3F, 0x84, 0xD1]) |
| 73 | + assert hkd.parse_hex("B4 3F 84 D1") == expected |
| 74 | + assert hkd.parse_hex("B4,3F,84,D1") == expected |
| 75 | + assert hkd.parse_hex("0xB4 0x3F 0x84 0xD1") == expected |
| 76 | + assert hkd.parse_hex("B43F84D1") == expected |
| 77 | + |
| 78 | + |
| 79 | +def test_fix_b64_restores_missing_padding(): |
| 80 | + raw = b"\x00\x01\x02\x03\x04" |
| 81 | + stripped = base64.b64encode(raw).decode().rstrip("=") |
| 82 | + assert hkd.fix_b64(stripped) == raw |
| 83 | + |
| 84 | + |
| 85 | +def test_wrong_key_does_not_recover_plaintext(): |
| 86 | + text = "RiverDragon#Storm25" |
| 87 | + ct = hkd.encrypt(text.encode("utf-8"), DES_KEY, DES_IV, "des", "cbc") |
| 88 | + wrong_key = bytes.fromhex("0000000000000000") |
| 89 | + pt = hkd.strip_pkcs7(hkd.decrypt(ct, wrong_key, DES_IV, "des", "cbc")) |
| 90 | + assert pt != text.encode("utf-8") |
| 91 | + |
| 92 | + |
| 93 | +def test_unknown_method_and_mode_raise(): |
| 94 | + with pytest.raises(ValueError): |
| 95 | + hkd.build_algorithm("aes", DES_KEY) |
| 96 | + with pytest.raises(ValueError): |
| 97 | + hkd.build_mode("ctr", DES_IV) |
0 commit comments