• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TestKAT(FileTest * t,void * arg)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.
XORKeyWithOddParityLSB(std::vector<uint8_t> * key,const std::vector<uint8_t> & value)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 
TestMCT(FileTest * t,void * arg)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 
usage(char * arg)304 static int usage(char *arg) {
305   fprintf(stderr, "usage: %s (kat|mct) <cipher> <test file>\n", arg);
306   return 1;
307 }
308 
cavp_tdes_test_main(int argc,char ** argv)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