/*
* X448
* (C) 2024 Jack Lloyd
*     2024 Fabian Albert - Rohde & Schwarz Cybersecurity
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/x448.h>

#include <botan/ber_dec.h>
#include <botan/der_enc.h>
#include <botan/mem_ops.h>
#include <botan/rng.h>
#include <botan/internal/ct_utils.h>
#include <botan/internal/pk_ops_impl.h>
#include <botan/internal/x448_internal.h>

namespace Botan {

class X448_PublicKey_Data final {
   public:
      explicit X448_PublicKey_Data(std::array<uint8_t, X448_LEN> key) : m_key(key) {}

      const std::array<uint8_t, X448_LEN>& key() const { return m_key; }

   private:
      std::array<uint8_t, X448_LEN> m_key;
};

class X448_PrivateKey_Data final {
   public:
      explicit X448_PrivateKey_Data(secure_vector<uint8_t> key) : m_key(std::move(key)) {}

      const secure_vector<uint8_t>& key() const { return m_key; }

   private:
      secure_vector<uint8_t> m_key;
};

secure_vector<uint8_t> X448_PrivateKey::raw_private_key_bits() const {
   const auto& sk = m_private->key();
   return {sk.begin(), sk.end()};
}

namespace {
void x448_basepoint_from_data(std::span<uint8_t, X448_LEN> mypublic, std::span<const uint8_t, X448_LEN> secret) {
   auto bp = x448_basepoint(decode_scalar(secret));
   auto bp_bytes = encode_point(bp);
   copy_mem(mypublic, bp_bytes);
}

secure_vector<uint8_t> ber_decode_sk(std::span<const uint8_t> key_bits) {
   secure_vector<uint8_t> decoded_bits;
   BER_Decoder(key_bits, BER_Decoder::Limits::DER()).decode(decoded_bits, ASN1_Type::OctetString).verify_end();
   if(decoded_bits.size() != X448_LEN) {
      throw Decoding_Error("Invalid size for X448 private key");
   }
   return decoded_bits;
}

// Given a secret key compute the public value and build the immutable public
// and private key data objects.
void load_x448_keypair(secure_vector<uint8_t> secret,
                       std::shared_ptr<const X448_PublicKey_Data>& pk_out,
                       std::shared_ptr<const X448_PrivateKey_Data>& sk_out) {
   BOTAN_ASSERT_NOMSG(secret.size() == X448_LEN);
   std::array<uint8_t, X448_LEN> pub{};
   {
      auto scope = CT::scoped_poison(secret);
      x448_basepoint_from_data(pub, std::span(secret).first<X448_LEN>());
      CT::unpoison(pub);
   }
   pk_out = std::make_shared<const X448_PublicKey_Data>(pub);
   sk_out = std::make_shared<const X448_PrivateKey_Data>(std::move(secret));
}

}  // namespace

AlgorithmIdentifier X448_PublicKey::algorithm_identifier() const {
   return AlgorithmIdentifier(object_identifier(), AlgorithmIdentifier::USE_EMPTY_PARAM);
}

bool X448_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const {
   return true;  // no tests possible?
}

std::vector<uint8_t> X448_PublicKey::raw_public_key_bits() const {
   const auto& pub = m_public->key();
   return {pub.begin(), pub.end()};
}

std::vector<uint8_t> X448_PublicKey::public_key_bits() const {
   return raw_public_key_bits();
}

std::unique_ptr<Private_Key> X448_PublicKey::generate_another(RandomNumberGenerator& rng) const {
   return std::make_unique<X448_PrivateKey>(rng);
}

X448_PublicKey::X448_PublicKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits) :
      X448_PublicKey(key_bits) {
   // RFC 8410 Section 3: "the parameters MUST be absent".
   if(!alg_id.parameters_are_empty()) {
      throw Decoding_Error("Unexpected parameters for X448 public key");
   }
}

X448_PublicKey::X448_PublicKey(std::span<const uint8_t> pub) {
   BOTAN_ARG_CHECK(pub.size() == X448_LEN, "Invalid size for X448 public key");
   std::array<uint8_t, X448_LEN> pub_arr{};
   copy_mem(pub_arr, pub);
   m_public = std::make_shared<const X448_PublicKey_Data>(pub_arr);
}

X448_PrivateKey::X448_PrivateKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits) :
      X448_PrivateKey(ber_decode_sk(key_bits)) {
   // RFC 8410 Section 3: "the parameters MUST be absent".
   if(!alg_id.parameters_are_empty()) {
      throw Decoding_Error("Unexpected parameters for X448 private key");
   }
}

X448_PrivateKey::X448_PrivateKey(std::span<const uint8_t> secret_key) {
   BOTAN_ARG_CHECK(secret_key.size() == X448_LEN, "Invalid size for X448 private key");
   load_x448_keypair(secure_vector<uint8_t>(secret_key.begin(), secret_key.end()), m_public, m_private);
}

X448_PrivateKey::X448_PrivateKey(RandomNumberGenerator& rng) : X448_PrivateKey(rng.random_vec(X448_LEN)) {}

std::unique_ptr<Public_Key> X448_PrivateKey::public_key() const {
   return std::make_unique<X448_PublicKey>(public_value());
}

secure_vector<uint8_t> X448_PrivateKey::private_key_bits() const {
   return DER_Encoder().encode(m_private->key(), ASN1_Type::OctetString).get_contents();
}

bool X448_PrivateKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const {
   const auto& sk = m_private->key();
   const auto& pub = m_public->key();
   std::array<uint8_t, X448_LEN> public_point{};
   BOTAN_ASSERT_NOMSG(sk.size() == X448_LEN);
   auto scope = CT::scoped_poison(sk);
   x448_basepoint_from_data(public_point, std::span(sk).first<X448_LEN>());
   return CT::is_equal(public_point.data(), pub.data(), pub.size()).as_bool();
}

namespace {

/**
* X448 operation
*/
class X448_KA_Operation final : public PK_Ops::Key_Agreement_with_KDF {
   public:
      X448_KA_Operation(std::shared_ptr<const X448_PrivateKey_Data> key, std::string_view kdf) :
            PK_Ops::Key_Agreement_with_KDF(kdf), m_key(std::move(key)) {}

