diff options
Diffstat (limited to 'src/tests/test_ecies.cpp')
-rw-r--r-- | src/tests/test_ecies.cpp | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/src/tests/test_ecies.cpp b/src/tests/test_ecies.cpp new file mode 100644 index 000000000..0ef7ca317 --- /dev/null +++ b/src/tests/test_ecies.cpp @@ -0,0 +1,253 @@ +/* +* (C) 2016 Philipp Weber +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" + + +#if defined(BOTAN_HAS_ECIES) + #include "test_pubkey.h" + #include <botan/ecies.h> + #include <botan/ecdh.h> + #include <botan/auto_rng.h> +#endif + +namespace Botan_Tests { + +namespace { + +#if defined(BOTAN_HAS_ECIES) + +using byte = Botan::byte; +using Flags = Botan::ECIES_Flags; + +Botan::PointGFp::Compression_Type get_compression_type(const std::string& format) + { + if(format == "uncompressed") + { + return Botan::PointGFp::UNCOMPRESSED; + } + else if(format == "compressed") + { + return Botan::PointGFp::COMPRESSED; + } + else if(format == "hybrid") + { + return Botan::PointGFp::HYBRID; + } + throw Botan::Invalid_Argument("invalid compression format"); + } + +Flags ecies_flags(bool cofactor_mode, bool old_cofactor_mode, bool check_mode, bool single_hash_mode) + { + return (cofactor_mode ? Flags::COFACTOR_MODE : Flags::NONE) + | (single_hash_mode ? Flags::SINGLE_HASH_MODE : Flags::NONE) + | (old_cofactor_mode ? Flags::OLD_COFACTOR_MODE : Flags::NONE) + | (check_mode ? Flags::CHECK_MODE : Flags::NONE); + } + +void check_encrypt_decrypt(Test::Result& result, const Botan::ECDH_PrivateKey& private_key, + const Botan::ECDH_PrivateKey& other_private_key, + const Botan::ECIES_System_Params& ecies_params, + const Botan::InitializationVector& iv, const std::string& label, + const std::vector<byte>& plaintext, const std::vector<byte>& ciphertext) + { + Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params); + ecies_enc.set_other_key(other_private_key.public_point()); + Botan::ECIES_Decryptor ecies_dec(other_private_key, ecies_params); + if(!iv.bits_of().empty()) + { + ecies_enc.set_initialization_vector(iv); + ecies_dec.set_initialization_vector(iv); + } + if(!label.empty()) + { + ecies_enc.set_label(label); + ecies_dec.set_label(label); + } + + try + { + const std::vector<byte> encrypted = ecies_enc.encrypt(plaintext, Test::rng()); + if(!ciphertext.empty()) + { + result.test_eq("encrypted data", encrypted, ciphertext); + } + const Botan::secure_vector<byte> decrypted = ecies_dec.decrypt(encrypted); + result.test_eq("decrypted data equals plaintext", decrypted, plaintext); + + std::vector<byte> invalid_encrypted = encrypted; + byte& last_byte = invalid_encrypted[invalid_encrypted.size() - 1]; + last_byte = ~last_byte; + result.test_throws("throw on invalid ciphertext", [&ecies_dec, &invalid_encrypted] + { + ecies_dec.decrypt(invalid_encrypted); + }); + } + catch(Botan::Lookup_Error& e) + { + result.test_note(std::string("Test not executed: ") + e.what()); + } + } + +void check_encrypt_decrypt(Test::Result& result, const Botan::ECDH_PrivateKey& private_key, + const Botan::ECDH_PrivateKey& other_private_key, + const Botan::ECIES_System_Params& ecies_params, size_t iv_length = 0) + { + const std::vector<byte> plaintext { 1, 2, 3 }; + check_encrypt_decrypt(result, private_key, other_private_key, ecies_params, std::vector<byte>(iv_length, 0), "", + plaintext, std::vector<byte>()); + } + +class ECIES_ISO_Tests : public Text_Based_Test + { + public: + ECIES_ISO_Tests() : Text_Based_Test( + "pubkey/ecies-18033.vec", + { "format", "p", "a", "b", "mu", "nu", "gx", "gy", "hx", "hy", "x", "r", "C0", "K" }) + { + } + + Test::Result run_one_test(const std::string&, const VarMap& vars) override + { + Test::Result result("ECIES-ISO"); + + // get test vectors defined by ISO 18033 + const Botan::PointGFp::Compression_Type compression_type = get_compression_type(get_req_str(vars, "format")); + const Botan::BigInt p = get_req_bn(vars, "p"); + const Botan::BigInt a = get_req_bn(vars, "a"); + const Botan::BigInt b = get_req_bn(vars, "b"); + const Botan::BigInt mu = get_req_bn(vars, "mu"); // order + const Botan::BigInt nu = get_req_bn(vars, "nu"); // cofactor + const Botan::BigInt gx = get_req_bn(vars, "gx"); // base point x + const Botan::BigInt gy = get_req_bn(vars, "gy"); // base point y + const Botan::BigInt hx = get_req_bn(vars, "hx"); // x of public point of bob + const Botan::BigInt hy = get_req_bn(vars, "hy"); // y of public point of bob + const Botan::BigInt x = get_req_bn(vars, "x"); // private key of bob + const Botan::BigInt r = get_req_bn(vars, "r"); // (ephemeral) private key of alice + const std::vector<byte> c0 = get_req_bin(vars, "C0"); // expected encoded (ephemeral) public key + const std::vector<byte> k = get_req_bin(vars, "K"); // expected derived secret + + const Botan::CurveGFp curve(p, a, b); + const Botan::EC_Group domain(curve, Botan::PointGFp(curve, gx, gy), mu, nu); + + // keys of bob + const Botan::ECDH_PrivateKey other_private_key(Test::rng(), domain, x); + const Botan::PointGFp other_public_key_point(curve, hx, hy); + const Botan::ECDH_PublicKey other_public_key(domain, other_public_key_point); + + // (ephemeral) keys of alice + const Botan::ECDH_PrivateKey eph_private_key(Test::rng(), domain, r); + const Botan::PointGFp eph_public_key_point = eph_private_key.public_point(); + const std::vector<byte> eph_public_key_bin = Botan::unlock( + Botan::EC2OSP(eph_public_key_point, compression_type)); + result.test_eq("encoded (ephemeral) public key", eph_public_key_bin, c0); + + // test secret derivation: ISO 18033 test vectors use KDF1 from ISO 18033 + // no cofactor-/oldcofactor-/singlehash-/check-mode and 128 byte secret length + Botan::ECIES_KA_Params ka_params(eph_private_key.domain(), "KDF1-18033(SHA-1)", 128, compression_type, Flags::NONE); + const Botan::ECIES_KA_Operation ka(eph_private_key, ka_params, true); + const Botan::SymmetricKey secret_key = ka.derive_secret(eph_public_key_bin, other_public_key_point); + result.test_eq("derived secret key", secret_key.bits_of(), k); + + // test encryption / decryption + for(int i_cofactor_mode = 0; i_cofactor_mode < 2; ++i_cofactor_mode) + { + for(int i_single_hash_mode = 0; i_single_hash_mode < 2; ++i_single_hash_mode) + { + for(int i_old_cofactor_mode = 0; i_old_cofactor_mode < 2; ++i_old_cofactor_mode) + { + for(int i_check_mode = 0; i_check_mode < 2; ++i_check_mode) + { + for(int i_compression_type = 0; i_compression_type < 3; ++i_compression_type) + { + const bool cofactor_mode = i_cofactor_mode != 0; + const bool single_hash_mode = i_single_hash_mode != 0; + const bool old_cofactor_mode = i_old_cofactor_mode != 0; + const bool check_mode = i_check_mode != 0; + const Botan::PointGFp::Compression_Type gen_compression_type = + static_cast<Botan::PointGFp::Compression_Type>(i_compression_type); + + Flags flags = ecies_flags(cofactor_mode, old_cofactor_mode, check_mode, single_hash_mode); + + if(cofactor_mode + check_mode + old_cofactor_mode > 1) + { + result.test_throws("throw on invalid ECIES_Flags", [&] + { + Botan::ECIES_System_Params(eph_private_key.domain(), "KDF2(SHA-1)", "AES-256/CBC", + 32, "HMAC(SHA-1)", 20, gen_compression_type, flags); + }); + continue; + } + + Botan::ECIES_System_Params ecies_params(eph_private_key.domain(), "KDF2(SHA-1)", "AES-256/CBC", + 32, "HMAC(SHA-1)", 20, gen_compression_type, flags); + check_encrypt_decrypt(result, eph_private_key, other_private_key, ecies_params, 16); + } + } + } + } + } + + return result; + } + }; + +BOTAN_REGISTER_TEST("ecies-iso", ECIES_ISO_Tests); + + +class ECIES_Tests : public Text_Based_Test + { + public: + ECIES_Tests() : Text_Based_Test( + "pubkey/ecies.vec", + { "Curve", "PrivateKey", "OtherPrivateKey", "Kdf", "Dem", "DemKeyLen", "Iv", "Mac", "MacKeyLen", "Format", + "CofactorMode", "OldCofactorMode", "CheckMode", "SingleHashMode", "Label", "Plaintext", "Ciphertext" }) + { + } + + Test::Result run_one_test(const std::string&, const VarMap& vars) override + { + Test::Result result("ECIES"); + + const std::string curve = get_req_str(vars, "Curve"); + const Botan::BigInt private_key_value = get_req_bn(vars, "PrivateKey"); + const Botan::BigInt other_private_key_value = get_req_bn(vars, "OtherPrivateKey"); + const std::string kdf = get_req_str(vars, "Kdf"); + const std::string dem = get_req_str(vars, "Dem"); + const size_t dem_key_len = get_req_sz(vars, "DemKeyLen"); + const std::vector<byte> iv = get_req_bin(vars, "Iv"); + const std::string mac = get_req_str(vars, "Mac"); + const size_t mac_key_len = get_req_sz(vars, "MacKeyLen"); + const Botan::PointGFp::Compression_Type compression_type = get_compression_type(get_req_str(vars, "Format")); + const bool cofactor_mode = get_req_sz(vars, "CofactorMode") != 0; + const bool old_cofactor_mode = get_req_sz(vars, "OldCofactorMode") != 0; + const bool check_mode = get_req_sz(vars, "CheckMode") != 0; + const bool single_hash_mode = get_req_sz(vars, "SingleHashMode") != 0; + const std::string label = get_req_str(vars, "Label"); + const std::vector<byte> plaintext = get_req_bin(vars, "Plaintext"); + const std::vector<byte> ciphertext = get_req_bin(vars, "Ciphertext"); + + const Flags flags = ecies_flags(cofactor_mode, old_cofactor_mode, check_mode, single_hash_mode); + const Botan::EC_Group domain(curve); + const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value); + const Botan::ECDH_PrivateKey other_private_key(Test::rng(), domain, other_private_key_value); + + const Botan::ECIES_System_Params ecies_params(private_key.domain(), kdf, dem, dem_key_len, mac, mac_key_len, + compression_type, flags); + check_encrypt_decrypt(result, private_key, other_private_key, ecies_params, iv, label, plaintext, ciphertext); + + return result; + } + + }; + +BOTAN_REGISTER_TEST("ecies", ECIES_Tests); + +#endif + +} + +} |