Home | History | Annotate | Download | only in fipstools
      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