/*
 * XMSS Public Key
 * An XMSS: Extended Hash-Based Signature public key.
 * The XMSS public key does not support the X509 standard. Instead the
 * raw format described in [1] is used.
 *
 * [1] XMSS: Extended Hash-Based Signatures,
 *     Request for Comments: 8391
 *     Release: May 2018.
 *     https://datatracker.ietf.org/doc/rfc8391/
 *
 * (C) 2016,2017 Matthias Gierlings
 *
 * Botan is released under the Simplified BSD License (see license.txt)
 **/

#include <botan/xmss.h>

#include <botan/ber_dec.h>
#include <botan/der_enc.h>
#include <botan/rng.h>
#include <botan/internal/buffer_slicer.h>
#include <botan/internal/concat_util.h>
#include <botan/internal/loadstor.h>
#include <botan/internal/xmss_verification_operation.h>

namespace Botan {

namespace {

XMSS_Parameters::xmss_algorithm_t deserialize_xmss_oid(std::span<const uint8_t> raw_key) {
   if(raw_key.size() < 4) {
      throw Decoding_Error("XMSS signature OID missing.");
   }

   // extract and convert algorithm id to enum type
   uint32_t raw_id = 0;
   for(size_t i = 0; i < 4; i++) {
      raw_id = ((raw_id << 8) | raw_key[i]);
   }

   return static_cast<XMSS_Parameters::xmss_algorithm_t>(raw_id);
}

// fall back to raw decoding for previous versions, which did not encode an OCTET STRING
std::vector<uint8_t> extract_raw_public_key(std::span<const uint8_t> key_bits) {
   std::vector<uint8_t> raw_key;
   try {
      BER_Decoder(key_bits, BER_Decoder::Limits::DER()).decode(raw_key, ASN1_Type::OctetString).verify_end();

      // Smoke check the decoded key. Valid raw keys might be decodable as BER
      // and they might be either a sole public key or a concatenation of public
      // and private key (with the optional WOTS+ derivation identifier).
      const XMSS_Parameters params = XMSS_Parameters::from_id(deserialize_xmss_oid(raw_key));
      if(raw_key.size() != params.raw_public_key_size() && raw_key.size() != params.raw_private_key_size() &&
         raw_key.size() != params.raw_legacy_private_key_size()) {
         throw Decoding_Error("unpacked XMSS key does not have the correct length");
      }
   } catch(Decoding_Error&) {
      raw_key.assign(key_bits.begin(), key_bits.end());
   } catch(Not_Implemented&) {
      raw_key.assign(key_bits.begin(), key_bits.end());
   }

   return raw_key;
}

}  // namespace

class XMSS_PublicKey_Internal final {
   public:
      XMSS_PublicKey_Internal(const XMSS_Parameters& params,
                              secure_vector<uint8_t> root,
                              secure_vector<uint8_t> public_seed) :
            m_xmss_params(params),
            m_wots_params(m_xmss_params.wots_parameters()),
            m_root(std::move(root)),
            m_public_seed(std::move(public_seed)) {}

      const XMSS_Parameters& xmss_parameters() const { return m_xmss_params; }

      const XMSS_WOTS_Parameters& wots_parameters() const { return m_wots_params; }

      const secure_vector<uint8_t>& root() const { return m_root; }

      const secure_vector<uint8_t>& public_seed() const { return m_public_seed; }

      std::vector<uint8_t> raw_public_key_bits() const {
         return concat<std::vector<uint8_t>>(
            store_be(static_cast<uint32_t>(m_xmss_params.oid())), m_root, m_public_seed);
      }

