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