Move crypto/fipsoracle/ to the top-level.
This works better with util/generate_build_files.py.
Change-Id: Icb55dc74e0a004aca3e09978640455b66f0473ff
Reviewed-on: https://boringssl-review.googlesource.com/15648
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/fipsoracle/CMakeLists.txt b/fipsoracle/CMakeLists.txt
new file mode 100644
index 0000000..04634ac
--- /dev/null
+++ b/fipsoracle/CMakeLists.txt
@@ -0,0 +1,62 @@
+include_directories(../include)
+
+if (FIPS)
+ add_executable(
+ cavp_aes_test
+
+ cavp_aes_test.cc
+ cavp_test_util.cc
+ $<TARGET_OBJECTS:test_support>
+ )
+
+ add_executable(
+ cavp_aes_gcm_test
+
+ cavp_aes_gcm_test.cc
+ cavp_test_util.cc
+ $<TARGET_OBJECTS:test_support>
+ )
+
+ add_executable(
+ cavp_ecdsa2_pkv_test
+
+ cavp_ecdsa2_pkv_test.cc
+ cavp_test_util.cc
+ $<TARGET_OBJECTS:test_support>
+ )
+
+ add_executable(
+ cavp_ecdsa2_sigver_test
+
+ cavp_ecdsa2_sigver_test.cc
+ cavp_test_util.cc
+ $<TARGET_OBJECTS:test_support>
+ )
+
+ add_executable(
+ cavp_sha_test
+
+ cavp_sha_test.cc
+ cavp_test_util.h
+ cavp_test_util.cc
+ $<TARGET_OBJECTS:test_support>
+ )
+
+ add_executable(
+ cavp_sha_monte_test
+
+ cavp_sha_monte_test.cc
+ cavp_test_util.h
+ cavp_test_util.cc
+ $<TARGET_OBJECTS:test_support>
+ )
+
+ target_link_libraries(cavp_aes_test crypto)
+ target_link_libraries(cavp_aes_gcm_test crypto)
+
+ target_link_libraries(cavp_ecdsa2_pkv_test crypto)
+ target_link_libraries(cavp_ecdsa2_sigver_test crypto)
+
+ target_link_libraries(cavp_sha_test crypto)
+ target_link_libraries(cavp_sha_monte_test crypto)
+endif()
diff --git a/fipsoracle/cavp_aes_gcm_test.cc b/fipsoracle/cavp_aes_gcm_test.cc
new file mode 100644
index 0000000..9871b73
--- /dev/null
+++ b/fipsoracle/cavp_aes_gcm_test.cc
@@ -0,0 +1,211 @@
+/* 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_gcm_test processes a NIST CAVP AES GCM test vector request file and
+// emits the corresponding response. An optional sample vector file can be
+// passed to verify the result.
+
+#include <stdlib.h>
+
+#include <openssl/aead.h>
+#include <openssl/cipher.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+
+#include "../crypto/test/file_test.h"
+#include "cavp_test_util.h"
+
+
+struct TestCtx {
+ const EVP_AEAD *aead;
+ std::unique_ptr<FileTest> response_sample;
+};
+
+static const EVP_AEAD *GetAEAD(const std::string &name, const bool enc) {
+ if (name == "aes-128-gcm") {
+ return EVP_aead_aes_128_gcm_fips_testonly();
+ } else if (name == "aes-256-gcm") {
+ return EVP_aead_aes_256_gcm_fips_testonly();
+ }
+ return nullptr;
+}
+
+static bool TestAEADEncrypt(FileTest *t, void *arg) {
+ TestCtx *ctx = reinterpret_cast<TestCtx *>(arg);
+
+ std::string key_len_str, iv_len_str, pt_len_str, aad_len_str, tag_len_str;
+ if (!t->GetInstruction(&key_len_str, "Keylen") ||
+ !t->GetInstruction(&iv_len_str, "IVlen") ||
+ !t->GetInstruction(&pt_len_str, "PTlen") ||
+ !t->GetInstruction(&aad_len_str, "AADlen") ||
+ !t->GetInstruction(&tag_len_str, "Taglen")) {
+ return false;
+ }
+
+ std::string count;
+ std::vector<uint8_t> key, iv, pt, aad, tag, ct;
+ if (!t->GetAttribute(&count, "Count") ||
+ !t->GetBytes(&key, "Key") ||
+ !t->GetBytes(&aad, "AAD") ||
+ !t->GetBytes(&pt, "PT") ||
+ key.size() * 8 != strtoul(key_len_str.c_str(), nullptr, 0) ||
+ pt.size() * 8 != strtoul(pt_len_str.c_str(), nullptr, 0) ||
+ aad.size() * 8 != strtoul(aad_len_str.c_str(), nullptr, 0)) {
+ return false;
+ }
+
+ size_t tag_len = strtoul(tag_len_str.c_str(), nullptr, 0) / 8;
+ if (!AEADEncrypt(ctx->aead, &ct, &tag, tag_len, key, pt, aad, &iv)) {
+ return false;
+ }
+ printf("%s", t->CurrentTestToString().c_str());
+ printf("IV = %s\r\n", EncodeHex(iv.data(), iv.size()).c_str());
+ printf("CT = %s\r\n", EncodeHex(ct.data(), ct.size()).c_str());
+ printf("Tag = %s\r\n\r\n", EncodeHex(tag.data(), tag.size()).c_str());
+
+ // Check if sample response file matches.
+ if (ctx->response_sample) {
+ ctx->response_sample->ReadNext();
+ std::string expected_count;
+ std::vector<uint8_t> expected_iv, expected_ct, expected_tag;
+ if (!ctx->response_sample->GetAttribute(&expected_count, "Count") ||
+ count != expected_count ||
+ !ctx->response_sample->GetBytes(&expected_iv, "IV") ||
+ !t->ExpectBytesEqual(expected_iv.data(), expected_iv.size(), iv.data(),
+ iv.size()) ||
+ !ctx->response_sample->GetBytes(&expected_ct, "CT") ||
+ !t->ExpectBytesEqual(expected_ct.data(), expected_ct.size(), ct.data(),
+ ct.size()) ||
+ !ctx->response_sample->GetBytes(&expected_tag, "Tag") ||
+ !t->ExpectBytesEqual(expected_tag.data(), expected_tag.size(),
+ tag.data(), tag.size())) {
+ t->PrintLine("result doesn't match");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool TestAEADDecrypt(FileTest *t, void *arg) {
+ TestCtx *ctx = reinterpret_cast<TestCtx *>(arg);
+
+ std::string key_len, iv_len, pt_len_str, aad_len_str, tag_len;
+ if (!t->GetInstruction(&key_len, "Keylen") ||
+ !t->GetInstruction(&iv_len, "IVlen") ||
+ !t->GetInstruction(&pt_len_str, "PTlen") ||
+ !t->GetInstruction(&aad_len_str, "AADlen") ||
+ !t->GetInstruction(&tag_len, "Taglen")) {
+ t->PrintLine("Invalid instruction block.");
+ return false;
+ }
+ size_t aad_len = strtoul(aad_len_str.c_str(), nullptr, 0) / 8;
+ size_t pt_len = strtoul(pt_len_str.c_str(), nullptr, 0) / 8;
+
+ std::string count;
+ std::vector<uint8_t> key, iv, ct, aad, tag, pt;
+ if (!t->GetAttribute(&count, "Count") ||
+ !t->GetBytes(&key, "Key") ||
+ !t->GetBytes(&aad, "AAD") ||
+ !t->GetBytes(&tag, "Tag") ||
+ !t->GetBytes(&iv, "IV") ||
+ !t->GetBytes(&ct, "CT") ||
+ key.size() * 8 != strtoul(key_len.c_str(), nullptr, 0) ||
+ iv.size() * 8 != strtoul(iv_len.c_str(), nullptr, 0) ||
+ ct.size() != pt_len ||
+ aad.size() != aad_len ||
+ tag.size() * 8 != strtoul(tag_len.c_str(), nullptr, 0)) {
+ t->PrintLine("Invalid test case");
+ return false;
+ }
+
+ printf("%s", t->CurrentTestToString().c_str());
+ bool aead_result =
+ AEADDecrypt(ctx->aead, &pt, &aad, pt_len, aad_len, key, ct, tag, iv);
+ if (aead_result) {
+ printf("PT = %s\r\n\r\n", EncodeHex(pt.data(), pt.size()).c_str());
+ } else {
+ printf("FAIL\r\n\r\n");
+ }
+
+ // Check if sample response file matches.
+ if (ctx->response_sample) {
+ ctx->response_sample->ReadNext();
+ std::string expected_count;
+ std::vector<uint8_t> expected_pt;
+ if (!ctx->response_sample->GetAttribute(&expected_count, "Count") ||
+ count != expected_count ||
+ (!aead_result && (ctx->response_sample->HasAttribute("PT") ||
+ !ctx->response_sample->HasAttribute("FAIL"))) ||
+ (aead_result &&
+ (ctx->response_sample->HasAttribute("FAIL") ||
+ !ctx->response_sample->GetBytes(&expected_pt, "PT") ||
+ !t->ExpectBytesEqual(expected_pt.data(), expected_pt.size(),
+ pt.data(), pt.size())))) {
+ t->PrintLine("result doesn't match");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static int usage(char *arg) {
+ fprintf(stderr,
+ "usage: %s (enc|dec) <cipher> <test file> [<sample response file>]\n",
+ arg);
+ return 1;
+}
+
+int main(int argc, char **argv) {
+ CRYPTO_library_init();
+
+ if (argc < 4 || argc > 5) {
+ return usage(argv[0]);
+ }
+
+ const std::string mode(argv[1]);
+ bool (*test_fn)(FileTest * t, void *arg);
+ if (mode == "enc") {
+ test_fn = &TestAEADEncrypt;
+ } else if (mode == "dec") {
+ test_fn = &TestAEADDecrypt;
+ } else {
+ return usage(argv[0]);
+ }
+
+ const EVP_AEAD *aead = GetAEAD(argv[2], mode == "enc");
+ if (aead == nullptr) {
+ fprintf(stderr, "invalid aead: %s\n", argv[2]);
+ return 1;
+ }
+
+ TestCtx ctx = {aead, nullptr};
+
+ if (argc == 5) {
+ ctx.response_sample.reset(new FileTest(argv[4]));
+ if (!ctx.response_sample->is_open()) {
+ return 1;
+ }
+ ctx.response_sample->SetIgnoreUnusedAttributes(true);
+ }
+
+ printf("# Generated by");
+ for (int i = 0; i < argc; i++) {
+ printf(" %s", argv[i]);
+ }
+ printf("\n\n");
+
+ return FileTestMainSilent(test_fn, &ctx, argv[3]);
+}
diff --git a/fipsoracle/cavp_aes_test.cc b/fipsoracle/cavp_aes_test.cc
new file mode 100644
index 0000000..c341e85
--- /dev/null
+++ b/fipsoracle/cavp_aes_test.cc
@@ -0,0 +1,280 @@
+/* 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. An optional sample vector file can be passed to
+// verify the result.
+
+#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"
+
+
+struct TestCtx {
+ const EVP_CIPHER *cipher;
+ std::unique_ptr<FileTest> response_sample;
+ 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());
+
+ // Check if sample response file matches.
+ if (ctx->response_sample) {
+ if (ctx->response_sample->ReadNext() != FileTest::kReadSuccess) {
+ t->PrintLine("invalid sample file");
+ return false;
+ }
+ std::string expected_count;
+ std::vector<uint8_t> expected_key, expected_result;
+ if (!ctx->response_sample->GetBytes(&expected_key, "KEY") ||
+ !t->ExpectBytesEqual(expected_key.data(), expected_key.size(),
+ key.data(), key.size()) ||
+ !ctx->response_sample->GetBytes(&expected_result, result_label) ||
+ !t->ExpectBytesEqual(expected_result.data(), expected_result.size(),
+ result.data(), result.size())) {
+ t->PrintLine("result doesn't match");
+ return false;
+ }
+ }
+
+ 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());
+
+ // Check if sample response file matches.
+ if (ctx->response_sample) {
+ if (ctx->response_sample->ReadNext() != FileTest::kReadSuccess) {
+ t->PrintLine("invalid sample file");
+ return false;
+ }
+ std::string expected_count;
+ std::vector<uint8_t> expected_result;
+ if (!ctx->response_sample->GetAttribute(&expected_count, "COUNT") ||
+ count != expected_count ||
+ (operation == kEncrypt &&
+ (!ctx->response_sample->GetBytes(&expected_result, "CIPHERTEXT") ||
+ !t->ExpectBytesEqual(expected_result.data(), expected_result.size(),
+ result.data(), result.size()))) ||
+ (operation == kDecrypt &&
+ (!ctx->response_sample->GetBytes(&expected_result, "PLAINTEXT") ||
+ !t->ExpectBytesEqual(expected_result.data(), expected_result.size(),
+ result.data(), result.size())))) {
+ t->PrintLine("result doesn't match");
+ return false;
+ }
+ }
+
+ } 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> [<sample response file>]\n",
+ arg);
+ return 1;
+}
+
+int main(int argc, char **argv) {
+ CRYPTO_library_init();
+
+ if (argc < 4 || argc > 5) {
+ 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, nullptr, has_iv, test_mode};
+
+ if (argc == 5) {
+ ctx.response_sample.reset(new FileTest(argv[4]));
+ if (!ctx.response_sample->is_open()) {
+ return 1;
+ }
+ ctx.response_sample->SetIgnoreUnusedAttributes(true);
+ }
+
+ printf("# Generated by");
+ for (int i = 0; i < argc; i++) {
+ printf(" %s", argv[i]);
+ }
+ printf("\r\n\r\n");
+
+ return FileTestMainSilent(TestCipher, &ctx, argv[3]);
+}
diff --git a/fipsoracle/cavp_ecdsa2_pkv_test.cc b/fipsoracle/cavp_ecdsa2_pkv_test.cc
new file mode 100644
index 0000000..74c9da1
--- /dev/null
+++ b/fipsoracle/cavp_ecdsa2_pkv_test.cc
@@ -0,0 +1,70 @@
+/* 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_ecdsa2_pkv_test processes a NIST CAVP ECDSA2 PKV test vector request file
+// and emits the corresponding response. An optional sample vector file can be
+// passed to verify the result.
+
+#include <vector>
+
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+#include <openssl/ec_key.h>
+#include <openssl/err.h>
+#include <openssl/nid.h>
+
+#include "../crypto/test/file_test.h"
+#include "cavp_test_util.h"
+
+
+static bool TestECDSA2PKV(FileTest *t, void *arg) {
+ int nid = GetECGroupNIDFromInstruction(t);
+ if (nid == NID_undef) {
+ return false;
+ }
+ bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(nid));
+ bssl::UniquePtr<BIGNUM> qx = GetBIGNUM(t, "Qx");
+ bssl::UniquePtr<BIGNUM> qy = GetBIGNUM(t, "Qy");
+ if (!key || !qx || !qy) {
+ return false;
+ }
+
+ if (EC_KEY_set_public_key_affine_coordinates(key.get(), qx.get(), qy.get())) {
+ printf("%sResult = P\r\n\r\n", t->CurrentTestToString().c_str());
+ } else {
+ char buf[256];
+ ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
+ printf("%sResult = F (%s)\r\n\r\n", t->CurrentTestToString().c_str(), buf);
+ }
+ ERR_clear_error();
+ return true;
+}
+
+int main(int argc, char **argv) {
+ CRYPTO_library_init();
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s <test file>\n",
+ argv[0]);
+ return 1;
+ }
+
+ printf("# Generated by");
+ for (int i = 0; i < argc; i++) {
+ printf(" %s", argv[i]);
+ }
+ printf("\r\n\r\n");
+
+ return FileTestMainSilent(TestECDSA2PKV, nullptr, argv[1]);
+}
diff --git a/fipsoracle/cavp_ecdsa2_sigver_test.cc b/fipsoracle/cavp_ecdsa2_sigver_test.cc
new file mode 100644
index 0000000..9401854
--- /dev/null
+++ b/fipsoracle/cavp_ecdsa2_sigver_test.cc
@@ -0,0 +1,88 @@
+/* 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_ecdsa2_sigver_test processes a NIST CAVP ECDSA2 SigVer test vector
+// request file and emits the corresponding response. An optional sample vector
+// file can be passed to verify the result.
+
+#include <vector>
+
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+#include <openssl/digest.h>
+#include <openssl/ec_key.h>
+#include <openssl/ecdsa.h>
+#include <openssl/err.h>
+#include <openssl/nid.h>
+
+#include "../crypto/test/file_test.h"
+#include "cavp_test_util.h"
+
+
+static bool TestECDSA2SigVer(FileTest *t, void *arg) {
+ int nid = GetECGroupNIDFromInstruction(t);
+ const EVP_MD *md = GetDigestFromInstruction(t);
+ if (nid == NID_undef || md == nullptr) {
+ return false;
+ }
+ bssl::UniquePtr<ECDSA_SIG> sig(ECDSA_SIG_new());
+ bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(nid));
+ bssl::UniquePtr<BIGNUM> qx = GetBIGNUM(t, "Qx");
+ bssl::UniquePtr<BIGNUM> qy = GetBIGNUM(t, "Qy");
+ bssl::UniquePtr<BIGNUM> r = GetBIGNUM(t, "R");
+ bssl::UniquePtr<BIGNUM> s = GetBIGNUM(t, "S");
+ std::vector<uint8_t> msg;
+ uint8_t digest[EVP_MAX_MD_SIZE];
+ unsigned digest_len;
+ if (!sig || !key || !qx || !qy || !r || !s ||
+ !EC_KEY_set_public_key_affine_coordinates(key.get(), qx.get(),
+ qy.get()) ||
+ !t->GetBytes(&msg, "Msg") ||
+ !EVP_Digest(msg.data(), msg.size(), digest, &digest_len, md, nullptr)) {
+ return false;
+ }
+
+ BN_free(sig->r);
+ sig->r = r.release();
+ BN_free(sig->s);
+ sig->s = s.release();
+
+ if (ECDSA_do_verify(digest, digest_len, sig.get(), key.get())) {
+ printf("%sResult = P\r\n\r\n", t->CurrentTestToString().c_str());
+ } else {
+ char buf[256];
+ ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
+ printf("%sResult = F (%s)\r\n\r\n", t->CurrentTestToString().c_str(), buf);
+ }
+ ERR_clear_error();
+ return true;
+}
+
+int main(int argc, char **argv) {
+ CRYPTO_library_init();
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s <test file>\n",
+ argv[0]);
+ return 1;
+ }
+
+ printf("# Generated by");
+ for (int i = 0; i < argc; i++) {
+ printf(" %s", argv[i]);
+ }
+ printf("\r\n\r\n");
+
+ return FileTestMainSilent(TestECDSA2SigVer, nullptr, argv[1]);
+}
diff --git a/fipsoracle/cavp_sha_monte_test.cc b/fipsoracle/cavp_sha_monte_test.cc
new file mode 100644
index 0000000..47b885e
--- /dev/null
+++ b/fipsoracle/cavp_sha_monte_test.cc
@@ -0,0 +1,101 @@
+/* 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_sha_monte_test processes a NIST CAVP SHA-Monte test vector request file
+// and emits the corresponding response. An optional sample vector file can be
+// passed to verify the result.
+
+#include <stdlib.h>
+
+#include <openssl/crypto.h>
+#include <openssl/digest.h>
+
+#include "../crypto/test/file_test.h"
+#include "cavp_test_util.h"
+
+
+struct TestCtx {
+ std::string hash;
+};
+
+static bool TestSHAMonte(FileTest *t, void *arg) {
+ TestCtx *ctx = reinterpret_cast<TestCtx *>(arg);
+
+ const EVP_MD *md = EVP_get_digestbyname(ctx->hash.c_str());
+ if (md == nullptr) {
+ return false;
+ }
+ const size_t md_len = EVP_MD_size(md);
+
+ std::string out_len;
+ if (!t->GetInstruction(&out_len, "L") ||
+ md_len != strtoul(out_len.c_str(), nullptr, 0)) {
+ return false;
+ }
+
+ std::vector<uint8_t> seed;
+ if (!t->GetBytes(&seed, "Seed") ||
+ seed.size() != md_len) {
+ return false;
+ }
+
+ std::vector<uint8_t> out = seed;
+
+ printf("%s\r\n", t->CurrentTestToString().c_str());
+
+ for (int count = 0; count < 100; count++) {
+ std::vector<uint8_t> msg;
+ msg.insert(msg.end(), out.begin(), out.end());
+ msg.insert(msg.end(), out.begin(), out.end());
+ msg.insert(msg.end(), out.begin(), out.end());
+ for (int i = 0; i < 1000; i++) {
+ unsigned digest_len;
+ if (!EVP_Digest(msg.data(), msg.size(), out.data(), &digest_len, md,
+ nullptr) ||
+ digest_len != out.size()) {
+ return false;
+ }
+
+ msg.erase(msg.begin(), msg.begin() + out.size());
+ msg.insert(msg.end(), out.begin(), out.end());
+ }
+ printf("COUNT = %d\r\n", count);
+ printf("MD = %s\r\n\r\n", EncodeHex(out.data(), out.size()).c_str());
+ }
+
+ return true;
+}
+
+static int usage(char *arg) {
+ fprintf(stderr, "usage: %s <hash> <test file>\n", arg);
+ return 1;
+}
+
+int main(int argc, char **argv) {
+ CRYPTO_library_init();
+
+ if (argc != 3) {
+ return usage(argv[0]);
+ }
+
+ TestCtx ctx = {std::string(argv[1])};
+
+ printf("# Generated by");
+ for (int i = 0; i < argc; i++) {
+ printf(" %s", argv[i]);
+ }
+ printf("\r\n\r\n");
+
+ return FileTestMainSilent(TestSHAMonte, &ctx, argv[2]);
+}
diff --git a/fipsoracle/cavp_sha_test.cc b/fipsoracle/cavp_sha_test.cc
new file mode 100644
index 0000000..ca60bb0
--- /dev/null
+++ b/fipsoracle/cavp_sha_test.cc
@@ -0,0 +1,96 @@
+/* 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_sha_test processes a NIST CAVP SHA test vector request file and emits
+// the corresponding response. An optional sample vector file can be passed to
+// verify the result.
+
+#include <stdlib.h>
+
+#include <openssl/crypto.h>
+#include <openssl/digest.h>
+
+#include "../crypto/test/file_test.h"
+#include "cavp_test_util.h"
+
+
+struct TestCtx {
+ std::string hash;
+};
+
+static bool TestSHA(FileTest *t, void *arg) {
+ TestCtx *ctx = reinterpret_cast<TestCtx *>(arg);
+
+ const EVP_MD *md = EVP_get_digestbyname(ctx->hash.c_str());
+ if (md == nullptr) {
+ return false;
+ }
+ const size_t md_len = EVP_MD_size(md);
+
+ std::string out_len;
+ if (!t->GetInstruction(&out_len, "L") ||
+ md_len != strtoul(out_len.c_str(), nullptr, 0)) {
+ return false;
+ }
+
+ std::string msg_len_str;
+ std::vector<uint8_t> msg;
+ if (!t->GetAttribute(&msg_len_str, "Len") ||
+ !t->GetBytes(&msg, "Msg")) {
+ return false;
+ }
+
+ size_t msg_len = strtoul(msg_len_str.c_str(), nullptr, 0);
+ if (msg_len % 8 != 0 ||
+ msg_len / 8 > msg.size()) {
+ return false;
+ }
+ msg_len /= 8;
+
+ std::vector<uint8_t> out;
+ out.resize(md_len);
+ unsigned digest_len;
+ if (!EVP_Digest(msg.data(), msg_len, out.data(), &digest_len, md, nullptr) ||
+ digest_len != out.size()) {
+ return false;
+ }
+
+ printf("%s", t->CurrentTestToString().c_str());
+ printf("MD = %s\r\n\r\n", EncodeHex(out.data(), out.size()).c_str());
+
+ return true;
+}
+
+static int usage(char *arg) {
+ fprintf(stderr, "usage: %s <hash> <test file>\n", arg);
+ return 1;
+}
+
+int main(int argc, char **argv) {
+ CRYPTO_library_init();
+
+ if (argc != 3) {
+ return usage(argv[0]);
+ }
+
+ TestCtx ctx = {std::string(argv[1])};
+
+ printf("# Generated by");
+ for (int i = 0; i < argc; i++) {
+ printf(" %s", argv[i]);
+ }
+ printf("\r\n\r\n");
+
+ return FileTestMainSilent(TestSHA, &ctx, argv[2]);
+}
diff --git a/fipsoracle/cavp_test_util.cc b/fipsoracle/cavp_test_util.cc
new file mode 100644
index 0000000..c3d2cdf
--- /dev/null
+++ b/fipsoracle/cavp_test_util.cc
@@ -0,0 +1,219 @@
+/* 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. */
+
+#include "cavp_test_util.h"
+
+#include <openssl/bn.h>
+#include <openssl/digest.h>
+#include <openssl/ec.h>
+#include <openssl/nid.h>
+
+
+std::string EncodeHex(const uint8_t *in, size_t in_len) {
+ static const char kHexDigits[] = "0123456789abcdef";
+ std::string ret;
+ ret.reserve(in_len * 2);
+ for (size_t i = 0; i < in_len; i++) {
+ ret += kHexDigits[in[i] >> 4];
+ ret += kHexDigits[in[i] & 0xf];
+ }
+ return ret;
+}
+
+const EVP_CIPHER *GetCipher(const std::string &name) {
+ if (name == "des-cbc") {
+ return EVP_des_cbc();
+ } else if (name == "des-ecb") {
+ return EVP_des_ecb();
+ } else if (name == "des-ede") {
+ return EVP_des_ede();
+ } else if (name == "des-ede3") {
+ return EVP_des_ede3();
+ } else if (name == "des-ede-cbc") {
+ return EVP_des_ede_cbc();
+ } else if (name == "des-ede3-cbc") {
+ return EVP_des_ede3_cbc();
+ } else if (name == "rc4") {
+ return EVP_rc4();
+ } else if (name == "aes-128-ecb") {
+ return EVP_aes_128_ecb();
+ } else if (name == "aes-256-ecb") {
+ return EVP_aes_256_ecb();
+ } else if (name == "aes-128-cbc") {
+ return EVP_aes_128_cbc();
+ } else if (name == "aes-128-gcm") {
+ return EVP_aes_128_gcm();
+ } else if (name == "aes-128-ofb") {
+ return EVP_aes_128_ofb();
+ } else if (name == "aes-192-cbc") {
+ return EVP_aes_192_cbc();
+ } else if (name == "aes-192-ctr") {
+ return EVP_aes_192_ctr();
+ } else if (name == "aes-192-ecb") {
+ return EVP_aes_192_ecb();
+ } else if (name == "aes-256-cbc") {
+ return EVP_aes_256_cbc();
+ } else if (name == "aes-128-ctr") {
+ return EVP_aes_128_ctr();
+ } else if (name == "aes-256-ctr") {
+ return EVP_aes_256_ctr();
+ } else if (name == "aes-256-gcm") {
+ return EVP_aes_256_gcm();
+ } else if (name == "aes-256-ofb") {
+ return EVP_aes_256_ofb();
+ }
+ return nullptr;
+}
+
+bool CipherOperation(const EVP_CIPHER *cipher, std::vector<uint8_t> *out,
+ bool encrypt, const std::vector<uint8_t> &key,
+ const std::vector<uint8_t> &iv,
+ const std::vector<uint8_t> &in) {
+ bssl::ScopedEVP_CIPHER_CTX ctx;
+ if (!EVP_CipherInit_ex(ctx.get(), cipher, nullptr, nullptr, nullptr,
+ encrypt ? 1 : 0)) {
+ return false;
+ }
+ if (!iv.empty() && iv.size() != EVP_CIPHER_CTX_iv_length(ctx.get())) {
+ return false;
+ }
+
+ int result_len1 = 0, result_len2;
+ *out = std::vector<uint8_t>(in.size());
+ if (!EVP_CIPHER_CTX_set_key_length(ctx.get(), key.size()) ||
+ !EVP_CipherInit_ex(ctx.get(), nullptr, nullptr, key.data(), iv.data(),
+ -1) ||
+ !EVP_CIPHER_CTX_set_padding(ctx.get(), 0) ||
+ !EVP_CipherUpdate(ctx.get(), out->data(), &result_len1, in.data(),
+ in.size()) ||
+ !EVP_CipherFinal_ex(ctx.get(), out->data() + result_len1, &result_len2)) {
+ return false;
+ }
+ out->resize(result_len1 + result_len2);
+
+ return true;
+}
+
+bool AEADEncrypt(const EVP_AEAD *aead, std::vector<uint8_t> *ct,
+ std::vector<uint8_t> *tag, size_t tag_len,
+ const std::vector<uint8_t> &key,
+ const std::vector<uint8_t> &pt,
+ const std::vector<uint8_t> &aad, std::vector<uint8_t> *iv) {
+ bssl::ScopedEVP_AEAD_CTX ctx;
+ if (!EVP_AEAD_CTX_init_with_direction(ctx.get(), aead, key.data(), key.size(),
+ tag->size(), evp_aead_seal)) {
+ return false;
+ }
+
+ std::vector<uint8_t> out;
+ out.resize(pt.size() + EVP_AEAD_max_overhead(aead));
+ size_t out_len;
+ if (!EVP_AEAD_CTX_seal(ctx.get(), out.data(), &out_len, out.size(),
+ nullptr /* iv */, 0 /* iv_len */, pt.data(), pt.size(),
+ aad.data(), aad.size())) {
+ return false;
+ }
+
+ static const size_t iv_len = EVP_AEAD_nonce_length(aead);
+ iv->assign(out.begin(), out.begin() + iv_len);
+ ct->assign(out.begin() + iv_len, out.end() - tag_len);
+ tag->assign(out.end() - tag_len, out.end());
+
+ return true;
+}
+
+bool AEADDecrypt(const EVP_AEAD *aead, std::vector<uint8_t> *pt,
+ std::vector<uint8_t> *aad, size_t pt_len, size_t aad_len,
+ const std::vector<uint8_t> &key,
+ const std::vector<uint8_t> &ct,
+ const std::vector<uint8_t> &tag, std::vector<uint8_t> &iv) {
+ bssl::ScopedEVP_AEAD_CTX ctx;
+ if (!EVP_AEAD_CTX_init_with_direction(ctx.get(), aead, key.data(), key.size(),
+ tag.size(), evp_aead_open)) {
+ return false;
+ }
+ std::vector<uint8_t> in = iv;
+ in.reserve(in.size() + ct.size() + tag.size());
+ in.insert(in.end(), ct.begin(), ct.end());
+ in.insert(in.end(), tag.begin(), tag.end());
+
+ pt->resize(pt_len);
+ aad->resize(aad_len);
+ size_t out_pt_len;
+ if (!EVP_AEAD_CTX_open(ctx.get(), pt->data(), &out_pt_len, pt->size(),
+ nullptr /* iv */, 0 /* iv_len */, in.data(), in.size(),
+ aad->data(), aad->size()) ||
+ out_pt_len != pt_len) {
+ return false;
+ }
+ return true;
+}
+
+static int HexToBIGNUM(bssl::UniquePtr<BIGNUM> *out, const char *in) {
+ BIGNUM *raw = NULL;
+ int ret = BN_hex2bn(&raw, in);
+ out->reset(raw);
+ return ret;
+}
+
+bssl::UniquePtr<BIGNUM> GetBIGNUM(FileTest *t, const char *attribute) {
+ std::string hex;
+ if (!t->GetAttribute(&hex, attribute)) {
+ return nullptr;
+ }
+
+ bssl::UniquePtr<BIGNUM> ret;
+ if (HexToBIGNUM(&ret, hex.c_str()) != static_cast<int>(hex.size())) {
+ t->PrintLine("Could not decode '%s'.", hex.c_str());
+ return nullptr;
+ }
+ return ret;
+}
+
+int GetECGroupNIDFromInstruction(FileTest *t) {
+ if (t->HasInstruction("P-224")) {
+ return NID_secp224r1;
+ }
+ if (t->HasInstruction("P-256")) {
+ return NID_X9_62_prime256v1;
+ }
+ if (t->HasInstruction("P-384")) {
+ return NID_secp384r1;
+ }
+ if (t->HasInstruction("P-521")) {
+ return NID_secp521r1;
+ }
+ t->PrintLine("No supported group specified.");
+ return NID_undef;
+}
+
+const EVP_MD *GetDigestFromInstruction(FileTest *t) {
+ if (t->HasInstruction("SHA-1")) {
+ return EVP_sha1();
+ }
+ if (t->HasInstruction("SHA-224")) {
+ return EVP_sha224();
+ }
+ if (t->HasInstruction("SHA-256")) {
+ return EVP_sha256();
+ }
+ if (t->HasInstruction("SHA-384")) {
+ return EVP_sha384();
+ }
+ if (t->HasInstruction("SHA-512")) {
+ return EVP_sha512();
+ }
+ t->PrintLine("No supported digest function specified.");
+ return nullptr;
+}
diff --git a/fipsoracle/cavp_test_util.h b/fipsoracle/cavp_test_util.h
new file mode 100644
index 0000000..44bb496
--- /dev/null
+++ b/fipsoracle/cavp_test_util.h
@@ -0,0 +1,56 @@
+/* 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. */
+
+#ifndef OPENSSL_HEADER_CRYPTO_FIPSMODULE_CAVP_TEST_UTIL_H
+#define OPENSSL_HEADER_CRYPTO_FIPSMODULE_CAVP_TEST_UTIL_H
+
+#include <stdlib.h>
+#include <string>
+#include <vector>
+
+#include <openssl/aead.h>
+#include <openssl/cipher.h>
+
+#include "../crypto/test/file_test.h"
+
+
+std::string EncodeHex(const uint8_t *in, size_t in_len);
+
+const EVP_CIPHER *GetCipher(const std::string &name);
+
+bool CipherOperation(const EVP_CIPHER *cipher, std::vector<uint8_t> *out,
+ bool encrypt, const std::vector<uint8_t> &key,
+ const std::vector<uint8_t> &iv,
+ const std::vector<uint8_t> &in);
+
+bool AEADEncrypt(const EVP_AEAD *aead, std::vector<uint8_t> *ct,
+ std::vector<uint8_t> *tag, size_t tag_len,
+ const std::vector<uint8_t> &key,
+ const std::vector<uint8_t> &pt,
+ const std::vector<uint8_t> &aad, std::vector<uint8_t> *iv);
+
+bool AEADDecrypt(const EVP_AEAD *aead, std::vector<uint8_t> *pt,
+ std::vector<uint8_t> *aad, size_t pt_len, size_t aad_len,
+ const std::vector<uint8_t> &key,
+ const std::vector<uint8_t> &ct,
+ const std::vector<uint8_t> &tag, std::vector<uint8_t> &iv);
+
+bssl::UniquePtr<BIGNUM> GetBIGNUM(FileTest *t, const char *attribute);
+
+int GetECGroupNIDFromInstruction(FileTest *t);
+
+const EVP_MD *GetDigestFromInstruction(FileTest *t);
+
+
+#endif // OPENSSL_HEADER_CRYPTO_FIPSMODULE_CAVP_TEST_UTIL_H
diff --git a/fipsoracle/run_cavp.go b/fipsoracle/run_cavp.go
new file mode 100644
index 0000000..449f1c5
--- /dev/null
+++ b/fipsoracle/run_cavp.go
@@ -0,0 +1,285 @@
+// run_cavp.go processes CAVP input files and generates suitable response
+// files, optionally comparing the results against the provided FAX files.
+package main
+
+import (
+ "bufio"
+ "flag"
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+)
+
+var (
+ binaryDir = flag.String("bin-dir", "", "Directory containing fipsoracle binaries")
+ suiteDir = flag.String("suite-dir", "", "Base directory containing the CAVP test suite")
+)
+
+// test describes a single request file.
+type test struct {
+ // inFile is the base of the filename without an extension, i.e.
+ // “ECBMCT128”.
+ inFile string
+ // args are the arguments (not including the input filename) to the
+ // oracle binary.
+ args []string
+ // noFAX, if true, indicates that the output cannot be compared against
+ // the FAX file. (E.g. because the primitive is non-deterministic.)
+ noFAX bool
+}
+
+// testSuite describes a series of tests that are handled by a single oracle
+// binary.
+type testSuite struct {
+ // directory is the name of the directory in the CAVP input, i.e. “AES”.
+ directory string
+ // binary is the name of the binary that can process these tests.
+ binary string
+ tests []test
+}
+
+func (t *testSuite) getDirectory() string {
+ return filepath.Join(*suiteDir, t.directory)
+}
+
+var aesGCMTests = testSuite{
+ "AES_GCM",
+ "cavp_aes_gcm_test",
+ []test{
+ {"gcmDecrypt128", []string{"dec", "aes-128-gcm"}, false},
+ {"gcmDecrypt256", []string{"dec", "aes-256-gcm"}, false},
+ {"gcmEncryptIntIV128", []string{"enc", "aes-128-gcm"}, true},
+ {"gcmEncryptIntIV256", []string{"enc", "aes-256-gcm"}, true},
+ },
+}
+
+var aesTests = testSuite{
+ "AES",
+ "cavp_aes_test",
+ []test{
+ {"CBCGFSbox128", []string{"kat", "aes-128-cbc"}, false},
+ {"CBCGFSbox192", []string{"kat", "aes-192-cbc"}, false},
+ {"CBCGFSbox256", []string{"kat", "aes-256-cbc"}, false},
+ {"CBCKeySbox128", []string{"kat", "aes-128-cbc"}, false},
+ {"CBCKeySbox192", []string{"kat", "aes-192-cbc"}, false},
+ {"CBCKeySbox256", []string{"kat", "aes-256-cbc"}, false},
+ {"CBCMMT128", []string{"kat", "aes-128-cbc"}, false},
+ {"CBCMMT192", []string{"kat", "aes-192-cbc"}, false},
+ {"CBCMMT256", []string{"kat", "aes-256-cbc"}, false},
+ {"CBCVarKey128", []string{"kat", "aes-128-cbc"}, false},
+ {"CBCVarKey192", []string{"kat", "aes-192-cbc"}, false},
+ {"CBCVarKey256", []string{"kat", "aes-256-cbc"}, false},
+ {"CBCVarTxt128", []string{"kat", "aes-128-cbc"}, false},
+ {"CBCVarTxt192", []string{"kat", "aes-192-cbc"}, false},
+ {"CBCVarTxt256", []string{"kat", "aes-256-cbc"}, false},
+ {"ECBGFSbox128", []string{"kat", "aes-128-ecb"}, false},
+ {"ECBGFSbox192", []string{"kat", "aes-192-ecb"}, false},
+ {"ECBGFSbox256", []string{"kat", "aes-256-ecb"}, false},
+ {"ECBKeySbox128", []string{"kat", "aes-128-ecb"}, false},
+ {"ECBKeySbox192", []string{"kat", "aes-192-ecb"}, false},
+ {"ECBKeySbox256", []string{"kat", "aes-256-ecb"}, false},
+ {"ECBMMT128", []string{"kat", "aes-128-ecb"}, false},
+ {"ECBMMT192", []string{"kat", "aes-192-ecb"}, false},
+ {"ECBMMT256", []string{"kat", "aes-256-ecb"}, false},
+ {"ECBVarKey128", []string{"kat", "aes-128-ecb"}, false},
+ {"ECBVarKey192", []string{"kat", "aes-192-ecb"}, false},
+ {"ECBVarKey256", []string{"kat", "aes-256-ecb"}, false},
+ {"ECBVarTxt128", []string{"kat", "aes-128-ecb"}, false},
+ {"ECBVarTxt192", []string{"kat", "aes-192-ecb"}, false},
+ {"ECBVarTxt256", []string{"kat", "aes-256-ecb"}, false},
+ // AES Monte-Carlo tests
+ {"ECBMCT128", []string{"mct", "aes-128-ecb"}, false},
+ {"ECBMCT192", []string{"mct", "aes-192-ecb"}, false},
+ {"ECBMCT256", []string{"mct", "aes-256-ecb"}, false},
+ {"CBCMCT128", []string{"mct", "aes-128-cbc"}, false},
+ {"CBCMCT192", []string{"mct", "aes-192-cbc"}, false},
+ {"CBCMCT256", []string{"mct", "aes-256-cbc"}, false},
+ },
+}
+
+var ecdsa2PKVTests = testSuite{
+ "ECDSA2",
+ "cavp_ecdsa2_pkv_test",
+ []test{{"PKV", nil, false}},
+}
+
+var ecdsa2SigVerTests = testSuite{
+ "ECDSA2",
+ "cavp_ecdsa2_sigver_test",
+ []test{{"SigVer", nil, false}},
+}
+
+var shaTests = testSuite{
+ "SHA",
+ "cavp_sha_test",
+ []test{
+ {"SHA1LongMsg", []string{"SHA1"}, false},
+ {"SHA1ShortMsg", []string{"SHA1"}, false},
+ {"SHA224LongMsg", []string{"SHA224"}, false},
+ {"SHA224ShortMsg", []string{"SHA224"}, false},
+ {"SHA256LongMsg", []string{"SHA256"}, false},
+ {"SHA256ShortMsg", []string{"SHA256"}, false},
+ {"SHA384LongMsg", []string{"SHA384"}, false},
+ {"SHA384ShortMsg", []string{"SHA384"}, false},
+ {"SHA512LongMsg", []string{"SHA512"}, false},
+ {"SHA512ShortMsg", []string{"SHA512"}, false},
+ },
+}
+
+var shaMonteTests = testSuite{
+ "SHA",
+ "cavp_sha_monte_test",
+ []test{
+ {"SHA1Monte", []string{"SHA1"}, false},
+ {"SHA224Monte", []string{"SHA224"}, false},
+ {"SHA256Monte", []string{"SHA256"}, false},
+ {"SHA384Monte", []string{"SHA384"}, false},
+ {"SHA512Monte", []string{"SHA512"}, false},
+ },
+}
+
+var allTestSuites = []*testSuite{
+ &aesGCMTests,
+ &aesTests,
+ &ecdsa2PKVTests,
+ &ecdsa2SigVerTests,
+ &shaTests,
+ &shaMonteTests,
+}
+
+func main() {
+ flag.Parse()
+
+ if len(*binaryDir) == 0 {
+ fmt.Fprintf(os.Stderr, "Must give -bin-dir\n")
+ os.Exit(1)
+ }
+
+ for _, suite := range allTestSuites {
+ for _, test := range suite.tests {
+ if err := doTest(suite, test); err != nil {
+ fmt.Fprintf(os.Stderr, "%s\n", err)
+ os.Exit(2)
+ }
+
+ if !test.noFAX {
+ if err := compareFAX(suite, test); err != nil {
+ fmt.Fprintf(os.Stderr, "%s\n", err)
+ os.Exit(3)
+ }
+ }
+ }
+ }
+}
+
+func doTest(suite *testSuite, test test) error {
+ binary := filepath.Join(*binaryDir, suite.binary)
+
+ var args []string
+ args = append(args, test.args...)
+ args = append(args, filepath.Join(suite.getDirectory(), "req", test.inFile+".req"))
+
+ outPath := filepath.Join(suite.getDirectory(), "resp", test.inFile+".resp")
+ outFile, err := os.OpenFile(outPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
+ if err != nil {
+ return fmt.Errorf("cannot open output file for %q %q: %s", suite.getDirectory(), test.inFile, err)
+ }
+ defer outFile.Close()
+
+ cmd := exec.Command(binary, args...)
+ cmd.Stdout = outFile
+ cmd.Stderr = os.Stderr
+
+ if err := cmd.Run(); err != nil {
+ return fmt.Errorf("cannot run command for %q %q: %s", suite.getDirectory(), test.inFile, err)
+ }
+
+ return nil
+}
+
+func canonicalizeLine(in string) string {
+ if strings.HasPrefix(in, "Result = P (") {
+ return "Result = P"
+ }
+ if strings.HasPrefix(in, "Result = F (") {
+ return "Result = F"
+ }
+ return in
+}
+
+func compareFAX(suite *testSuite, test test) error {
+ respPath := filepath.Join(suite.getDirectory(), "resp", test.inFile+".resp")
+ respFile, err := os.Open(respPath)
+ if err != nil {
+ return fmt.Errorf("cannot read output of %q %q: %s", suite.getDirectory(), test.inFile, err)
+ }
+ defer respFile.Close()
+
+ faxPath := filepath.Join(suite.getDirectory(), "fax", test.inFile+".fax")
+ faxFile, err := os.Open(faxPath)
+ if err != nil {
+ return fmt.Errorf("cannot open fax file for %q %q: %s", suite.getDirectory(), test.inFile, err)
+ }
+ defer faxFile.Close()
+
+ respScanner := bufio.NewScanner(respFile)
+ faxScanner := bufio.NewScanner(faxFile)
+
+ lineNo := 0
+ inHeader := true
+
+ for respScanner.Scan() {
+ lineNo++
+ respLine := respScanner.Text()
+ var faxLine string
+
+ if inHeader && (len(respLine) == 0 || respLine[0] == '#') {
+ continue
+ }
+
+ for {
+ haveFaxLine := false
+
+ if inHeader {
+ for faxScanner.Scan() {
+ faxLine = faxScanner.Text()
+ if len(faxLine) != 0 && faxLine[0] != '#' {
+ haveFaxLine = true
+ break
+ }
+ }
+
+ inHeader = false
+ } else {
+ if faxScanner.Scan() {
+ faxLine = faxScanner.Text()
+ haveFaxLine = true
+ }
+ }
+
+ if !haveFaxLine {
+ return fmt.Errorf("resp file is longer than fax for %q %q", suite.getDirectory(), test.inFile)
+ }
+
+ if strings.HasPrefix(faxLine, " (Reason: ") {
+ continue
+ }
+
+ break
+ }
+
+ if canonicalizeLine(faxLine) == canonicalizeLine(respLine) {
+ continue
+ }
+
+ return fmt.Errorf("resp and fax differ at line %d for %q %q: %q vs %q", lineNo, suite.getDirectory(), test.inFile, respLine, faxLine)
+ }
+
+ if faxScanner.Scan() {
+ return fmt.Errorf("fax file is longer than resp for %q %q", suite.getDirectory(), test.inFile)
+ }
+
+ return nil
+}