| 1 | // -------------------------------------------------------------------------- |
|---|
| 2 | // |
|---|
| 3 | // File |
|---|
| 4 | // Name: testcrypto.cpp |
|---|
| 5 | // Purpose: test lib/crypto |
|---|
| 6 | // Created: 1/12/03 |
|---|
| 7 | // |
|---|
| 8 | // -------------------------------------------------------------------------- |
|---|
| 9 | |
|---|
| 10 | #include "Box.h" |
|---|
| 11 | |
|---|
| 12 | #include <string.h> |
|---|
| 13 | #include <strings.h> |
|---|
| 14 | #include <openssl/rand.h> |
|---|
| 15 | |
|---|
| 16 | #include "Test.h" |
|---|
| 17 | #include "CipherContext.h" |
|---|
| 18 | #include "CipherBlowfish.h" |
|---|
| 19 | #include "CipherAES.h" |
|---|
| 20 | #include "CipherException.h" |
|---|
| 21 | #include "RollingChecksum.h" |
|---|
| 22 | #include "Random.h" |
|---|
| 23 | |
|---|
| 24 | #include "MemLeakFindOn.h" |
|---|
| 25 | |
|---|
| 26 | #define STRING1 "Mary had a little lamb" |
|---|
| 27 | #define STRING2 "Skjdf sdjf sjksd fjkhsdfjk hsdfuiohcverfg sdfnj sdfgkljh sdfjb jlhdfvghsdip vjsdfv bsdfhjvg yuiosdvgpvj kvbn m,sdvb sdfuiovg sdfuivhsdfjkv" |
|---|
| 28 | |
|---|
| 29 | #define KEY "0123456701234567012345670123456" |
|---|
| 30 | #define KEY2 "1234567012345670123456A" |
|---|
| 31 | |
|---|
| 32 | #define CHECKSUM_DATA_SIZE (128*1024) |
|---|
| 33 | #define CHECKSUM_BLOCK_SIZE_BASE (65*1024) |
|---|
| 34 | #define CHECKSUM_BLOCK_SIZE_LAST (CHECKSUM_BLOCK_SIZE_BASE + 64) |
|---|
| 35 | #define CHECKSUM_ROLLS 16 |
|---|
| 36 | |
|---|
| 37 | void check_random_int(uint32_t max) |
|---|
| 38 | { |
|---|
| 39 | for(int c = 0; c < 1024; ++c) |
|---|
| 40 | { |
|---|
| 41 | uint32_t v = Random::RandomInt(max); |
|---|
| 42 | TEST_THAT(v >= 0 && v <= max); |
|---|
| 43 | } |
|---|
| 44 | } |
|---|
| 45 | |
|---|
| 46 | #define ZERO_BUFFER(x) ::memset(x, 0, sizeof(x)); |
|---|
| 47 | |
|---|
| 48 | template<typename CipherType, int BLOCKSIZE> |
|---|
| 49 | void test_cipher() |
|---|
| 50 | { |
|---|
| 51 | { |
|---|
| 52 | // Make a couple of cipher contexts |
|---|
| 53 | CipherContext encrypt1; |
|---|
| 54 | encrypt1.Reset(); |
|---|
| 55 | encrypt1.Init(CipherContext::Encrypt, CipherType(CipherDescription::Mode_CBC, KEY, sizeof(KEY))); |
|---|
| 56 | TEST_CHECK_THROWS(encrypt1.Init(CipherContext::Encrypt, CipherType(CipherDescription::Mode_CBC, KEY, sizeof(KEY))), |
|---|
| 57 | CipherException, AlreadyInitialised); |
|---|
| 58 | // Encrpt something |
|---|
| 59 | char buf1[256]; |
|---|
| 60 | unsigned int buf1_used = encrypt1.TransformBlock(buf1, sizeof(buf1), STRING1, sizeof(STRING1)); |
|---|
| 61 | TEST_THAT(buf1_used >= sizeof(STRING1)); |
|---|
| 62 | // Decrypt it |
|---|
| 63 | CipherContext decrypt1; |
|---|
| 64 | decrypt1.Init(CipherContext::Decrypt, CipherType(CipherDescription::Mode_CBC, KEY, sizeof(KEY))); |
|---|
| 65 | char buf1_de[256]; |
|---|
| 66 | unsigned int buf1_de_used = decrypt1.TransformBlock(buf1_de, sizeof(buf1_de), buf1, buf1_used); |
|---|
| 67 | TEST_THAT(buf1_de_used == sizeof(STRING1)); |
|---|
| 68 | TEST_THAT(memcmp(STRING1, buf1_de, sizeof(STRING1)) == 0); |
|---|
| 69 | |
|---|
| 70 | // Use them again... |
|---|
| 71 | char buf1_de2[256]; |
|---|
| 72 | unsigned int buf1_de2_used = decrypt1.TransformBlock(buf1_de2, sizeof(buf1_de2), buf1, buf1_used); |
|---|
| 73 | TEST_THAT(buf1_de2_used == sizeof(STRING1)); |
|---|
| 74 | TEST_THAT(memcmp(STRING1, buf1_de2, sizeof(STRING1)) == 0); |
|---|
| 75 | |
|---|
| 76 | // Test the interface |
|---|
| 77 | char buf2[256]; |
|---|
| 78 | TEST_CHECK_THROWS(encrypt1.Transform(buf2, sizeof(buf2), STRING1, sizeof(STRING1)), |
|---|
| 79 | CipherException, BeginNotCalled); |
|---|
| 80 | TEST_CHECK_THROWS(encrypt1.Final(buf2, sizeof(buf2)), |
|---|
| 81 | CipherException, BeginNotCalled); |
|---|
| 82 | encrypt1.Begin(); |
|---|
| 83 | int e = 0; |
|---|
| 84 | e = encrypt1.Transform(buf2, sizeof(buf2), STRING2, sizeof(STRING2) - 16); |
|---|
| 85 | e += encrypt1.Transform(buf2 + e, sizeof(buf2) - e, STRING2 + sizeof(STRING2) - 16, 16); |
|---|
| 86 | e += encrypt1.Final(buf2 + e, sizeof(buf2) - e); |
|---|
| 87 | TEST_THAT(e >= (int)sizeof(STRING2)); |
|---|
| 88 | |
|---|
| 89 | // Then decrypt |
|---|
| 90 | char buf2_de[256]; |
|---|
| 91 | decrypt1.Begin(); |
|---|
| 92 | TEST_CHECK_THROWS(decrypt1.Transform(buf2_de, 2, buf2, e), CipherException, OutputBufferTooSmall); |
|---|
| 93 | TEST_CHECK_THROWS(decrypt1.Final(buf2_de, 2), CipherException, OutputBufferTooSmall); |
|---|
| 94 | int d = decrypt1.Transform(buf2_de, sizeof(buf2_de), buf2, e - 48); |
|---|
| 95 | d += decrypt1.Transform(buf2_de + d, sizeof(buf2_de) - d, buf2 + e - 48, 48); |
|---|
| 96 | d += decrypt1.Final(buf2_de + d, sizeof(buf2_de) - d); |
|---|
| 97 | TEST_THAT(d == sizeof(STRING2)); |
|---|
| 98 | TEST_THAT(memcmp(STRING2, buf2_de, sizeof(STRING2)) == 0); |
|---|
| 99 | |
|---|
| 100 | // Try a reset and rekey |
|---|
| 101 | encrypt1.Reset(); |
|---|
| 102 | encrypt1.Init(CipherContext::Encrypt, CipherType(CipherDescription::Mode_CBC, KEY2, sizeof(KEY2))); |
|---|
| 103 | buf1_used = encrypt1.TransformBlock(buf1, sizeof(buf1), STRING1, sizeof(STRING1)); |
|---|
| 104 | } |
|---|
| 105 | |
|---|
| 106 | // Test initialisation vectors |
|---|
| 107 | { |
|---|
| 108 | // Init with random IV |
|---|
| 109 | CipherContext encrypt2; |
|---|
| 110 | encrypt2.Init(CipherContext::Encrypt, CipherType(CipherDescription::Mode_CBC, KEY, sizeof(KEY))); |
|---|
| 111 | int ivLen; |
|---|
| 112 | char iv2[BLOCKSIZE]; |
|---|
| 113 | const void *ivGen = encrypt2.SetRandomIV(ivLen); |
|---|
| 114 | TEST_THAT(ivLen == BLOCKSIZE); // block size |
|---|
| 115 | TEST_THAT(ivGen != 0); |
|---|
| 116 | memcpy(iv2, ivGen, ivLen); |
|---|
| 117 | |
|---|
| 118 | char buf3[256]; |
|---|
| 119 | unsigned int buf3_used = encrypt2.TransformBlock(buf3, sizeof(buf3), STRING2, sizeof(STRING2)); |
|---|
| 120 | |
|---|
| 121 | // Encrypt again with different IV |
|---|
| 122 | char iv3[BLOCKSIZE]; |
|---|
| 123 | int ivLen3; |
|---|
| 124 | const void *ivGen3 = encrypt2.SetRandomIV(ivLen3); |
|---|
| 125 | TEST_THAT(ivLen3 == BLOCKSIZE); // block size |
|---|
| 126 | TEST_THAT(ivGen3 != 0); |
|---|
| 127 | memcpy(iv3, ivGen3, ivLen3); |
|---|
| 128 | // Check the two generated IVs are different |
|---|
| 129 | TEST_THAT(memcmp(iv2, iv3, BLOCKSIZE) != 0); |
|---|
| 130 | |
|---|
| 131 | char buf4[256]; |
|---|
| 132 | unsigned int buf4_used = encrypt2.TransformBlock(buf4, sizeof(buf4), STRING2, sizeof(STRING2)); |
|---|
| 133 | |
|---|
| 134 | // check encryptions are different |
|---|
| 135 | TEST_THAT(buf3_used == buf4_used); |
|---|
| 136 | TEST_THAT(memcmp(buf3, buf4, buf3_used) != 0); |
|---|
| 137 | |
|---|
| 138 | // Test that decryption with the right IV works |
|---|
| 139 | CipherContext decrypt2; |
|---|
| 140 | decrypt2.Init(CipherContext::Decrypt, CipherType(CipherDescription::Mode_CBC, KEY, sizeof(KEY), iv2)); |
|---|
| 141 | char buf3_de[256]; |
|---|
| 142 | unsigned int buf3_de_used = decrypt2.TransformBlock(buf3_de, sizeof(buf3_de), buf3, buf3_used); |
|---|
| 143 | TEST_THAT(buf3_de_used == sizeof(STRING2)); |
|---|
| 144 | TEST_THAT(memcmp(STRING2, buf3_de, sizeof(STRING2)) == 0); |
|---|
| 145 | |
|---|
| 146 | // And that using the wrong one doesn't |
|---|
| 147 | decrypt2.SetIV(iv3); |
|---|
| 148 | buf3_de_used = decrypt2.TransformBlock(buf3_de, sizeof(buf3_de), buf3, buf3_used); |
|---|
| 149 | TEST_THAT(buf3_de_used == sizeof(STRING2)); |
|---|
| 150 | TEST_THAT(memcmp(STRING2, buf3_de, sizeof(STRING2)) != 0); |
|---|
| 151 | } |
|---|
| 152 | |
|---|
| 153 | // Test with padding off. |
|---|
| 154 | { |
|---|
| 155 | CipherContext encrypt3; |
|---|
| 156 | encrypt3.Init(CipherContext::Encrypt, CipherType(CipherDescription::Mode_CBC, KEY, sizeof(KEY))); |
|---|
| 157 | encrypt3.UsePadding(false); |
|---|
| 158 | |
|---|
| 159 | // Should fail because the encrypted size is not a multiple of the block size |
|---|
| 160 | char buf4[256]; |
|---|
| 161 | encrypt3.Begin(); |
|---|
| 162 | ZERO_BUFFER(buf4); |
|---|
| 163 | int buf4_used = encrypt3.Transform(buf4, sizeof(buf4), STRING2, 6); |
|---|
| 164 | TEST_CHECK_THROWS(encrypt3.Final(buf4, sizeof(buf4)), CipherException, EVPFinalFailure); |
|---|
| 165 | |
|---|
| 166 | // Check a nice encryption with the correct block size |
|---|
| 167 | CipherContext encrypt4; |
|---|
| 168 | encrypt4.Init(CipherContext::Encrypt, CipherType(CipherDescription::Mode_CBC, KEY, sizeof(KEY))); |
|---|
| 169 | encrypt4.UsePadding(false); |
|---|
| 170 | encrypt4.Begin(); |
|---|
| 171 | ZERO_BUFFER(buf4); |
|---|
| 172 | buf4_used = encrypt4.Transform(buf4, sizeof(buf4), STRING2, 16); |
|---|
| 173 | buf4_used += encrypt4.Final(buf4+buf4_used, sizeof(buf4)); |
|---|
| 174 | TEST_THAT(buf4_used == 16); |
|---|
| 175 | |
|---|
| 176 | // Check it's encrypted to the same thing as when there's padding on |
|---|
| 177 | CipherContext encrypt4b; |
|---|
| 178 | encrypt4b.Init(CipherContext::Encrypt, CipherType(CipherDescription::Mode_CBC, KEY, sizeof(KEY))); |
|---|
| 179 | encrypt4b.Begin(); |
|---|
| 180 | char buf4b[256]; |
|---|
| 181 | ZERO_BUFFER(buf4b); |
|---|
| 182 | int buf4b_used = encrypt4b.Transform(buf4b, sizeof(buf4b), STRING2, 16); |
|---|
| 183 | buf4b_used += encrypt4b.Final(buf4b + buf4b_used, sizeof(buf4b)); |
|---|
| 184 | TEST_THAT(buf4b_used == 16+BLOCKSIZE); |
|---|
| 185 | TEST_THAT(::memcmp(buf4, buf4b, 16) == 0); |
|---|
| 186 | |
|---|
| 187 | // Decrypt |
|---|
| 188 | char buf4_de[256]; |
|---|
| 189 | CipherContext decrypt4; |
|---|
| 190 | decrypt4.Init(CipherContext::Decrypt, CipherType(CipherDescription::Mode_CBC, KEY, sizeof(KEY))); |
|---|
| 191 | decrypt4.UsePadding(false); |
|---|
| 192 | decrypt4.Begin(); |
|---|
| 193 | ZERO_BUFFER(buf4_de); |
|---|
| 194 | int buf4_de_used = decrypt4.Transform(buf4_de, sizeof(buf4_de), buf4, 16); |
|---|
| 195 | buf4_de_used += decrypt4.Final(buf4_de+buf4_de_used, sizeof(buf4_de)); |
|---|
| 196 | TEST_THAT(buf4_de_used == 16); |
|---|
| 197 | TEST_THAT(::memcmp(buf4_de, STRING2, 16) == 0); |
|---|
| 198 | |
|---|
| 199 | // Test that the TransformBlock thing works as expected too with blocks the same size as the input |
|---|
| 200 | TEST_THAT(encrypt4.TransformBlock(buf4, 16, STRING2, 16) == 16); |
|---|
| 201 | // But that it exceptions if we try the trick with padding on |
|---|
| 202 | encrypt4.UsePadding(true); |
|---|
| 203 | TEST_CHECK_THROWS(encrypt4.TransformBlock(buf4, 16, STRING2, 16), CipherException, OutputBufferTooSmall); |
|---|
| 204 | } |
|---|
| 205 | |
|---|
| 206 | // And again, but with different string size |
|---|
| 207 | { |
|---|
| 208 | char buf4[256]; |
|---|
| 209 | int buf4_used; |
|---|
| 210 | |
|---|
| 211 | // Check a nice encryption with the correct block size |
|---|
| 212 | CipherContext encrypt4; |
|---|
| 213 | encrypt4.Init(CipherContext::Encrypt, CipherType(CipherDescription::Mode_CBC, KEY, sizeof(KEY))); |
|---|
| 214 | encrypt4.UsePadding(false); |
|---|
| 215 | encrypt4.Begin(); |
|---|
| 216 | ZERO_BUFFER(buf4); |
|---|
| 217 | buf4_used = encrypt4.Transform(buf4, sizeof(buf4), STRING2, (BLOCKSIZE*3)); // do three blocks worth |
|---|
| 218 | buf4_used += encrypt4.Final(buf4+buf4_used, sizeof(buf4)); |
|---|
| 219 | TEST_THAT(buf4_used == (BLOCKSIZE*3)); |
|---|
| 220 | |
|---|
| 221 | // Check it's encrypted to the same thing as when there's padding on |
|---|
| 222 | CipherContext encrypt4b; |
|---|
| 223 | encrypt4b.Init(CipherContext::Encrypt, CipherType(CipherDescription::Mode_CBC, KEY, sizeof(KEY))); |
|---|
| 224 | encrypt4b.Begin(); |
|---|
| 225 | char buf4b[256]; |
|---|
| 226 | ZERO_BUFFER(buf4b); |
|---|
| 227 | int buf4b_used = encrypt4b.Transform(buf4b, sizeof(buf4b), STRING2, (BLOCKSIZE*3)); |
|---|
| 228 | buf4b_used += encrypt4b.Final(buf4b + buf4b_used, sizeof(buf4b)); |
|---|
| 229 | TEST_THAT(buf4b_used == (BLOCKSIZE*4)); |
|---|
| 230 | TEST_THAT(::memcmp(buf4, buf4b, (BLOCKSIZE*3)) == 0); |
|---|
| 231 | |
|---|
| 232 | // Decrypt |
|---|
| 233 | char buf4_de[256]; |
|---|
| 234 | CipherContext decrypt4; |
|---|
| 235 | decrypt4.Init(CipherContext::Decrypt, CipherType(CipherDescription::Mode_CBC, KEY, sizeof(KEY))); |
|---|
| 236 | decrypt4.UsePadding(false); |
|---|
| 237 | decrypt4.Begin(); |
|---|
| 238 | ZERO_BUFFER(buf4_de); |
|---|
| 239 | int buf4_de_used = decrypt4.Transform(buf4_de, sizeof(buf4_de), buf4, (BLOCKSIZE*3)); |
|---|
| 240 | buf4_de_used += decrypt4.Final(buf4_de+buf4_de_used, sizeof(buf4_de)); |
|---|
| 241 | TEST_THAT(buf4_de_used == (BLOCKSIZE*3)); |
|---|
| 242 | TEST_THAT(::memcmp(buf4_de, STRING2, (BLOCKSIZE*3)) == 0); |
|---|
| 243 | |
|---|
| 244 | // Test that the TransformBlock thing works as expected too with blocks the same size as the input |
|---|
| 245 | TEST_THAT(encrypt4.TransformBlock(buf4, (BLOCKSIZE*3), STRING2, (BLOCKSIZE*3)) == (BLOCKSIZE*3)); |
|---|
| 246 | // But that it exceptions if we try the trick with padding on |
|---|
| 247 | encrypt4.UsePadding(true); |
|---|
| 248 | TEST_CHECK_THROWS(encrypt4.TransformBlock(buf4, (BLOCKSIZE*3), STRING2, (BLOCKSIZE*3)), CipherException, OutputBufferTooSmall); |
|---|
| 249 | } |
|---|
| 250 | } |
|---|
| 251 | |
|---|
| 252 | int test(int argc, const char *argv[]) |
|---|
| 253 | { |
|---|
| 254 | Random::Initialise(); |
|---|
| 255 | |
|---|
| 256 | // Cipher type |
|---|
| 257 | ::printf("Blowfish...\n"); |
|---|
| 258 | test_cipher<CipherBlowfish, 8>(); |
|---|
| 259 | #ifndef HAVE_OLD_SSL |
|---|
| 260 | ::printf("AES...\n"); |
|---|
| 261 | test_cipher<CipherAES, 16>(); |
|---|
| 262 | #else |
|---|
| 263 | ::printf("Skipping AES -- not supported by version of OpenSSL in use.\n"); |
|---|
| 264 | #endif |
|---|
| 265 | |
|---|
| 266 | ::printf("Misc...\n"); |
|---|
| 267 | // Check rolling checksums |
|---|
| 268 | uint8_t *checkdata_blk = (uint8_t *)malloc(CHECKSUM_DATA_SIZE); |
|---|
| 269 | uint8_t *checkdata = checkdata_blk; |
|---|
| 270 | RAND_pseudo_bytes(checkdata, CHECKSUM_DATA_SIZE); |
|---|
| 271 | for(int size = CHECKSUM_BLOCK_SIZE_BASE; size <= CHECKSUM_BLOCK_SIZE_LAST; ++size) |
|---|
| 272 | { |
|---|
| 273 | // Test skip-roll code |
|---|
| 274 | RollingChecksum rollFast(checkdata, size); |
|---|
| 275 | rollFast.RollForwardSeveral(checkdata, checkdata+size, size, CHECKSUM_ROLLS/2); |
|---|
| 276 | RollingChecksum calc(checkdata + (CHECKSUM_ROLLS/2), size); |
|---|
| 277 | TEST_THAT(calc.GetChecksum() == rollFast.GetChecksum()); |
|---|
| 278 | |
|---|
| 279 | //printf("size = %d\n", size); |
|---|
| 280 | // Checksum to roll |
|---|
| 281 | RollingChecksum roll(checkdata, size); |
|---|
| 282 | |
|---|
| 283 | // Roll forward |
|---|
| 284 | for(int l = 0; l < CHECKSUM_ROLLS; ++l) |
|---|
| 285 | { |
|---|
| 286 | // Calculate new one |
|---|
| 287 | RollingChecksum calc(checkdata, size); |
|---|
| 288 | |
|---|
| 289 | //printf("%08X %08X %d %d\n", roll.GetChecksum(), calc.GetChecksum(), checkdata[0], checkdata[size]); |
|---|
| 290 | |
|---|
| 291 | // Compare them! |
|---|
| 292 | TEST_THAT(calc.GetChecksum() == roll.GetChecksum()); |
|---|
| 293 | |
|---|
| 294 | // Roll it onwards |
|---|
| 295 | roll.RollForward(checkdata[0], checkdata[size], size); |
|---|
| 296 | |
|---|
| 297 | // increment |
|---|
| 298 | ++checkdata; |
|---|
| 299 | } |
|---|
| 300 | } |
|---|
| 301 | ::free(checkdata_blk); |
|---|
| 302 | |
|---|
| 303 | // Random integers |
|---|
| 304 | check_random_int(0); |
|---|
| 305 | check_random_int(1); |
|---|
| 306 | check_random_int(5); |
|---|
| 307 | check_random_int(15); // all 1's |
|---|
| 308 | check_random_int(1022); |
|---|
| 309 | |
|---|
| 310 | return 0; |
|---|
| 311 | } |
|---|
| 312 | |
|---|
| 313 | |
|---|
| 314 | |
|---|