      size_t agreed_value_size() const override { return X448_LEN; }

      secure_vector<uint8_t> raw_agree(const uint8_t w_data[], size_t w_len) override {
         const auto& sk = m_key->key();
         BOTAN_ASSERT_NOMSG(sk.size() == X448_LEN);
         auto scope = CT::scoped_poison(sk);

         const std::span<const uint8_t> w(w_data, w_len);
         if(w.size() != X448_LEN) {
            throw Decoding_Error("Invalid size for X448 public key");
         }
         const auto k = decode_scalar(sk);
         const auto u = decode_point(w);

         auto shared_secret = encode_point(x448(k, u));
         CT::unpoison(shared_secret);

         // RFC 7748 Section 6.2
         //    As with X25519, both sides MAY check, without leaking extra
         //    information about the value of K, whether the resulting shared K
         //    is the all-zero value and abort if so.
         //
         // TODO: once the generic Key Agreement operation creation is equipped
         //       with a more flexible parameterization, this check could be
         //       made optional.
         //       For instance: `sk->agree().with_optional_sanity_checks(true)`.
         //       See also:     https://github.com/randombit/botan/pull/4318
         if(CT::all_zeros(shared_secret.data(), shared_secret.size()).as_bool()) {
            throw Invalid_Argument("X448 public point appears to be of low order");
         }

         return shared_secret;
      }

   private:
      std::shared_ptr<const X448_PrivateKey_Data> m_key;
};

}  // namespace

std::unique_ptr<PK_Ops::Key_Agreement> X448_PrivateKey::create_key_agreement_op(RandomNumberGenerator& /*rng*/,
                                                                                std::string_view params,
                                                                                std::string_view provider) const {
   if(provider == "base" || provider.empty()) {
      return std::make_unique<X448_KA_Operation>(m_private, params);
   }
   throw Provider_Not_Found(algo_name(), provider);
}

}  // namespace Botan
