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_aes_test processes a NIST CAVP AES test vector request file and emits
16 // the 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 bool has_iv;
34 enum Mode {
35 kKAT, // Known Answer Test
36 kMCT, // Monte Carlo Test
37 };
38 Mode mode;
39 };
40
41 }
42
MonteCarlo(const TestCtx * ctx,FileTest * t,const EVP_CIPHER * cipher,std::vector<uint8_t> * out,bool encrypt,std::vector<uint8_t> key,std::vector<uint8_t> iv,std::vector<uint8_t> in)43 static bool MonteCarlo(const TestCtx *ctx, FileTest *t,
44 const EVP_CIPHER *cipher, std::vector<uint8_t> *out,
45 bool encrypt, std::vector<uint8_t> key,
46 std::vector<uint8_t> iv, std::vector<uint8_t> in) {
47 const std::string in_label = encrypt ? "PLAINTEXT" : "CIPHERTEXT",
48 result_label = encrypt ? "CIPHERTEXT" : "PLAINTEXT";
49 std::vector<uint8_t> prev_result, result, prev_in;
50 for (int i = 0; i < 100; i++) {
51 printf("COUNT = %d\r\nKEY = %s\r\n", i, EncodeHex(key).c_str());
52 if (ctx->has_iv) {
53 printf("IV = %s\r\n", EncodeHex(iv).c_str());
54 }
55 printf("%s = %s\r\n", in_label.c_str(), EncodeHex(in).c_str());
56
57 if (!ctx->has_iv) { // ECB mode
58 for (int j = 0; j < 1000; j++) {
59 prev_result = result;
60 if (!CipherOperation(cipher, &result, encrypt, key, iv, in)) {
61 return false;
62 }
63 in = result;
64 }
65 } else {
66 for (int j = 0; j < 1000; j++) {
67 prev_result = result;
68 if (j > 0) {
69 if (encrypt) {
70 iv = result;
71 } else {
72 iv = prev_in;
73 }
74 }
75
76 if (!CipherOperation(cipher, &result, encrypt, key, iv, in)) {
77 return false;
78 }
79
80 prev_in = in;
81
82 if (j == 0) {
83 in = iv;
84 } else {
85 in = prev_result;
86 }
87 }
88 }
89
90 printf("%s = %s\r\n\r\n", result_label.c_str(), EncodeHex(result).c_str());
91
92 const size_t key_len = key.size() * 8;
93 if (key_len == 128) {
94 for (size_t k = 0; k < key.size(); k++) {
95 key[k] ^= result[k];
96 }
97 } else if (key_len == 192) {
98 for (size_t k = 0; k < key.size(); k++) {
99 // Key[i+1] = Key[i] xor (last 64-bits of CT[j-1] || CT[j])
100 if (k < 8) {
101 key[k] ^= prev_result[prev_result.size() - 8 + k];
102 } else {
103 key[k] ^= result[k - 8];
104 }
105 }
106 } else { // key_len == 256
107 for (size_t k = 0; k < key.size(); k++) {
108 // Key[i+1] = Key[i] xor (CT[j-1] || CT[j])
109 if (k < 16) {
110 key[k] ^= prev_result[k];
111 } else {
112 key[k] ^= result[k - 16];
113 }
114 }
115 }
116
117 if (ctx->has_iv) {
118 iv = result;
119 in = prev_result;
120 } else {
121 in = result;
122 }
123 }
124
125 return true;
126 }
127
TestCipher(FileTest * t,void * arg)128 static bool TestCipher(FileTest *t, void *arg) {
129 TestCtx *ctx = reinterpret_cast<TestCtx *>(arg);
130
131 if (t->HasInstruction("ENCRYPT") == t->HasInstruction("DECRYPT")) {
132 t->PrintLine("Want either ENCRYPT or DECRYPT");
133 return false;
134 }
135 enum {
136 kEncrypt,
137 kDecrypt,
138 } operation = t->HasInstruction("ENCRYPT") ? kEncrypt : kDecrypt;
139
140 std::string count;
141 std::vector<uint8_t> key, iv, in, result;
142 if (!t->GetAttribute(&count, "COUNT") ||
143 !t->GetBytes(&key, "KEY") ||
144 (ctx->has_iv && !t->GetBytes(&iv, "IV"))) {
145 return false;
146 }
147
148 const EVP_CIPHER *cipher = ctx->cipher;
149 if (operation == kEncrypt) {
150 if (!t->GetBytes(&in, "PLAINTEXT")) {
151 return false;
152 }
153 } else { // operation == kDecrypt
154 if (!t->GetBytes(&in, "CIPHERTEXT")) {
155 return false;
156 }
157 }
158
159 if (ctx->mode == TestCtx::kKAT) {
160 if (!CipherOperation(cipher, &result, operation == kEncrypt, key, iv, in)) {
161 return false;
162 }
163 const std::string label =
164 operation == kEncrypt ? "CIPHERTEXT" : "PLAINTEXT";
165 printf("%s%s = %s\r\n\r\n", t->CurrentTestToString().c_str(), label.c_str(),
166 EncodeHex(result).c_str());
167 } else { // ctx->mode == kMCT
168 const std::string op_label =
169 operation == kEncrypt ? "[ENCRYPT]" : "[DECRYPT]";
170 printf("%s\r\n\r\n", op_label.c_str());
171 if (!MonteCarlo(ctx, t, cipher, &result, operation == kEncrypt, key, iv,
172 in)) {
173 return false;
174 }
175 if (operation == kEncrypt) {
176 // MCT tests contain a stray blank line after the ENCRYPT section.
177 printf("\r\n");
178 }
179 }
180
181 return true;
182 }
183
usage(char * arg)184 static int usage(char *arg) {
185 fprintf(stderr, "usage: %s (kat|mct) <cipher> <test file>\n", arg);
186 return 1;
187 }
188
cavp_aes_test_main(int argc,char ** argv)189 int cavp_aes_test_main(int argc, char **argv) {
190 if (argc != 4) {
191 return usage(argv[0]);
192 }
193
194 const std::string tm(argv[1]);
195 enum TestCtx::Mode test_mode;
196 if (tm == "kat") {
197 test_mode = TestCtx::kKAT;
198 } else if (tm == "mct") {
199 test_mode = TestCtx::kMCT;
200 } else {
201 fprintf(stderr, "invalid test_mode: %s\n", tm.c_str());
202 return usage(argv[0]);
203 }
204
205 const std::string cipher_name(argv[2]);
206 const EVP_CIPHER *cipher = GetCipher(argv[2]);
207 if (cipher == nullptr) {
208 fprintf(stderr, "invalid cipher: %s\n", argv[2]);
209 return 1;
210 }
211 const bool has_iv =
212 (cipher_name != "aes-128-ecb" &&
213 cipher_name != "aes-192-ecb" &&
214 cipher_name != "aes-256-ecb");
215
216 TestCtx ctx = {cipher, has_iv, test_mode};
217
218 FileTest::Options opts;
219 opts.path = argv[3];
220 opts.callback = TestCipher;
221 opts.arg = &ctx;
222 opts.silent = true;
223 opts.comment_callback = EchoComment;
224 return FileTestMain(opts);
225 }
226