// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
///////////////////////////////////////////////////////////////////////////////

#ifndef TINK_EXPERIMENTAL_PQCRYPTO_KEM_SUBTLE_CECPQ2_HKDF_RECIPIENT_KEM_BORINGSSL_H_
#define TINK_EXPERIMENTAL_PQCRYPTO_KEM_SUBTLE_CECPQ2_HKDF_RECIPIENT_KEM_BORINGSSL_H_

#include <memory>
#include <utility>

#include "absl/strings/string_view.h"
#include "openssl/curve25519.h"
#include "openssl/ec.h"
#include "openssl/hrss.h"
#include "tink/internal/fips_utils.h"
#include "tink/subtle/common_enums.h"
#include "tink/util/secret_data.h"
#include "tink/util/statusor.h"

namespace crypto {
namespace tink {
namespace subtle {

// This class implements the CECPQ2 hybrid KEM from the recipient's perspective,
// using Boring SSL for the underlying cryptographic operations.
// This class is made generic enough so that extending the ECC algorithm to
// support other curves is trivial. As of now, the only supported curve is
// Curve25519.
//
// CECPQ2 combines both X25519 KEM and NTRU-HRSS KEM into a single hybrid KEM.
// The NTRU-HRSS is a structured lattice-based key encapsulation mechanism. It
// was originally proposed in [1] and submitted to the NIST Post-Quantum
// Cryptography standardization process [2].
//
// During the course of the NIST PQC standardization process, the NTRU-HRSS
// proposal merged with another proposal (NTRUEncrypt). The resulting scheme,
// simply called NTRU [3], is a 3rd round finalist of the NIST PQC
// standardization process.
//
// The implementation available in BoringSSL is based on [1] but it
// uses a different KEM construction based on [4]. Similar path has been taken
// by the NTRU team in the NIST competition which later adopted [4] as their
// QROM security proof approach. Note that the BoringSSL implementation is *not*
// compatible with the 3rd Round finalist NTRU running in the NIST Post-Quantum
// Cryptography standardization process [5].
//
// References:
// [1]: https://eprint.iacr.org/2017/667.pdf
// [2]: https://csrc.nist.gov/Projects/post-quantum-cryptography/
// [3]: https://ntru.org/
// [4]: https://eprint.iacr.org/2017/1005.pdf
// [5]: https://ntru.org/release/NIST-PQ-Submission-NTRU-20201016.tar.gz
class Cecpq2HkdfRecipientKemBoringSsl {
 public:
  // Constructs a recipient KEM for the specified curve, recipient's ECC
  // private key, which must be a big-endian byte array, and recipient's HRSS
  // private key. This method is made generic enough so that extending the ECC
  // algorithm to support other curves is trivial.
  static crypto::tink::util::StatusOr<
      std::unique_ptr<Cecpq2HkdfRecipientKemBoringSsl>>
  New(EllipticCurveType curve, util::SecretData ec_private_key,
      util::SecretData hrss_private_key_seed);

  virtual ~Cecpq2HkdfRecipientKemBoringSsl() = default;

  // Computes the shared secret from the ECC private key and peer's ECC encoded
  // public key, and the shared secret from the HRSS private key, then uses a
  // hkdf to derive the symmetric key from the two shared secrets, hkdf info and
  // hkdf salt. This method is made generic enough so that extending the ECC
  // algorithm to support other curves is trivial.
  virtual crypto::tink::util::StatusOr<util::SecretData> GenerateKey(
      absl::string_view kem_bytes, HashType hash, absl::string_view hkdf_salt,
      absl::string_view hkdf_info, uint32_t key_size_in_bytes,
      EcPointFormat point_format) const = 0;
};

// Implementation of Cecpq2HkdfRecipientKemBoringSsl for Curve25519.
class Cecpq2HkdfX25519RecipientKemBoringSsl
    : public Cecpq2HkdfRecipientKemBoringSsl {
 public:
  static constexpr crypto::tink::internal::FipsCompatibility kFipsStatus =
      crypto::tink::internal::FipsCompatibility::kNotFips;

  // Constructs a recipient CECPQ2 KEM for recipient's X25519 private key,
  // which must be a big-endian byte array, and recipient's HRSS private key.
  static crypto::tink::util::StatusOr<
      std::unique_ptr<Cecpq2HkdfRecipientKemBoringSsl>>
  New(EllipticCurveType curve, util::SecretData ec_private_key,
      util::SecretData hrss_private_key_seed);

  // Computes the shared secret from X25519 private key and peer's X25519
  // encoded public key, and the shared secret from the HRSS private key, then
  // uses a hkdf to derive the symmetric key from the two shared secrets, hkdf
  // info and hkdf salt.
  crypto::tink::util::StatusOr<util::SecretData> GenerateKey(
      absl::string_view kem_bytes, HashType hash, absl::string_view hkdf_salt,
      absl::string_view hkdf_info, uint32_t key_size_in_bytes,
      EcPointFormat point_format) const override;

 private:
  // The private constructor only takes the X25519 and HRSS private keys and
  // assign them to the class private members.
  explicit Cecpq2HkdfX25519RecipientKemBoringSsl(
      util::SecretData ec_private_key, util::SecretData hrss_private_key_seed)
      : private_key_x25519_(std::move(ec_private_key)),
        private_key_hrss_seed_(std::move(hrss_private_key_seed)) {}

  // X25519 and HRSS private key containers
  util::SecretData private_key_x25519_;
  util::SecretData private_key_hrss_seed_;
};

}  // namespace subtle
}  // namespace tink
}  // namespace crypto

#endif  // TINK_EXPERIMENTAL_PQCRYPTO_KEM_SUBTLE_CECPQ2_HKDF_RECIPIENT_KEM_BORINGSSL_H_
