text: /*
  * (C) 2014 cryptosource GmbH
  * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
  * (C) 2014,2015 Jack Lloyd
  *
  * Botan is released under the Simplified BSD License (see license.txt)
  */
  
  #include "tests.h"
  
  #if defined(BOTAN_HAS_MCELIECE)
  
     #include <botan/mceliece.h>
     #include <botan/pubkey.h>
     #include <botan/loadstor.h>
     #include <botan/hash.h>
     #include <botan/hex.h>
  
     #if defined(BOTAN_HAS_HMAC_DRBG)
        #include <botan/hmac_drbg.h>
     #endif
  
     #if defined(BOTAN_HAS_MCEIES)
        #include <botan/mceies.h>
     #endif
  
  #endif
  
  namespace Botan_Tests {
  
  namespace {
  
  #if defined(BOTAN_HAS_MCELIECE)
  
  #if defined(BOTAN_HAS_HMAC_DRBG) && defined(BOTAN_HAS_SHA2_32) && defined(BOTAN_HAS_SHA2_64)
  class McEliece_Keygen_Encrypt_Test final : public Text_Based_Test
     {
     public:
        McEliece_Keygen_Encrypt_Test()
           : Text_Based_Test("pubkey/mce.vec",
                             "McElieceSeed,KeyN,KeyT,PublicKeyFingerprint,PrivateKeyFingerprint,"
                             "EncryptPRNGSeed,SharedKey,Ciphertext",
                             "") {}
  
        Test::Result run_one_test(const std::string&, const VarMap& vars) override
           {
           const std::vector<uint8_t> keygen_seed  = vars.get_req_bin("McElieceSeed");
           const std::vector<uint8_t> fprint_pub   = vars.get_req_bin("PublicKeyFingerprint");
           const std::vector<uint8_t> fprint_priv  = vars.get_req_bin("PrivateKeyFingerprint");
           const std::vector<uint8_t> encrypt_seed = vars.get_req_bin("EncryptPRNGSeed");
           const std::vector<uint8_t> ciphertext   = vars.get_req_bin("Ciphertext");
           const std::vector<uint8_t> shared_key   = vars.get_req_bin("SharedKey");
           const size_t keygen_n = vars.get_req_sz("KeyN");
           const size_t keygen_t = vars.get_req_sz("KeyT");
  
           Test::Result result("McEliece keygen");
           result.start_timer();
  
           if(Test::run_long_tests() == false && keygen_n > 3072)
              {
              result.test_note("Skipping because long");
              return result;
              }
  
           Botan::HMAC_DRBG rng("SHA-384");
           rng.initialize_with(keygen_seed.data(), keygen_seed.size());
           Botan::McEliece_PrivateKey mce_priv(rng, keygen_n, keygen_t);
  
           result.test_eq("public key fingerprint", hash_bytes(mce_priv.public_key_bits()), fprint_pub);
           result.test_eq("private key fingerprint", hash_bytes(mce_priv.private_key_bits()), fprint_priv);
  
           rng.clear();
           rng.initialize_with(encrypt_seed.data(), encrypt_seed.size());
  
           try
              {
              Botan::PK_KEM_Encryptor kem_enc(mce_priv, Test::rng(), "KDF1(SHA-512)");
              Botan::PK_KEM_Decryptor kem_dec(mce_priv, Test::rng(), "KDF1(SHA-512)");
  
              Botan::secure_vector<uint8_t> encap_key, prod_shared_key;
              kem_enc.encrypt(encap_key, prod_shared_key, 64, rng);
  
              Botan::secure_vector<uint8_t> dec_shared_key = kem_dec.decrypt(encap_key.data(), encap_key.size(), 64);
  
              result.test_eq("ciphertext", encap_key, ciphertext);
              result.test_eq("encrypt shared", prod_shared_key, shared_key);
              result.test_eq("decrypt shared", dec_shared_key, shared_key);
              }
           catch(Botan::Lookup_Error&)
              {
              }
  
           result.end_timer();
           return result;
           }
  
     private:
        std::vector<uint8_t> hash_bytes(const uint8_t b[], size_t len, const std::string& hash_fn = "SHA-256")
           {
           std::unique_ptr<Botan::HashFunction> hash(Botan::HashFunction::create(hash_fn));
           hash->update(b, len);
           std::vector<uint8_t> r(hash->output_length());
           hash->final(r.data());
           return r;
           }
  
        template<typename A>
        std::vector<uint8_t> hash_bytes(const std::vector<uint8_t, A>& v)
           {
           return hash_bytes(v.data(), v.size());
           }
  
     };
  
  BOTAN_REGISTER_TEST("pubkey", "mce_keygen", McEliece_Keygen_Encrypt_Test);
  #endif
  
  
  #if defined(BOTAN_HAS_SHA2_32)
  
  class McEliece_Tests final : public Test
     {
     public:
  
        std::string fingerprint(const Botan::Private_Key& key, const std::string& hash_algo = "SHA-256")
           {
           std::unique_ptr<Botan::HashFunction> hash(Botan::HashFunction::create(hash_algo));
           if(!hash)
              {
              throw Test_Error("Hash " + hash_algo + " not available");
              }
  
           hash->update(key.private_key_bits());
           return Botan::hex_encode(hash->final());
           }
  
        std::string fingerprint(const Botan::Public_Key& key, const std::string& hash_algo = "SHA-256")
           {
           std::unique_ptr<Botan::HashFunction> hash(Botan::HashFunction::create(hash_algo));
           if(!hash)
              {
              throw Test_Error("Hash " + hash_algo + " not available");
              }
  
           hash->update(key.public_key_bits());
           return Botan::hex_encode(hash->final());
           }
  
        std::vector<Test::Result> run() override
           {
           struct keygen_params { size_t code_length, t_min, t_max; };
  
           const keygen_params param_sets[] =
              {
                 { 256, 5, 15 },
                 { 512, 5, 33 },
                 { 1024, 15, 35 },
                 { 2048, 33, 50 },
                 { 6624, 110, 115 }
              };
  
           std::vector<Test::Result> results;
  
           for(size_t i = 0; i < sizeof(param_sets) / sizeof(param_sets[0]); ++i)
              {
              if(Test::run_long_tests() == false && param_sets[i].code_length >= 2048)
                 {
                 continue;
                 }
  
              for(size_t t = param_sets[i].t_min; t <= param_sets[i].t_max; ++t)
                 {
                 Test::Result result("McEliece keygen");
                 result.start_timer();
  
                 Botan::McEliece_PrivateKey sk1(Test::rng(), param_sets[i].code_length, t);
                 const Botan::McEliece_PublicKey& pk1 = sk1;
  
                 const std::vector<uint8_t> pk_enc = pk1.public_key_bits();
                 const Botan::secure_vector<uint8_t> sk_enc = sk1.private_key_bits();
  
                 Botan::McEliece_PublicKey pk(pk_enc);
                 Botan::McEliece_PrivateKey sk(sk_enc);
  
                 result.test_eq("decoded public key equals original", fingerprint(pk1), fingerprint(pk));
                 result.test_eq("decoded private key equals original", fingerprint(sk1), fingerprint(sk));
                 result.test_eq("key validation passes", sk.check_key(Test::rng(), false), true);
                 result.end_timer();
  
                 result.end_timer();
  
                 results.push_back(result);
  
  #if defined(BOTAN_HAS_KDF2)
                 results.push_back(test_kem(sk, pk));
  #endif
  
  #if defined(BOTAN_HAS_MCEIES)
                 results.push_back(test_mceies(sk, pk));
  #endif
                 }
              }
  
           return results;
           }
  
     private:
        Test::Result test_kem(const Botan::McEliece_PrivateKey& sk,
                              const Botan::McEliece_PublicKey& pk)
           {
           Test::Result result("McEliece KEM");
           result.start_timer();
  
           Botan::PK_KEM_Encryptor enc_op(pk, Test::rng(), "KDF2(SHA-256)");
           Botan::PK_KEM_Decryptor dec_op(sk, Test::rng(), "KDF2(SHA-256)");
  
           const size_t trials = (Test::run_long_tests() ? 30 : 10);
           for(size_t i = 0; i < trials; i++)
              {
              Botan::secure_vector<uint8_t> salt = Test::rng().random_vec(i);
  
              Botan::secure_vector<uint8_t> encap_key, shared_key;
              enc_op.encrypt(encap_key, shared_key, 64, Test::rng(), salt);
  
              Botan::secure_vector<uint8_t> shared_key2 = dec_op.decrypt(encap_key, 64, salt);
  
              result.test_eq("same key", shared_key, shared_key2);
              }
           result.end_timer();
           return result;
           }
  
  #if defined(BOTAN_HAS_MCEIES)
        Test::Result test_mceies(const Botan::McEliece_PrivateKey& sk,
                                 const Botan::McEliece_PublicKey& pk)
           {
           Test::Result result("McEliece IES");
           result.start_timer();
  
           for(size_t i = 0; i <= 10; ++i)
              {
              uint8_t ad[8];
              Botan::store_be(static_cast<uint64_t>(i), ad);
              const size_t ad_len = sizeof(ad);
  
              const Botan::secure_vector<uint8_t> pt = Test::rng().random_vec(Test::rng().next_byte());
  
              const Botan::secure_vector<uint8_t> ct = mceies_encrypt(pk, pt.data(), pt.size(), ad, ad_len, Test::rng());
              const Botan::secure_vector<uint8_t> dec = mceies_decrypt(sk, ct.data(), ct.size(), ad, ad_len);
  
              result.test_eq("decrypted ok", dec, pt);
  
              Botan::secure_vector<uint8_t> bad_ct = ct;
              for(size_t j = 0; j != 3; ++j)
                 {
                 bad_ct = mutate_vec(ct, true);
  
                 try
                    {
                    mceies_decrypt(sk, bad_ct.data(), bad_ct.size(), ad, ad_len);
                    result.test_failure("AEAD decrypted manipulated ciphertext");
                    result.test_note("Manipulated text was " + Botan::hex_encode(bad_ct));
                    }
                 catch(Botan::Integrity_Failure&)
                    {
                    result.test_note("AEAD rejected manipulated ciphertext");
                    }
                 catch(std::exception& e)
                    {
                    result.test_failure("AEAD rejected manipulated ciphertext with unexpected error", e.what());
                    }
                 }
              }
  
           result.end_timer();
           return result;
           }
  #endif
  
     };
  
  BOTAN_REGISTER_TEST("pubkey", "mceliece", McEliece_Tests);
  
  #endif
  
  #endif
  
  }
  
  }
  