/* * (C) 2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include "tests.h" #if defined(BOTAN_HAS_NEWHOPE) && defined(BOTAN_HAS_CHACHA) #include #include #include #include #endif namespace Botan_Tests { #if defined(BOTAN_HAS_NEWHOPE) && defined(BOTAN_HAS_CHACHA) class NEWHOPE_RNG final : public Botan::RandomNumberGenerator { public: std::string name() const override { return "NEWHOPE_RNG"; } void clear() override { /* ignored */ } void randomize(uint8_t out[], size_t len) override { if(m_first.size() == len) { if(len != 32) { throw Test_Error("NEWHOPE_RNG called in unexpected way, bad test?"); } Botan::copy_mem(out, m_first.data(), m_first.size()); return; } /* * This slavishly emulates the behavior of the reference * implementations RNG, in order to ensure that from the same * random seed we compute the exact same result. */ Botan::clear_mem(out, len); m_chacha->cipher1(out, len); m_calls += 1; uint8_t nonce[8] = { 0 }; if(m_calls < 3) { nonce[0] = m_calls; } else { nonce[7] = m_calls; } m_chacha->set_iv(nonce, 8); } bool is_seeded() const override { return true; } bool accepts_input() const override { return false; } void add_entropy(const uint8_t[], size_t) override { /* ignored */ } NEWHOPE_RNG(const std::vector& seed) { m_chacha = Botan::StreamCipher::create_or_throw("ChaCha20"); if(seed.size() != 64 && seed.size() != 32) { throw Test_Error("Invalid NEWHOPE RNG seed"); } if(seed.size() == 64) { m_first.assign(seed.begin(), seed.begin() + 32); m_chacha->set_key(seed.data() + 32, 32); } else { m_chacha->set_key(seed.data(), 32); } } private: std::unique_ptr m_chacha; std::vector m_first; uint8_t m_calls = 0; }; class NEWHOPE_Tests final : public Text_Based_Test { public: NEWHOPE_Tests() : Text_Based_Test( "pubkey/newhope.vec", "DRBG_SeedA,H_OutputA,DRBG_SeedB,H_OutputB,SharedKey") {} Test::Result run_one_test(const std::string&, const VarMap& vars) override { Test::Result result("NEWHOPE"); const std::vector h_output_a = vars.get_req_bin("H_OutputA"); const std::vector h_output_b = vars.get_req_bin("H_OutputB"); const std::vector shared_key = vars.get_req_bin("SharedKey"); NEWHOPE_RNG drbg_a(vars.get_req_bin("DRBG_SeedA")); NEWHOPE_RNG drbg_b(vars.get_req_bin("DRBG_SeedB")); std::unique_ptr sha3 = Botan::HashFunction::create("SHA-3(256)"); std::vector send_a(Botan::NEWHOPE_SENDABYTES); Botan::newhope_poly a_sk; Botan::newhope_keygen(send_a.data(), &a_sk, drbg_a); std::vector h_send_a(sha3->output_length()); sha3->update(send_a); sha3->final(h_send_a.data()); result.test_eq("Hash Output A", h_send_a, h_output_a); std::vector sharedkey_b(32); std::vector send_b(Botan::NEWHOPE_SENDBBYTES); Botan::newhope_sharedb(sharedkey_b.data(), send_b.data(), send_a.data(), drbg_b); result.test_eq("Key B", sharedkey_b, shared_key); std::vector h_send_b(sha3->output_length()); sha3->update(send_b); sha3->final(h_send_b.data()); result.test_eq("Hash Output B", h_send_b, h_output_b); std::vector sharedkey_a(32); newhope_shareda(sharedkey_a.data(), &a_sk, send_b.data()); result.test_eq("Key A", sharedkey_a, shared_key); return result; } }; BOTAN_REGISTER_TEST("newhope", NEWHOPE_Tests); #endif }