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