   private:
      XMSS_Parameters m_xmss_params;
      XMSS_WOTS_Parameters m_wots_params;
      secure_vector<uint8_t> m_root;
      secure_vector<uint8_t> m_public_seed;
};

XMSS_PublicKey::XMSS_PublicKey(XMSS_Parameters::xmss_algorithm_t xmss_oid, RandomNumberGenerator& rng) {
   const auto params = XMSS_Parameters::from_id(xmss_oid);
   m_public_key = std::make_shared<XMSS_PublicKey_Internal>(
      params, secure_vector<uint8_t>(params.element_size()), rng.random_vec(params.element_size()));
}

XMSS_PublicKey::XMSS_PublicKey(std::span<const uint8_t> key_bits) : XMSS_PublicKey(AlgorithmIdentifier(), key_bits) {}

XMSS_PublicKey::XMSS_PublicKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits) {
   // The XMSS parameter set is carried in the key bits; no AlgorithmIdentifier parameters are defined
   if(!alg_id.parameters_are_empty()) {
      throw Decoding_Error("Unexpected parameters for XMSS public key");
   }

   const auto raw_key = extract_raw_public_key(key_bits);
   const auto xmss_oid = deserialize_xmss_oid(raw_key);
   const auto params = XMSS_Parameters::from_id(xmss_oid);
   if(raw_key.size() < params.raw_public_key_size()) {
      throw Decoding_Error("Invalid XMSS public key size detected");
   }

   BufferSlicer s(raw_key);
   s.skip(4 /* algorithm ID -- already consumed by `deserialize_xmss_oid()` */);

   auto root = s.copy_as_secure_vector(params.element_size());
   auto public_seed = s.copy_as_secure_vector(params.element_size());

   m_public_key = std::make_shared<XMSS_PublicKey_Internal>(params, std::move(root), std::move(public_seed));
}

XMSS_PublicKey::XMSS_PublicKey(XMSS_Parameters::xmss_algorithm_t xmss_oid,
                               secure_vector<uint8_t> root,
                               secure_vector<uint8_t> public_seed) {
   const auto params = XMSS_Parameters::from_id(xmss_oid);
   BOTAN_ARG_CHECK(root.size() == params.element_size(), "XMSS: unexpected byte length of root hash");
   BOTAN_ARG_CHECK(public_seed.size() == params.element_size(), "XMSS: unexpected byte length of public seed");
   m_public_key = std::make_shared<XMSS_PublicKey_Internal>(params, std::move(root), std::move(public_seed));
}

const secure_vector<uint8_t>& XMSS_PublicKey::public_seed() const {
   return m_public_key->public_seed();
}

const secure_vector<uint8_t>& XMSS_PublicKey::root() const {
   return m_public_key->root();
}

const XMSS_Parameters& XMSS_PublicKey::xmss_parameters() const {
   return m_public_key->xmss_parameters();
}

size_t XMSS_PublicKey::estimated_strength() const {
   return xmss_parameters().estimated_strength();
}

size_t XMSS_PublicKey::key_length() const {
   return xmss_parameters().estimated_strength();
}

bool XMSS_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const {
   // The public key consists of (OID, root hash, public seed). The OID is
   // validated and the byte lengths of root and public_seed are verified
   // against the parameter set during deserialization. These are opaque
   // hash outputs with no further structural invariants to check.
   return true;
}

std::unique_ptr<PK_Ops::Verification> XMSS_PublicKey::create_verification_op(std::string_view /*params*/,
                                                                             std::string_view provider) const {
   if(provider == "base" || provider.empty()) {
      return std::make_unique<XMSS_Verification_Operation>(*this);
   }
   throw Provider_Not_Found(algo_name(), provider);
}

std::unique_ptr<PK_Ops::Verification> XMSS_PublicKey::create_x509_verification_op(const AlgorithmIdentifier& alg_id,
                                                                                  std::string_view provider) const {
   if(provider == "base" || provider.empty()) {
      if(alg_id != this->algorithm_identifier()) {
         throw Decoding_Error("Unexpected AlgorithmIdentifier for XMSS X509 signature");
      }
      return std::make_unique<XMSS_Verification_Operation>(*this);
   }
   throw Provider_Not_Found(algo_name(), provider);
}

std::vector<uint8_t> XMSS_PublicKey::raw_public_key_bits() const {
   return m_public_key->raw_public_key_bits();
}

std::vector<uint8_t> XMSS_PublicKey::public_key_bits() const {
   std::vector<uint8_t> output;
   DER_Encoder(output).encode(raw_public_key_bits(), ASN1_Type::OctetString);
   return output;
}

std::vector<uint8_t> XMSS_PublicKey::raw_public_key() const {
   return raw_public_key_bits();
}

std::unique_ptr<Private_Key> XMSS_PublicKey::generate_another(RandomNumberGenerator& rng) const {
   // Note: Given only an XMSS public key we cannot know which WOTS key
   //       derivation method was used to build the XMSS tree. Hence, we have to
   //       use the default here.
   return std::make_unique<XMSS_PrivateKey>(xmss_parameters().oid(), rng);
}

}  // namespace Botan
