|
| 1 | +#include <bkcrack/Data.hpp> |
| 2 | +#include <bkcrack/Keys.hpp> |
| 3 | + |
| 4 | +#include <TestRunner.hpp> |
| 5 | +#include <string_view> |
| 6 | + |
| 7 | +namespace |
| 8 | +{ |
| 9 | +const auto plaintext = std::vector<std::uint8_t>{ |
| 10 | + 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', |
| 11 | +}; |
| 12 | +const auto ciphertext = std::vector<std::uint8_t>{ |
| 13 | + 0x3e, 0xb4, 0xc5, 0x92, 0x58, 0x40, 0x9a, 0x6c, 0xed, 0x99, 0x65, 0x81, |
| 14 | + 0x66, 0x1b, 0x1d, 0xda, 0x5d, 0x8a, 0x8c, 0x30, 0x07, 0x76, 0x50, 0xbb, |
| 15 | +}; |
| 16 | +const auto keystream = std::vector<std::uint8_t>{ |
| 17 | + 0xee, 0x96, 0x22, 0x47, 0x78, 0xb8, 0x73, 0x54, 0x4c, 0xd7, 0x7d, 0x0d, |
| 18 | + 0x2e, 0x7e, 0x71, 0xb6, 0x32, 0xaa, 0xdb, 0x5f, 0x75, 0x1a, 0x34, 0x9a, |
| 19 | +}; |
| 20 | + |
| 21 | +auto prefix(const std::vector<std::uint8_t>& vector, std::size_t length) -> std::vector<std::uint8_t> |
| 22 | +{ |
| 23 | + return std::vector<std::uint8_t>{vector.begin(), vector.begin() + length}; |
| 24 | +} |
| 25 | +auto slice(const std::vector<std::uint8_t>& vector, std::size_t begin, std::size_t end) -> std::vector<std::uint8_t> |
| 26 | +{ |
| 27 | + return std::vector<std::uint8_t>{vector.begin() + begin, vector.begin() + end}; |
| 28 | +} |
| 29 | +} // namespace |
| 30 | + |
| 31 | +TEST("Data::Error") |
| 32 | +{ |
| 33 | + const auto error = Data::Error{"description"}; |
| 34 | + CHECK(error.what() == std::string_view{"Data error: description."}); |
| 35 | +} |
| 36 | + |
| 37 | +TEST("contiguous plaintext only") |
| 38 | +{ |
| 39 | + const auto data = Data{ciphertext, plaintext, 0, {}}; |
| 40 | + CHECK(data.ciphertext == ciphertext); |
| 41 | + CHECK(data.plaintext == plaintext); |
| 42 | + CHECK(data.offset == 12); |
| 43 | + CHECK(data.keystream == slice(keystream, 12, 24)); |
| 44 | + CHECK(data.extraPlaintext.empty()); |
| 45 | +} |
| 46 | + |
| 47 | +TEST("contiguous plaintext and sparse extra plaintext") |
| 48 | +{ |
| 49 | + const auto data = Data{ciphertext, prefix(plaintext, 8), 0, {{-3, 0x4e}, {-2, 0x18}, {10, 'd'}, {11, '!'}}}; |
| 50 | + CHECK(data.ciphertext == ciphertext); |
| 51 | + CHECK(data.plaintext == prefix(plaintext, 8)); |
| 52 | + CHECK(data.offset == 12); |
| 53 | + CHECK(data.keystream == slice(keystream, 12, 20)); |
| 54 | + CHECK(data.extraPlaintext == decltype(data.extraPlaintext){{22, 'd'}, {23, '!'}, {10, 0x18}, {9, 0x4e}}); |
| 55 | +} |
| 56 | + |
| 57 | +TEST("merge extra plaintext after") |
| 58 | +{ |
| 59 | + const auto data = Data{ciphertext, prefix(plaintext, 10), 0, {{10, 'd'}, {11, '!'}}}; |
| 60 | + CHECK(data.ciphertext == ciphertext); |
| 61 | + CHECK(data.plaintext == plaintext); |
| 62 | + CHECK(data.offset == 12); |
| 63 | + CHECK(data.keystream == slice(keystream, 12, 24)); |
| 64 | + CHECK(data.extraPlaintext.empty()); |
| 65 | +} |
| 66 | + |
| 67 | +TEST("merge extra plaintext before") |
| 68 | +{ |
| 69 | + const auto data = Data{ciphertext, slice(plaintext, 2, 12), 2, {{0, 'H'}, {1, 'e'}}}; |
| 70 | + CHECK(data.ciphertext == ciphertext); |
| 71 | + CHECK(data.plaintext == plaintext); |
| 72 | + CHECK(data.offset == 12); |
| 73 | + CHECK(data.keystream == slice(keystream, 12, 24)); |
| 74 | + CHECK(data.extraPlaintext.empty()); |
| 75 | +} |
| 76 | + |
| 77 | +TEST("overwrite contiguous plaintext with extra plaintext") |
| 78 | +{ |
| 79 | + auto overwrittenPlaintext = plaintext; |
| 80 | + overwrittenPlaintext[5] = '*'; |
| 81 | + |
| 82 | + auto overwrittenKeystream = keystream; |
| 83 | + overwrittenKeystream[17] = ciphertext[17] ^ '*'; |
| 84 | + |
| 85 | + const auto data = Data{ciphertext, plaintext, 0, {{5, '*'}}}; |
| 86 | + CHECK(data.ciphertext == ciphertext); |
| 87 | + CHECK(data.plaintext == overwrittenPlaintext); |
| 88 | + CHECK(data.offset == 12); |
| 89 | + CHECK(data.keystream == slice(overwrittenKeystream, 12, 24)); |
| 90 | + CHECK(data.extraPlaintext.empty()); |
| 91 | +} |
| 92 | + |
| 93 | +TEST("long contiguous extra plaintext after") |
| 94 | +{ |
| 95 | + const auto data = Data{ciphertext, |
| 96 | + {0x8c, 'H', 'e', 'l'}, |
| 97 | + -1, |
| 98 | + {{4, 'o'}, {5, ' '}, {6, 'W'}, {7, 'o'}, {8, 'r'}, {9, 'l'}, {10, 'd'}, {11, '!'}}}; |
| 99 | + CHECK(data.ciphertext == ciphertext); |
| 100 | + CHECK(data.plaintext == slice(plaintext, 4, 12)); |
| 101 | + CHECK(data.offset == 16); |
| 102 | + CHECK(data.keystream == slice(keystream, 16, 24)); |
| 103 | + CHECK(data.extraPlaintext == decltype(data.extraPlaintext){{14, 'l'}, {13, 'e'}, {12, 'H'}, {11, 0x8c}}); |
| 104 | +} |
| 105 | + |
| 106 | +TEST("long contiguous extra plaintext before") |
| 107 | +{ |
| 108 | + const auto data = Data{ciphertext, |
| 109 | + {'r', 'l', 'd', '!'}, |
| 110 | + 8, |
| 111 | + {{-1, 0x8c}, {0, 'H'}, {1, 'e'}, {2, 'l'}, {3, 'l'}, {4, 'o'}, {5, ' '}, {6, 'W'}}}; |
| 112 | + CHECK(data.ciphertext == ciphertext); |
| 113 | + CHECK(data.plaintext == decltype(data.plaintext){0x8c, 'H', 'e', 'l', 'l', 'o', ' ', 'W'}); |
| 114 | + CHECK(data.offset == 11); |
| 115 | + CHECK(data.keystream == slice(keystream, 11, 19)); |
| 116 | + CHECK(data.extraPlaintext == decltype(data.extraPlaintext){{20, 'r'}, {21, 'l'}, {22, 'd'}, {23, '!'}}); |
| 117 | +} |
| 118 | + |
| 119 | +TEST("extra plaintext only") |
| 120 | +{ |
| 121 | + const auto extraPlaintext = std::map<int, std::uint8_t>{ |
| 122 | + {0, 'H'}, {1, 'e'}, {2, 'l'}, {3, 'l'}, {4, 'o'}, {5, ' '}, |
| 123 | + {6, 'W'}, {7, 'o'}, {8, 'r'}, {9, 'l'}, {10, 'd'}, {11, '!'}, |
| 124 | + }; |
| 125 | + const auto data = Data{ciphertext, {}, -1, extraPlaintext}; |
| 126 | + CHECK(data.ciphertext == ciphertext); |
| 127 | + CHECK(data.plaintext == plaintext); |
| 128 | + CHECK(data.offset == 12); |
| 129 | + CHECK(data.keystream == slice(keystream, 12, 24)); |
| 130 | + CHECK(data.extraPlaintext.empty()); |
| 131 | +} |
| 132 | + |
| 133 | +TEST("not enough data") |
| 134 | +{ |
| 135 | + // not enough ciphertext |
| 136 | + CHECK_THROWS(Data::Error, Data{prefix(ciphertext, 11), prefix(plaintext, 11), -12, {}}); |
| 137 | + |
| 138 | + // less ciphertext than plaintext |
| 139 | + CHECK_THROWS(Data::Error, Data{prefix(ciphertext, 12), std::vector<std::uint8_t>('A', 13), -12, {}}); |
| 140 | + |
| 141 | + // not enough contiguous plaintext |
| 142 | + CHECK_THROWS(Data::Error, Data{ciphertext, prefix(plaintext, 7), 0, {}}); |
| 143 | + |
| 144 | + // not enough contiguous + extra plaintext |
| 145 | + CHECK_THROWS(Data::Error, Data{ciphertext, prefix(plaintext, 9), 0, {{10, 'd'}, {11, '!'}}}); |
| 146 | +} |
| 147 | + |
| 148 | +TEST("invalid offset") |
| 149 | +{ |
| 150 | + // offset is too small |
| 151 | + CHECK_THROWS(Data::Error, Data{ciphertext, plaintext, -13, {}}); |
| 152 | + |
| 153 | + // offset is too large |
| 154 | + CHECK_THROWS(Data::Error, Data{ciphertext, plaintext, 1, {}}); |
| 155 | + |
| 156 | + // extra plaintext offset is too small |
| 157 | + CHECK_THROWS(Data::Error, Data{ciphertext, plaintext, 0, {{-13, 0x00}}}); |
| 158 | + |
| 159 | + // extra plaintext offset is too large |
| 160 | + CHECK_THROWS(Data::Error, Data{ciphertext, plaintext, 0, {{12, 0x00}}}); |
| 161 | +} |
0 commit comments