diff options
author | Jack Lloyd <[email protected]> | 2016-04-03 11:18:40 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2016-04-03 11:18:40 -0400 |
commit | 181f8052fecfa202066f885e0b1693a3f8180996 (patch) | |
tree | 15aee6e274689dfb53204127608bab5f7f6f749d | |
parent | 858cf5c82260e45e5bf51ff17b63f493d8295356 (diff) | |
parent | 4835852dec10d3882fd193ee686993571b92210b (diff) |
Merge pull request #458 from cordney/add-more-ffi-tests
Add more tests for ffi
-rw-r--r-- | src/tests/test_ffi.cpp | 290 | ||||
-rw-r--r-- | src/tests/tests.cpp | 14 | ||||
-rw-r--r-- | src/tests/tests.h | 1 |
3 files changed, 300 insertions, 5 deletions
diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp index 77beaf855..d055d093d 100644 --- a/src/tests/test_ffi.cpp +++ b/src/tests/test_ffi.cpp @@ -1,5 +1,6 @@ /* * (C) 2015 Jack Lloyd +* (C) 2016 René Korthaus * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -20,6 +21,7 @@ namespace { #define TEST_FFI_OK(func, args) result.test_rc_ok(#func, func args) #define TEST_FFI_FAIL(msg, func, args) result.test_rc_fail(#func, msg, func args) +#define TEST_FFI_RC(rc, func, args) result.test_rc(#func, rc, func args) #define REQUIRE_FFI_OK(func, args) \ if(!TEST_FFI_OK(func, args)) { \ @@ -38,6 +40,15 @@ class FFI_Unit_Tests : public Test result.test_is_eq("Major version", botan_version_major(), Botan::version_major()); result.test_is_eq("Minor version", botan_version_minor(), Botan::version_minor()); result.test_is_eq("Patch version", botan_version_patch(), Botan::version_patch()); + result.test_is_eq("Botan version", botan_version_string(), Botan::version_cstr()); + result.test_is_eq("Botan version datestamp", botan_version_datestamp(), Botan::version_datestamp()); + + const std::vector<uint8_t> mem1 = { 0xFF, 0xAA, 0xFF }; + const std::vector<uint8_t> mem2 = mem1; + const std::vector<uint8_t> mem3 = { 0xFF, 0xA9, 0xFF }; + + TEST_FFI_RC(0, botan_same_mem, (mem1.data(), mem2.data(), mem1.size())); + TEST_FFI_RC(-1, botan_same_mem, (mem1.data(), mem3.data(), mem1.size())); const std::vector<uint8_t> bin = { 0xAA, 0xDE, 0x01 }; const char* input_str = "ABC"; @@ -134,20 +145,27 @@ class FFI_Unit_Tests : public Test result.test_eq("MAC output length", output_len, 32); const byte mac_key[] = { 0xAA, 0xBB, 0xCC, 0xDD }; - TEST_FFI_OK(botan_mac_set_key, (mac, mac_key, sizeof(mac_key))); - outbuf.resize(output_len); - TEST_FFI_OK(botan_mac_update, (mac, reinterpret_cast<const uint8_t*>(input_str), std::strlen(input_str))); + // Test that after clear or final the object can be reused + for(size_t r = 0; r != 2; ++r) + { + TEST_FFI_OK(botan_mac_set_key, (mac, mac_key, sizeof(mac_key))); + TEST_FFI_OK(botan_mac_update, (mac, reinterpret_cast<const uint8_t*>(input_str), std::strlen(input_str))); + TEST_FFI_OK(botan_mac_clear, (mac)); - TEST_FFI_OK(botan_mac_final, (mac, outbuf.data())); + TEST_FFI_OK(botan_mac_set_key, (mac, mac_key, sizeof(mac_key))); + TEST_FFI_OK(botan_mac_update, (mac, reinterpret_cast<const uint8_t*>(input_str), std::strlen(input_str))); + TEST_FFI_OK(botan_mac_final, (mac, outbuf.data())); - result.test_eq("HMAC output", outbuf, "1A82EEA984BC4A7285617CC0D05F1FE1D6C96675924A81BC965EE8FF7B0697A7"); + result.test_eq("HMAC output", outbuf, "1A82EEA984BC4A7285617CC0D05F1FE1D6C96675924A81BC965EE8FF7B0697A7"); + } } TEST_FFI_OK(botan_mac_destroy, (mac)); } + // KDF test const std::vector<uint8_t> pbkdf_salt = Botan::hex_decode("ED1F39A0A7F3889AAF7E60743B3BC1CC2C738E60"); const std::string passphrase = "ltexmfeyylmlbrsyikaw"; const size_t pbkdf_out_len = 10; @@ -206,10 +224,162 @@ class FFI_Unit_Tests : public Test TEST_FFI_FAIL("bad password", botan_bcrypt_is_valid, ("nope", outstr.data())); } + // TODO: Cipher test + /* + botan_cipher_t cipher_encrypt, cipher_decrypt; + + if(TEST_FFI_OK(botan_cipher_init, (&cipher_encrypt, "AES-128/CBC", BOTAN_CIPHER_INIT_FLAG_ENCRYPT))) + { + size_t min_keylen = 0; + size_t max_keylen = 0; + TEST_FFI_OK(botan_cipher_query_keylen, (cipher_encrypt, &min_keylen, &max_keylen)); + result.test_int_eq(min_keylen, 16, "Min key length"); + result.test_int_eq(max_keylen, 16, "Max key length"); + + std::vector<uint8_t> plaintext(256); + std::vector<uint8_t> nonce(16); + TEST_FFI_OK(botan_rng_get, (rng, plaintext.data(), plaintext.size())); + TEST_FFI_OK(botan_rng_get, (rng, nonce.data(), nonce.size())); + + std::vector<uint8_t> ciphertext(plaintext.size()); // TODO: no way to know this size from API + + const std::vector<uint8_t> symkey = { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 }; + + size_t output_written = 0; + size_t input_consumed = 0; + + // Test that after clear or final the object can be reused + for(size_t r = 0; r != 2; ++r) + { + TEST_FFI_OK(botan_cipher_start, (cipher_encrypt, nonce.data(), nonce.size())); + TEST_FFI_OK(botan_cipher_set_key, (cipher_encrypt, symkey.data(), symkey.size())); + TEST_FFI_OK(botan_cipher_update, (cipher_encrypt, 0, ciphertext.data(), ciphertext.size(), &output_written, + plaintext.data(), plaintext.size(), &input_consumed)); + TEST_FFI_OK(botan_cipher_clear, (cipher_encrypt)); + + TEST_FFI_OK(botan_cipher_start, (cipher_encrypt, nonce.data(), nonce.size())); + TEST_FFI_OK(botan_cipher_set_key, (cipher_encrypt, symkey.data(), symkey.size())); + TEST_FFI_OK(botan_cipher_update, (cipher_encrypt, BOTAN_CIPHER_UPDATE_FLAG_FINAL, ciphertext.data(), ciphertext.size(), &output_written, + plaintext.data(), plaintext.size(), &input_consumed)); + + if(TEST_FFI_OK(botan_cipher_init, (&cipher_decrypt, "AES-128/CBC", BOTAN_CIPHER_INIT_FLAG_DECRYPT))) + { + std::vector<uint8_t> decrypted(plaintext.size()); + + TEST_FFI_OK(botan_cipher_start, (cipher_decrypt, nonce.data(), nonce.size())); + TEST_FFI_OK(botan_cipher_set_key, (cipher_decrypt, symkey.data(), symkey.size())); + TEST_FFI_OK(botan_cipher_update, (cipher_decrypt, BOTAN_CIPHER_UPDATE_FLAG_FINAL, decrypted.data(), decrypted.size(), &output_written, + ciphertext.data(), ciphertext.size(), &input_consumed)); + + result.test_eq("AES plaintext", decrypted, plaintext); + + TEST_FFI_OK(botan_cipher_destroy, (cipher_decrypt)); + } + } + + TEST_FFI_OK(botan_cipher_destroy, (cipher_encrypt)); + } + */ + + // TODO: AEAD test + // TODO ONCE MORE WITH AES-GCM + // botan_cipher_set_associated_data + // botan_cipher_valid_nonce_length + // botan_cipher_get_default_nonce_length + // botan_cipher_get_tag_length + + // x509 cert test + botan_x509_cert_t cert; + if(TEST_FFI_OK(botan_x509_cert_load_file, (&cert, "src/tests/data/ecc/CSCA.CSCA.csca-germany.1.crt"))) + { + size_t date_len = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_time_starts, (cert, nullptr, &date_len)); + + std::string date(date_len-1, '0'); + TEST_FFI_OK(botan_x509_cert_get_time_starts, (cert, &date[0], &date_len)); + result.test_eq("cert valid from", date, "070719152718Z"); + + date_len = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_time_expires, (cert, nullptr, &date_len)); + + date.resize(date_len-1); + TEST_FFI_OK(botan_x509_cert_get_time_expires, (cert, &date[0], &date_len)); + result.test_eq("cert valid until", date, "280119151800Z"); + + size_t serial_len = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_serial_number, (cert, nullptr, &serial_len)); + + std::vector<uint8_t> serial(serial_len); + TEST_FFI_OK(botan_x509_cert_get_serial_number, (cert, serial.data(), &serial_len)); + result.test_eq("cert serial length", serial.size(), 1); + result.test_int_eq(serial[0], 1, "cert serial"); + + size_t fingerprint_len = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_fingerprint, (cert, "SHA-256", nullptr, &fingerprint_len)); + + std::vector<uint8_t> fingerprint(fingerprint_len); + TEST_FFI_OK(botan_x509_cert_get_fingerprint, (cert, "SHA-256", fingerprint.data(), &fingerprint_len)); + result.test_eq("cert fingerprint", reinterpret_cast<const char*>(fingerprint.data()), "3B:6C:99:1C:D6:5A:51:FC:EB:17:E3:AA:F6:3C:1A:DA:14:1F:82:41:30:6F:64:EE:FF:63:F3:1F:D6:07:14:9F"); + + size_t key_id_len = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_authority_key_id, (cert, nullptr, &key_id_len)); + + std::vector<uint8_t> key_id(key_id_len); + TEST_FFI_OK(botan_x509_cert_get_authority_key_id, (cert, key_id.data(), &key_id_len)); + result.test_eq("cert authority key id", Botan::hex_encode(key_id.data(), key_id.size(), true), "0096452DE588F966C4CCDF161DD1F3F5341B71E7"); + + key_id_len = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_subject_key_id, (cert, nullptr, &key_id_len)); + + key_id.resize(key_id_len); + TEST_FFI_OK(botan_x509_cert_get_subject_key_id, (cert, key_id.data(), &key_id_len)); + result.test_eq("cert subject key id", Botan::hex_encode(key_id.data(), key_id.size(), true), "0096452DE588F966C4CCDF161DD1F3F5341B71E7"); + + size_t pubkey_len = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_public_key_bits, (cert, nullptr, &pubkey_len)); + + std::vector<uint8_t> pubkey(pubkey_len); + TEST_FFI_OK(botan_x509_cert_get_public_key_bits, (cert, pubkey.data(), &pubkey_len)); + + botan_pubkey_t pub; + if(TEST_FFI_OK(botan_x509_cert_get_public_key, (cert, &pub))) + { + TEST_FFI_OK(botan_pubkey_destroy, (pub)); + } + + size_t dn_len = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_issuer_dn, (cert, "Name", 0, nullptr, &dn_len)); + + std::vector<uint8_t> dn(dn_len); + TEST_FFI_OK(botan_x509_cert_get_issuer_dn, (cert, "Name", 0, dn.data(), &dn_len)); + result.test_eq("issuer dn", reinterpret_cast<const char*>(dn.data()), "csca-germany"); + + dn_len = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_subject_dn, (cert, "Name", 0, nullptr, &dn_len)); + + dn.resize(dn_len); + TEST_FFI_OK(botan_x509_cert_get_subject_dn, (cert, "Name", 0, dn.data(), &dn_len)); + result.test_eq("subject dn", reinterpret_cast<const char*>(dn.data()), "csca-germany"); + + size_t printable_len = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_to_string, (cert, nullptr, &printable_len)); + + std::string printable(printable_len-1, '0'); + TEST_FFI_OK(botan_x509_cert_to_string, (cert, &printable[0], &printable_len)); + + TEST_FFI_RC(0, botan_x509_cert_allowed_usage, (cert, KEY_CERT_SIGN)); + TEST_FFI_RC(0, botan_x509_cert_allowed_usage, (cert, CRL_SIGN)); + TEST_FFI_RC(1, botan_x509_cert_allowed_usage, (cert, DIGITAL_SIGNATURE)); + + TEST_FFI_OK(botan_x509_cert_destroy, (cert)); + } + std::vector<Test::Result> results; results.push_back(ffi_test_rsa(rng)); results.push_back(ffi_test_ecdsa(rng)); results.push_back(ffi_test_ecdh(rng)); + results.push_back(ffi_test_mceliece(rng)); TEST_FFI_OK(botan_rng_destroy, (rng)); @@ -218,6 +388,59 @@ class FFI_Unit_Tests : public Test } private: + void ffi_test_pubkey_export(Test::Result& result, botan_pubkey_t pub, botan_privkey_t priv, botan_rng_t rng) + { + // export public key + size_t pubkey_len = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_pubkey_export, (pub, nullptr, &pubkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_DER)); + + std::vector<uint8_t> pubkey(pubkey_len); + TEST_FFI_OK(botan_pubkey_export, (pub, pubkey.data(), &pubkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_DER)); + + pubkey_len = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_pubkey_export, (pub, nullptr, &pubkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_PEM)); + + pubkey.resize(pubkey_len); + TEST_FFI_OK(botan_pubkey_export, (pub, pubkey.data(), &pubkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_PEM)); + + // export private key + size_t privkey_len = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_privkey_export, (priv, nullptr, &privkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_DER)); + + std::vector<uint8_t> privkey(privkey_len); + TEST_FFI_OK(botan_privkey_export, (priv, privkey.data(), &privkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_DER)); + + privkey_len = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_privkey_export, (priv, nullptr, &privkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_PEM)); + + privkey.resize(privkey_len); + TEST_FFI_OK(botan_privkey_export, (priv, privkey.data(), &privkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_PEM)); + + // export private key encrypted + privkey_len = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_privkey_export_encrypted, (priv, nullptr, &privkey_len, rng, "password", "", BOTAN_PRIVKEY_EXPORT_FLAG_DER)); + + privkey.resize(privkey_len); + TEST_FFI_OK(botan_privkey_export_encrypted, (priv, privkey.data(), &privkey_len, rng, "password", "", BOTAN_PRIVKEY_EXPORT_FLAG_DER)); + + privkey_len = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_privkey_export_encrypted, (priv, nullptr, &privkey_len, rng, "password", "", BOTAN_PRIVKEY_EXPORT_FLAG_PEM)); + + privkey.resize(privkey_len); + TEST_FFI_OK(botan_privkey_export_encrypted, (priv, privkey.data(), &privkey_len, rng, "password", "", BOTAN_PRIVKEY_EXPORT_FLAG_PEM)); + + // calculate fingerprint + size_t strength = 0; + TEST_FFI_OK(botan_pubkey_estimated_strength, (pub, &strength)); + result.test_gte("estimated strength", strength, 1); + + size_t fingerprint_len = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_pubkey_fingerprint, (pub, "SHA-512", nullptr, &fingerprint_len)); + + std::vector<uint8_t> fingerprint(fingerprint_len); + TEST_FFI_OK(botan_pubkey_fingerprint, (pub, "SHA-512", fingerprint.data(), &fingerprint_len)); + } + Test::Result ffi_test_rsa(botan_rng_t rng) { Test::Result result("FFI"); @@ -228,6 +451,8 @@ class FFI_Unit_Tests : public Test botan_pubkey_t pub; TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv)); + ffi_test_pubkey_export(result, pub, priv, rng); + char namebuf[32] = { 0 }; size_t name_len = sizeof(namebuf); if(TEST_FFI_OK(botan_pubkey_algo_name, (pub, namebuf, &name_len))) @@ -287,6 +512,8 @@ class FFI_Unit_Tests : public Test botan_pubkey_t pub; TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv)); + ffi_test_pubkey_export(result, pub, priv, rng); + char namebuf[32] = { 0 }; size_t name_len = sizeof(namebuf); TEST_FFI_OK(botan_pubkey_algo_name, (pub, &namebuf[0], &name_len)); @@ -361,6 +588,9 @@ class FFI_Unit_Tests : public Test botan_pubkey_t pub2; REQUIRE_FFI_OK(botan_privkey_export_pubkey, (&pub2, priv2)); + ffi_test_pubkey_export(result, pub1, priv1, rng); + ffi_test_pubkey_export(result, pub2, priv2, rng); + botan_pk_op_ka_t ka1; REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka1, priv1, "KDF2(SHA-256)", 0)); botan_pk_op_ka_t ka2; @@ -404,6 +634,56 @@ class FFI_Unit_Tests : public Test return result; } + + Test::Result ffi_test_mceliece(botan_rng_t rng) + { + Test::Result result("FFI"); + + botan_privkey_t priv; + if (TEST_FFI_OK(botan_privkey_create_mceliece, (&priv, rng, 2048, 50))) + { + botan_pubkey_t pub; + TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv)); + + ffi_test_pubkey_export(result, pub, priv, rng); + + char namebuf[32] = { 0 }; + size_t name_len = sizeof(namebuf); + if (TEST_FFI_OK(botan_pubkey_algo_name, (pub, namebuf, &name_len))) + { + result.test_eq("algo name", std::string(namebuf), "McEliece"); + } + + size_t i = 0; + uint8_t ad[8]; + Botan::store_be(static_cast<Botan::u64bit>(i), ad); + const size_t ad_len = sizeof(ad); + + const Botan::secure_vector<byte> plaintext = Test::rng().random_vec(Test::rng().next_byte()); + size_t plaintext_len = plaintext.size(); + size_t ciphertext_len = 0; + + // first calculate ciphertext length + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_mceies_encrypt, (pub, rng, "AES-256/OCB", plaintext.data(), plaintext.size(), ad, ad_len, nullptr, &ciphertext_len)); + std::vector<uint8_t> ciphertext(ciphertext_len); + + // now encrypt + if (TEST_FFI_OK(botan_mceies_encrypt, (pub, rng, "AES-256/OCB", plaintext.data(), plaintext.size(), ad, ad_len, ciphertext.data(), &ciphertext_len))) + { + std::vector<uint8_t> decrypted(plaintext.size()); + size_t decrypted_len = plaintext_len; + + TEST_FFI_OK(botan_mceies_decrypt, (priv, "AES-256/OCB", ciphertext.data(), ciphertext.size(), ad, ad_len, decrypted.data(), &decrypted_len)); + + result.test_eq("MCIES plaintext", decrypted, plaintext); + } + + TEST_FFI_OK(botan_pubkey_destroy, (pub)); + TEST_FFI_OK(botan_privkey_destroy, (priv)); + } + + return result; + } }; BOTAN_REGISTER_TEST("ffi", FFI_Unit_Tests); diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp index 6d419310b..a79e05aba 100644 --- a/src/tests/tests.cpp +++ b/src/tests/tests.cpp @@ -282,6 +282,20 @@ bool Test::Result::test_rc_fail(const std::string& func, const std::string& why, return test_success(); } +bool Test::Result::test_rc(const std::string& func, int expected, int rc) + { + if(expected != rc) + { + std::ostringstream err; + err << m_who; + err << " call to " << func << " unexpectedly returned " << rc; + err << " but expecting " << expected; + return test_failure(err.str()); + } + + return test_success(); + } + namespace { std::string format_time(uint64_t ns) diff --git a/src/tests/tests.h b/src/tests/tests.h index 0bb65778a..c7bed64d9 100644 --- a/src/tests/tests.h +++ b/src/tests/tests.h @@ -186,6 +186,7 @@ class Test bool test_rc_ok(const std::string& func, int rc); bool test_rc_fail(const std::string& func, const std::string& why, int rc); + bool test_rc(const std::string& func, int expected, int rc); #if defined(BOTAN_HAS_BIGINT) bool test_eq(const std::string& what, const BigInt& produced, const BigInt& expected); |