|
- /* Copyright (c) 2017, Google Inc.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
-
- // cavp_aes_test processes a NIST CAVP AES test vector request file and emits
- // the corresponding response.
-
- #include <stdlib.h>
-
- #include <openssl/cipher.h>
- #include <openssl/crypto.h>
- #include <openssl/err.h>
-
- #include "../crypto/test/file_test.h"
- #include "cavp_test_util.h"
-
-
- namespace {
-
- struct TestCtx {
- const EVP_CIPHER *cipher;
- bool has_iv;
- enum Mode {
- kKAT, // Known Answer Test
- kMCT, // Monte Carlo Test
- };
- Mode mode;
- };
-
- }
-
- static bool 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) {
- const std::string in_label = encrypt ? "PLAINTEXT" : "CIPHERTEXT",
- result_label = encrypt ? "CIPHERTEXT" : "PLAINTEXT";
- std::vector<uint8_t> prev_result, result, prev_in;
- for (int i = 0; i < 100; i++) {
- printf("COUNT = %d\r\nKEY = %s\r\n", i,
- EncodeHex(key.data(), key.size()).c_str());
- if (ctx->has_iv) {
- printf("IV = %s\r\n", EncodeHex(iv.data(), iv.size()).c_str());
- }
- printf("%s = %s\r\n", in_label.c_str(),
- EncodeHex(in.data(), in.size()).c_str());
-
- if (!ctx->has_iv) { // ECB mode
- for (int j = 0; j < 1000; j++) {
- prev_result = result;
- if (!CipherOperation(cipher, &result, encrypt, key, iv, in)) {
- return false;
- }
- in = result;
- }
- } else {
- for (int j = 0; j < 1000; j++) {
- prev_result = result;
- if (j > 0) {
- if (encrypt) {
- iv = result;
- } else {
- iv = prev_in;
- }
- }
-
- if (!CipherOperation(cipher, &result, encrypt, key, iv, in)) {
- return false;
- }
-
- prev_in = in;
-
- if (j == 0) {
- in = iv;
- } else {
- in = prev_result;
- }
- }
- }
-
- printf("%s = %s\r\n\r\n", result_label.c_str(),
- EncodeHex(result.data(), result.size()).c_str());
-
- const size_t key_len = key.size() * 8;
- if (key_len == 128) {
- for (size_t k = 0; k < key.size(); k++) {
- key[k] ^= result[k];
- }
- } else if (key_len == 192) {
- for (size_t k = 0; k < key.size(); k++) {
- // Key[i+1] = Key[i] xor (last 64-bits of CT[j-1] || CT[j])
- if (k < 8) {
- key[k] ^= prev_result[prev_result.size() - 8 + k];
- } else {
- key[k] ^= result[k - 8];
- }
- }
- } else { // key_len == 256
- for (size_t k = 0; k < key.size(); k++) {
- // Key[i+1] = Key[i] xor (CT[j-1] || CT[j])
- if (k < 16) {
- key[k] ^= prev_result[k];
- } else {
- key[k] ^= result[k - 16];
- }
- }
- }
-
- if (ctx->has_iv) {
- iv = result;
- in = prev_result;
- } else {
- in = result;
- }
- }
-
- return true;
- }
-
- static bool TestCipher(FileTest *t, void *arg) {
- TestCtx *ctx = reinterpret_cast<TestCtx *>(arg);
-
- if (t->HasInstruction("ENCRYPT") == t->HasInstruction("DECRYPT")) {
- t->PrintLine("Want either ENCRYPT or DECRYPT");
- return false;
- }
- enum {
- kEncrypt,
- kDecrypt,
- } operation = t->HasInstruction("ENCRYPT") ? kEncrypt : kDecrypt;
-
- std::string count;
- std::vector<uint8_t> key, iv, in, result;
- if (!t->GetAttribute(&count, "COUNT") ||
- !t->GetBytes(&key, "KEY") ||
- (ctx->has_iv && !t->GetBytes(&iv, "IV"))) {
- return false;
- }
-
- const EVP_CIPHER *cipher = ctx->cipher;
- if (operation == kEncrypt) {
- if (!t->GetBytes(&in, "PLAINTEXT")) {
- return false;
- }
- } else { // operation == kDecrypt
- if (!t->GetBytes(&in, "CIPHERTEXT")) {
- return false;
- }
- }
-
- if (ctx->mode == TestCtx::kKAT) {
- if (!CipherOperation(cipher, &result, operation == kEncrypt, key, iv, in)) {
- return false;
- }
- const std::string label =
- operation == kEncrypt ? "CIPHERTEXT" : "PLAINTEXT";
- printf("%s%s = %s\r\n\r\n", t->CurrentTestToString().c_str(), label.c_str(),
- EncodeHex(result.data(), result.size()).c_str());
- } else { // ctx->mode == kMCT
- const std::string op_label =
- operation == kEncrypt ? "[ENCRYPT]" : "[DECRYPT]";
- printf("%s\r\n\r\n", op_label.c_str());
- if (!MonteCarlo(ctx, t, cipher, &result, operation == kEncrypt, key, iv,
- in)) {
- return false;
- }
- if (operation == kEncrypt) {
- // MCT tests contain a stray blank line after the ENCRYPT section.
- printf("\r\n");
- }
- }
-
- return true;
- }
-
- static int usage(char *arg) {
- fprintf(stderr, "usage: %s (kat|mct) <cipher> <test file>\n", arg);
- return 1;
- }
-
- int cavp_aes_test_main(int argc, char **argv) {
- if (argc != 4) {
- return usage(argv[0]);
- }
-
- const std::string tm(argv[1]);
- enum TestCtx::Mode test_mode;
- if (tm == "kat") {
- test_mode = TestCtx::kKAT;
- } else if (tm == "mct") {
- test_mode = TestCtx::kMCT;
- } else {
- fprintf(stderr, "invalid test_mode: %s\n", tm.c_str());
- return usage(argv[0]);
- }
-
- const std::string cipher_name(argv[2]);
- const EVP_CIPHER *cipher = GetCipher(argv[2]);
- if (cipher == nullptr) {
- fprintf(stderr, "invalid cipher: %s\n", argv[2]);
- return 1;
- }
- const bool has_iv =
- (cipher_name != "aes-128-ecb" &&
- cipher_name != "aes-192-ecb" &&
- cipher_name != "aes-256-ecb");
-
- TestCtx ctx = {cipher, has_iv, test_mode};
-
- FileTest::Options opts;
- opts.path = argv[3];
- opts.callback = TestCipher;
- opts.arg = &ctx;
- opts.silent = true;
- opts.comment_callback = EchoComment;
- return FileTestMain(opts);
- }
|