1 /* Copyright (c) 2017, Google Inc. 2 * 3 * Permission to use, copy, modify, and/or distribute this software for any 4 * purpose with or without fee is hereby granted, provided that the above 5 * copyright notice and this permission notice appear in all copies. 6 * 7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ 14 15 // cavp_tdes_test processes a NIST TMOVS test vector request file and emits the 16 // corresponding response. 17 18 #include <stdlib.h> 19 20 #include <openssl/cipher.h> 21 #include <openssl/crypto.h> 22 #include <openssl/err.h> 23 24 #include "../crypto/test/file_test.h" 25 #include "cavp_test_util.h" 26 27 28 namespace { 29 30 struct TestCtx { 31 const EVP_CIPHER *cipher; 32 enum Mode { 33 kKAT, // Known Answer Test 34 kMCT, // Monte Carlo Test 35 }; 36 bool has_iv; 37 Mode mode; 38 }; 39 40 } 41 42 static bool TestKAT(FileTest *t, void *arg) { 43 TestCtx *ctx = reinterpret_cast<TestCtx *>(arg); 44 45 if (t->HasInstruction("ENCRYPT") == t->HasInstruction("DECRYPT")) { 46 t->PrintLine("Want either ENCRYPT or DECRYPT"); 47 return false; 48 } 49 enum { 50 kEncrypt, 51 kDecrypt, 52 } operation = t->HasInstruction("ENCRYPT") ? kEncrypt : kDecrypt; 53 54 if (t->HasAttribute("NumKeys")) { 55 // Another file format quirk: NumKeys is a single attribute line immediately 56 // following an instruction and should probably have been an instruction 57 // instead. If it is present, the file has separate attributes "KEY{1,2,3}". 58 // If it is not, the keys are concatenated in a single attribute "KEYs". 59 std::string num_keys; 60 t->GetAttribute(&num_keys, "NumKeys"); 61 t->InjectInstruction("NumKeys", num_keys); 62 63 std::string header = operation == kEncrypt ? "[ENCRYPT]" : "[DECRYPT]"; 64 printf("%s\r\n\r\n", header.c_str()); 65 66 return true; 67 } 68 69 enum { 70 kNotPresent, 71 kTwo, 72 kThree, 73 } num_keys = kNotPresent; 74 if (t->HasInstruction("NumKeys")) { 75 std::string num_keys_str; 76 t->GetInstruction(&num_keys_str, "NumKeys"); 77 const int n = strtoul(num_keys_str.c_str(), nullptr, 0); 78 if (n == 2) { 79 num_keys = kTwo; 80 } else if (n == 3) { 81 num_keys = kThree; 82 } else { 83 t->PrintLine("invalid NumKeys value"); 84 return false; 85 } 86 } 87 88 std::string count; 89 std::vector<uint8_t> keys, key1, key2, key3, iv, in, result; 90 const std::string in_label = 91 operation == kEncrypt ? "PLAINTEXT" : "CIPHERTEXT"; 92 // clang-format off 93 if (!t->GetAttribute(&count, "COUNT") || 94 (num_keys == 0 && !t->GetBytes(&keys, "KEYs")) || 95 (num_keys > 0 && 96 (!t->GetBytes(&key1, "KEY1") || 97 !t->GetBytes(&key2, "KEY2") || 98 !t->GetBytes(&key3, "KEY3"))) || 99 (ctx->has_iv && !t->GetBytes(&iv, "IV")) || 100 !t->GetBytes(&in, in_label)) { 101 return false; 102 } 103 // clang-format on 104 std::vector<uint8_t> key; 105 if (num_keys != kNotPresent) { 106 key.insert(key.end(), key1.begin(), key1.end()); 107 key.insert(key.end(), key2.begin(), key2.end()); 108 if (num_keys == kThree) { 109 key.insert(key.end(), key3.begin(), key3.end()); 110 } 111 } else { 112 key.insert(key.end(), keys.begin(), keys.end()); 113 key.insert(key.end(), keys.begin(), keys.end()); 114 key.insert(key.end(), keys.begin(), keys.end()); 115 } 116 117 if (!CipherOperation(ctx->cipher, &result, operation == kEncrypt, key, iv, 118 in)) { 119 return false; 120 } 121 122 // TDES fax files output format differs from file to file, and the input 123 // format is inconsistent with the output, so we construct the output manually 124 // rather than printing CurrentTestToString(). 125 if (t->IsAtNewInstructionBlock() && num_keys == kNotPresent) { 126 // If NumKeys is present, header is printed when parsing NumKeys. 127 std::string header = operation == kEncrypt ? "[ENCRYPT]" : "[DECRYPT]"; 128 printf("%s\r\n", header.c_str()); 129 } 130 const std::string result_label = 131 operation == kEncrypt ? "CIPHERTEXT" : "PLAINTEXT"; 132 printf("COUNT = %s\r\n", count.c_str()); 133 if (num_keys == kNotPresent) { 134 printf("KEYs = %s\r\n", EncodeHex(keys.data(), keys.size()).c_str()); 135 } else { 136 printf("KEY1 = %s\r\nKEY2 = %s\r\nKEY3 = %s\r\n", 137 EncodeHex(key1.data(), key1.size()).c_str(), 138 EncodeHex(key2.data(), key2.size()).c_str(), 139 EncodeHex(key3.data(), key3.size()).c_str()); 140 } 141 if (ctx->has_iv) { 142 printf("IV = %s\r\n", EncodeHex(iv.data(), iv.size()).c_str()); 143 } 144 printf("%s = %s\r\n", in_label.c_str(), 145 EncodeHex(in.data(), in.size()).c_str()); 146 printf("%s = %s\r\n\r\n", result_label.c_str(), 147 EncodeHex(result.data(), result.size()).c_str()); 148 149 return true; 150 } 151 152 // XORKeyWithOddParityLSB sets |*key| to |key| XOR |value| and then writes 153 // the LSB of each byte to establish odd parity for that byte. This parity-based 154 // embedded of a DES key into 64 bits is an old tradition and something that 155 // NIST's tests require. 156 static void XORKeyWithOddParityLSB(std::vector<uint8_t> *key, 157 const std::vector<uint8_t> &value) { 158 for (size_t i = 0; i < key->size(); i++) { 159 uint8_t v = (*key)[i] ^ value[i]; 160 161 // Use LSB to establish odd parity. 162 v |= 0x01; 163 for (uint8_t j = 1; j < 8; j++) { 164 v ^= ((v >> j) & 0x01); 165 } 166 (*key)[i] = v; 167 } 168 } 169 170 static bool TestMCT(FileTest *t, void *arg) { 171 TestCtx *ctx = reinterpret_cast<TestCtx *>(arg); 172 173 if (t->HasInstruction("ENCRYPT") == t->HasInstruction("DECRYPT")) { 174 t->PrintLine("Want either ENCRYPT or DECRYPT"); 175 return false; 176 } 177 enum { 178 kEncrypt, 179 kDecrypt, 180 } operation = t->HasInstruction("ENCRYPT") ? kEncrypt : kDecrypt; 181 182 if (t->HasAttribute("NumKeys")) { 183 // Another file format quirk: NumKeys is a single attribute line immediately 184 // following an instruction and should probably have been an instruction 185 // instead. 186 std::string num_keys; 187 t->GetAttribute(&num_keys, "NumKeys"); 188 t->InjectInstruction("NumKeys", num_keys); 189 return true; 190 } 191 192 enum { 193 kTwo, 194 kThree, 195 } num_keys; 196 std::string num_keys_str; 197 if (!t->GetInstruction(&num_keys_str, "NumKeys")) { 198 return false; 199 } else { 200 const int n = strtoul(num_keys_str.c_str(), nullptr, 0); 201 if (n == 2) { 202 num_keys = kTwo; 203 } else if (n == 3) { 204 num_keys = kThree; 205 } else { 206 t->PrintLine("invalid NumKeys value"); 207 return false; 208 } 209 } 210 211 std::string count; 212 std::vector<uint8_t> key1, key2, key3, iv, in, result; 213 const std::string in_label = 214 operation == kEncrypt ? "PLAINTEXT" : "CIPHERTEXT"; 215 // clang-format off 216 if (!t->GetBytes(&key1, "KEY1") || 217 !t->GetBytes(&key2, "KEY2") || 218 !t->GetBytes(&key3, "KEY3") || 219 (ctx->has_iv && !t->GetBytes(&iv, "IV")) || 220 !t->GetBytes(&in, in_label)) { 221 return false; 222 } 223 // clang-format on 224 225 for (int i = 0; i < 400; i++) { 226 std::vector<uint8_t> current_iv = iv, current_in = in, prev_result, 227 prev_prev_result; 228 229 std::vector<uint8_t> key(key1); 230 key.insert(key.end(), key2.begin(), key2.end()); 231 key.insert(key.end(), key3.begin(), key3.end()); 232 233 for (int j = 0; j < 10000; j++) { 234 prev_prev_result = prev_result; 235 prev_result = result; 236 const EVP_CIPHER *cipher = ctx->cipher; 237 if (!CipherOperation(cipher, &result, operation == kEncrypt, key, 238 current_iv, current_in)) { 239 t->PrintLine("CipherOperation failed"); 240 return false; 241 } 242 if (ctx->has_iv) { 243 if (operation == kEncrypt) { 244 if (j == 0) { 245 current_in = current_iv; 246 } else { 247 current_in = prev_result; 248 } 249 current_iv = result; 250 } else { // operation == kDecrypt 251 current_iv = current_in; 252 current_in = result; 253 } 254 } else { 255 current_in = result; 256 } 257 } 258 259 // Output result for COUNT = i. 260 const std::string result_label = 261 operation == kEncrypt ? "CIPHERTEXT" : "PLAINTEXT"; 262 if (i == 0) { 263 const std::string op_label = 264 operation == kEncrypt ? "ENCRYPT" : "DECRYPT"; 265 printf("[%s]\n\n", op_label.c_str()); 266 } 267 printf("COUNT = %d\r\nKEY1 = %s\r\nKEY2 = %s\r\nKEY3 = %s\r\n", i, 268 EncodeHex(key1.data(), key1.size()).c_str(), 269 EncodeHex(key2.data(), key2.size()).c_str(), 270 EncodeHex(key3.data(), key3.size()).c_str()); 271 if (ctx->has_iv) { 272 printf("IV = %s\r\n", EncodeHex(iv.data(), iv.size()).c_str()); 273 } 274 printf("%s = %s\r\n", in_label.c_str(), 275 EncodeHex(in.data(), in.size()).c_str()); 276 printf("%s = %s\r\n\r\n", result_label.c_str(), 277 EncodeHex(result.data(), result.size()).c_str()); 278 279 280 XORKeyWithOddParityLSB(&key1, result); 281 XORKeyWithOddParityLSB(&key2, prev_result); 282 if (num_keys == kThree) { 283 XORKeyWithOddParityLSB(&key3, prev_prev_result); 284 } else { 285 XORKeyWithOddParityLSB(&key3, result); 286 } 287 288 if (ctx->has_iv) { 289 if (operation == kEncrypt) { 290 in = prev_result; 291 iv = result; 292 } else { 293 iv = current_iv; 294 in = current_in; 295 } 296 } else { 297 in = result; 298 } 299 } 300 301 return true; 302 } 303 304 static int usage(char *arg) { 305 fprintf(stderr, "usage: %s (kat|mct) <cipher> <test file>\n", arg); 306 return 1; 307 } 308 309 int cavp_tdes_test_main(int argc, char **argv) { 310 if (argc != 4) { 311 return usage(argv[0]); 312 } 313 314 const std::string tm(argv[1]); 315 enum TestCtx::Mode test_mode; 316 if (tm == "kat") { 317 test_mode = TestCtx::kKAT; 318 } else if (tm == "mct") { 319 test_mode = TestCtx::kMCT; 320 } else { 321 fprintf(stderr, "invalid test_mode: %s\n", tm.c_str()); 322 return usage(argv[0]); 323 } 324 325 const std::string cipher_name(argv[2]); 326 const EVP_CIPHER *cipher = GetCipher(argv[2]); 327 if (cipher == nullptr) { 328 fprintf(stderr, "invalid cipher: %s\n", argv[2]); 329 return 1; 330 } 331 bool has_iv = cipher_name != "des-ede" && cipher_name != "des-ede3"; 332 TestCtx ctx = {cipher, has_iv, test_mode}; 333 334 FileTestFunc test_fn = test_mode == TestCtx::kKAT ? &TestKAT : &TestMCT; 335 FileTest::Options opts; 336 opts.path = argv[3]; 337 opts.callback = test_fn; 338 opts.arg = &ctx; 339 opts.silent = true; 340 opts.comment_callback = EchoComment; 341 return FileTestMain(opts); 342 } 343