1 /* Copyright (c) 2015, 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 #include "file_test.h" 16 17 #include <algorithm> 18 #include <utility> 19 20 #include <assert.h> 21 #include <ctype.h> 22 #include <errno.h> 23 #include <stdarg.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 28 #include <openssl/err.h> 29 30 #include "../internal.h" 31 32 33 FileTest::FileTest(std::unique_ptr<FileTest::LineReader> reader, 34 std::function<void(const std::string &)> comment_callback, 35 bool is_kas_test) 36 : reader_(std::move(reader)), 37 is_kas_test_(is_kas_test), 38 comment_callback_(std::move(comment_callback)) {} 39 40 FileTest::~FileTest() {} 41 42 // FindDelimiter returns a pointer to the first '=' or ':' in |str| or nullptr 43 // if there is none. 44 static const char *FindDelimiter(const char *str) { 45 while (*str) { 46 if (*str == ':' || *str == '=') { 47 return str; 48 } 49 str++; 50 } 51 return nullptr; 52 } 53 54 // StripSpace returns a string containing up to |len| characters from |str| with 55 // leading and trailing whitespace removed. 56 static std::string StripSpace(const char *str, size_t len) { 57 // Remove leading space. 58 while (len > 0 && isspace(*str)) { 59 str++; 60 len--; 61 } 62 while (len > 0 && isspace(str[len - 1])) { 63 len--; 64 } 65 return std::string(str, len); 66 } 67 68 static std::pair<std::string, std::string> ParseKeyValue(const char *str, const size_t len) { 69 const char *delimiter = FindDelimiter(str); 70 std::string key, value; 71 if (delimiter == nullptr) { 72 key = StripSpace(str, len); 73 } else { 74 key = StripSpace(str, delimiter - str); 75 value = StripSpace(delimiter + 1, str + len - delimiter - 1); 76 } 77 return {key, value}; 78 } 79 80 FileTest::ReadResult FileTest::ReadNext() { 81 // If the previous test had unused attributes or instructions, it is an error. 82 if (!unused_attributes_.empty()) { 83 for (const std::string &key : unused_attributes_) { 84 PrintLine("Unused attribute: %s", key.c_str()); 85 } 86 return kReadError; 87 } 88 if (!unused_instructions_.empty()) { 89 for (const std::string &key : unused_instructions_) { 90 PrintLine("Unused instruction: %s", key.c_str()); 91 } 92 return kReadError; 93 } 94 95 ClearTest(); 96 97 static const size_t kBufLen = 8192 * 4; 98 std::unique_ptr<char[]> buf(new char[kBufLen]); 99 100 bool in_instruction_block = false; 101 is_at_new_instruction_block_ = false; 102 103 while (true) { 104 // Read the next line. 105 switch (reader_->ReadLine(buf.get(), kBufLen)) { 106 case kReadError: 107 fprintf(stderr, "Error reading from input at line %u.\n", line_ + 1); 108 return kReadError; 109 case kReadEOF: 110 // EOF is a valid terminator for a test. 111 return start_line_ > 0 ? kReadSuccess : kReadEOF; 112 case kReadSuccess: 113 break; 114 } 115 116 line_++; 117 size_t len = strlen(buf.get()); 118 if (buf[0] == '\n' || buf[0] == '\r' || buf[0] == '\0') { 119 // Empty lines delimit tests. 120 if (start_line_ > 0) { 121 return kReadSuccess; 122 } 123 if (in_instruction_block) { 124 in_instruction_block = false; 125 // Delimit instruction block from test with a blank line. 126 current_test_ += "\r\n"; 127 } else if (is_kas_test_) { 128 // KAS tests have random blank lines scattered around. 129 current_test_ += "\r\n"; 130 } 131 } else if (buf[0] == '#') { 132 if (is_kas_test_ && seen_non_comment_) { 133 // KAS tests have comments after the initial comment block which need 134 // to be included in the corresponding place in the output. 135 current_test_ += std::string(buf.get()); 136 } else if (comment_callback_) { 137 comment_callback_(buf.get()); 138 } 139 // Otherwise ignore comments. 140 } else if (strcmp("[B.4.2 Key Pair Generation by Testing Candidates]\r\n", 141 buf.get()) == 0) { 142 // The above instruction-like line is ignored because the FIPS lab's 143 // request files are hopelessly inconsistent. 144 } else if (buf[0] == '[') { // Inside an instruction block. 145 is_at_new_instruction_block_ = true; 146 seen_non_comment_ = true; 147 if (start_line_ != 0) { 148 // Instructions should be separate blocks. 149 fprintf(stderr, "Line %u is an instruction in a test case.\n", line_); 150 return kReadError; 151 } 152 if (!in_instruction_block) { 153 ClearInstructions(); 154 in_instruction_block = true; 155 } 156 157 // Parse the line as an instruction ("[key = value]" or "[key]"). 158 159 // KAS tests contain invalid syntax. 160 std::string kv = buf.get(); 161 const bool is_broken_kas_instruction = 162 is_kas_test_ && 163 (kv == "[SHA(s) supported (Used for hashing Z): SHA512 \r\n"); 164 165 if (!is_broken_kas_instruction) { 166 kv = StripSpace(buf.get(), len); 167 if (kv[kv.size() - 1] != ']') { 168 fprintf(stderr, "Line %u, invalid instruction: '%s'\n", line_, 169 kv.c_str()); 170 return kReadError; 171 } 172 } else { 173 // Just remove the newline for the broken instruction. 174 kv = kv.substr(0, kv.size() - 2); 175 } 176 177 current_test_ += kv + "\r\n"; 178 kv = std::string(kv.begin() + 1, kv.end() - 1); 179 180 for (;;) { 181 size_t idx = kv.find(","); 182 if (idx == std::string::npos) { 183 idx = kv.size(); 184 } 185 std::string key, value; 186 std::tie(key, value) = ParseKeyValue(kv.c_str(), idx); 187 instructions_[key] = value; 188 if (idx == kv.size()) 189 break; 190 kv = kv.substr(idx + 1); 191 } 192 } else { 193 // Parsing a test case. 194 if (in_instruction_block) { 195 // Some NIST CAVP test files (TDES) have a test case immediately 196 // following an instruction block, without a separate blank line, some 197 // of the time. 198 in_instruction_block = false; 199 } 200 201 current_test_ += std::string(buf.get(), len); 202 std::string key, value; 203 std::tie(key, value) = ParseKeyValue(buf.get(), len); 204 205 // Duplicate keys are rewritten to have /2, /3, suffixes. 206 std::string mapped_key = key; 207 for (unsigned i = 2; attributes_.count(mapped_key) != 0; i++) { 208 char suffix[32]; 209 snprintf(suffix, sizeof(suffix), "/%u", i); 210 suffix[sizeof(suffix)-1] = 0; 211 mapped_key = key + suffix; 212 } 213 214 unused_attributes_.insert(mapped_key); 215 attributes_[mapped_key] = value; 216 if (start_line_ == 0) { 217 // This is the start of a test. 218 type_ = mapped_key; 219 parameter_ = value; 220 start_line_ = line_; 221 for (const auto &kv : instructions_) { 222 unused_instructions_.insert(kv.first); 223 } 224 } 225 } 226 } 227 } 228 229 void FileTest::PrintLine(const char *format, ...) { 230 va_list args; 231 va_start(args, format); 232 233 fprintf(stderr, "Line %u: ", start_line_); 234 vfprintf(stderr, format, args); 235 fprintf(stderr, "\n"); 236 237 va_end(args); 238 } 239 240 const std::string &FileTest::GetType() { 241 OnKeyUsed(type_); 242 return type_; 243 } 244 245 const std::string &FileTest::GetParameter() { 246 OnKeyUsed(type_); 247 return parameter_; 248 } 249 250 bool FileTest::HasAttribute(const std::string &key) { 251 OnKeyUsed(key); 252 return attributes_.count(key) > 0; 253 } 254 255 bool FileTest::GetAttribute(std::string *out_value, const std::string &key) { 256 OnKeyUsed(key); 257 auto iter = attributes_.find(key); 258 if (iter == attributes_.end()) { 259 PrintLine("Missing attribute '%s'.", key.c_str()); 260 return false; 261 } 262 *out_value = iter->second; 263 return true; 264 } 265 266 const std::string &FileTest::GetAttributeOrDie(const std::string &key) { 267 if (!HasAttribute(key)) { 268 abort(); 269 } 270 return attributes_[key]; 271 } 272 273 bool FileTest::HasInstruction(const std::string &key) { 274 OnInstructionUsed(key); 275 return instructions_.count(key) > 0; 276 } 277 278 bool FileTest::GetInstruction(std::string *out_value, const std::string &key) { 279 OnInstructionUsed(key); 280 auto iter = instructions_.find(key); 281 if (iter == instructions_.end()) { 282 PrintLine("Missing instruction '%s'.", key.c_str()); 283 return false; 284 } 285 *out_value = iter->second; 286 return true; 287 } 288 289 const std::string &FileTest::GetInstructionOrDie(const std::string &key) { 290 if (!HasInstruction(key)) { 291 abort(); 292 } 293 return instructions_[key]; 294 } 295 296 bool FileTest::GetInstructionBytes(std::vector<uint8_t> *out, 297 const std::string &key) { 298 std::string value; 299 return GetInstruction(&value, key) && ConvertToBytes(out, value); 300 } 301 302 const std::string &FileTest::CurrentTestToString() const { 303 return current_test_; 304 } 305 306 bool FileTest::GetBytes(std::vector<uint8_t> *out, const std::string &key) { 307 std::string value; 308 return GetAttribute(&value, key) && ConvertToBytes(out, value); 309 } 310 311 static std::string EncodeHex(const uint8_t *in, size_t in_len) { 312 static const char kHexDigits[] = "0123456789abcdef"; 313 std::string ret; 314 ret.reserve(in_len * 2); 315 for (size_t i = 0; i < in_len; i++) { 316 ret += kHexDigits[in[i] >> 4]; 317 ret += kHexDigits[in[i] & 0xf]; 318 } 319 return ret; 320 } 321 322 bool FileTest::ExpectBytesEqual(const uint8_t *expected, size_t expected_len, 323 const uint8_t *actual, size_t actual_len) { 324 if (expected_len == actual_len && 325 OPENSSL_memcmp(expected, actual, expected_len) == 0) { 326 return true; 327 } 328 329 std::string expected_hex = EncodeHex(expected, expected_len); 330 std::string actual_hex = EncodeHex(actual, actual_len); 331 PrintLine("Expected: %s", expected_hex.c_str()); 332 PrintLine("Actual: %s", actual_hex.c_str()); 333 return false; 334 } 335 336 void FileTest::ClearTest() { 337 start_line_ = 0; 338 type_.clear(); 339 parameter_.clear(); 340 attributes_.clear(); 341 unused_attributes_.clear(); 342 unused_instructions_.clear(); 343 current_test_ = ""; 344 } 345 346 void FileTest::ClearInstructions() { 347 instructions_.clear(); 348 unused_attributes_.clear(); 349 } 350 351 void FileTest::OnKeyUsed(const std::string &key) { 352 unused_attributes_.erase(key); 353 } 354 355 void FileTest::OnInstructionUsed(const std::string &key) { 356 unused_instructions_.erase(key); 357 } 358 359 static bool FromHexDigit(uint8_t *out, char c) { 360 if ('0' <= c && c <= '9') { 361 *out = c - '0'; 362 return true; 363 } 364 if ('a' <= c && c <= 'f') { 365 *out = c - 'a' + 10; 366 return true; 367 } 368 if ('A' <= c && c <= 'F') { 369 *out = c - 'A' + 10; 370 return true; 371 } 372 return false; 373 } 374 375 bool FileTest::ConvertToBytes(std::vector<uint8_t> *out, 376 const std::string &value) { 377 if (value.size() >= 2 && value[0] == '"' && value[value.size() - 1] == '"') { 378 out->assign(value.begin() + 1, value.end() - 1); 379 return true; 380 } 381 382 if (value.size() % 2 != 0) { 383 PrintLine("Error decoding value: %s", value.c_str()); 384 return false; 385 } 386 out->clear(); 387 out->reserve(value.size() / 2); 388 for (size_t i = 0; i < value.size(); i += 2) { 389 uint8_t hi, lo; 390 if (!FromHexDigit(&hi, value[i]) || !FromHexDigit(&lo, value[i + 1])) { 391 PrintLine("Error decoding value: %s", value.c_str()); 392 return false; 393 } 394 out->push_back((hi << 4) | lo); 395 } 396 return true; 397 } 398 399 bool FileTest::IsAtNewInstructionBlock() const { 400 return is_at_new_instruction_block_; 401 } 402 403 void FileTest::InjectInstruction(const std::string &key, 404 const std::string &value) { 405 instructions_[key] = value; 406 } 407 408 class FileLineReader : public FileTest::LineReader { 409 public: 410 explicit FileLineReader(const char *path) : file_(fopen(path, "r")) {} 411 ~FileLineReader() override { 412 if (file_ != nullptr) { 413 fclose(file_); 414 } 415 } 416 417 // is_open returns true if the file was successfully opened. 418 bool is_open() const { return file_ != nullptr; } 419 420 FileTest::ReadResult ReadLine(char *out, size_t len) override { 421 assert(len > 0); 422 if (file_ == nullptr) { 423 return FileTest::kReadError; 424 } 425 426 if (fgets(out, len, file_) == nullptr) { 427 return feof(file_) ? FileTest::kReadEOF : FileTest::kReadError; 428 } 429 430 if (strlen(out) == len - 1 && out[len - 2] != '\n' && !feof(file_)) { 431 fprintf(stderr, "Line too long.\n"); 432 return FileTest::kReadError; 433 } 434 435 return FileTest::kReadSuccess; 436 } 437 438 private: 439 FILE *file_; 440 441 FileLineReader(const FileLineReader &) = delete; 442 FileLineReader &operator=(const FileLineReader &) = delete; 443 }; 444 445 int FileTestMain(FileTestFunc run_test, void *arg, const char *path) { 446 FileTest::Options opts; 447 opts.callback = run_test; 448 opts.arg = arg; 449 opts.path = path; 450 451 return FileTestMain(opts); 452 } 453 454 int FileTestMain(const FileTest::Options &opts) { 455 std::unique_ptr<FileLineReader> reader( 456 new FileLineReader(opts.path)); 457 if (!reader->is_open()) { 458 fprintf(stderr, "Could not open file %s: %s.\n", opts.path, 459 strerror(errno)); 460 return 1; 461 } 462 463 FileTest t(std::move(reader), opts.comment_callback, opts.is_kas_test); 464 465 bool failed = false; 466 while (true) { 467 FileTest::ReadResult ret = t.ReadNext(); 468 if (ret == FileTest::kReadError) { 469 return 1; 470 } else if (ret == FileTest::kReadEOF) { 471 break; 472 } 473 474 bool result = opts.callback(&t, opts.arg); 475 if (t.HasAttribute("Error")) { 476 if (result) { 477 t.PrintLine("Operation unexpectedly succeeded."); 478 failed = true; 479 continue; 480 } 481 uint32_t err = ERR_peek_error(); 482 if (ERR_reason_error_string(err) != t.GetAttributeOrDie("Error")) { 483 t.PrintLine("Unexpected error; wanted '%s', got '%s'.", 484 t.GetAttributeOrDie("Error").c_str(), 485 ERR_reason_error_string(err)); 486 failed = true; 487 ERR_clear_error(); 488 continue; 489 } 490 ERR_clear_error(); 491 } else if (!result) { 492 // In case the test itself doesn't print output, print something so the 493 // line number is reported. 494 t.PrintLine("Test failed"); 495 ERR_print_errors_fp(stderr); 496 failed = true; 497 continue; 498 } 499 } 500 501 if (!opts.silent && !failed) { 502 printf("PASS\n"); 503 } 504 505 return failed ? 1 : 0; 506 } 507 508 void FileTest::SkipCurrent() { 509 ClearTest(); 510 } 511