/*
 * Copyright 2018 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 FCP_SECAGG_SHARED_ECDH_KEY_AGREEMENT_H_
#define FCP_SECAGG_SHARED_ECDH_KEY_AGREEMENT_H_

#include <string>

#include "fcp/base/monitoring.h"
#include "fcp/secagg/shared/aes_key.h"
#include "fcp/secagg/shared/ecdh_keys.h"
#include "openssl/base.h"
#include "openssl/ec.h"

namespace fcp {
namespace secagg {

// This class represents a participant in the ECDH Key Agreement protocol. It
// serves to hold one private/public ECDH key pair, or just one private key
// (with no public key associated).
//
// The curve used for this is NIST P-256, also known as prime256v1.
//
// The private and public keys can be retrieved from the object in the form of
// strings. The compressed representation of the public key is used.
//
// The shared secret is always hashed with SHA-256 to produce a valid AES-256
// key before returning.
//
// Because only certain strings are valid ECDH keys, even if they have the
// correct length, this class uses factory methods to instantiate new objects.
// The constructors should never be called directly by a user of this class.
class EcdhKeyAgreement {
 public:
  // FACTORY METHODS:
  // Use one of the CreateFrom* factory methods below to instantiate a new
  // EcdhKeyAgreement object, and note that these methods may fail. Do not use a
  // constructor directly to instantiate an EcdhKeyAgreement object.

  // Returns a new EcdhKeyAgreement object containing a keypair randomly
  // generated using OpenSSL's randomness. Only fails if OpenSSL has an internal
  // error.
  static StatusOr<std::unique_ptr<EcdhKeyAgreement>> CreateFromRandomKeys();

  // Returns a new EcdhKeyAgreement object containing the supplied private key,
  // and no public key. This object can still be used to do ECDH with other
  // public keys.
  // Fails if the supplied private key is invalid.
  static StatusOr<std::unique_ptr<EcdhKeyAgreement>> CreateFromPrivateKey(
      const EcdhPrivateKey& private_key);

  // Returns a new EcdhKeyAgreement object containing the supplied
  // private/public keypair.
  // Fails if the supplied keypair is invalid.
  static StatusOr<std::unique_ptr<EcdhKeyAgreement>> CreateFromKeypair(
      const EcdhPrivateKey& private_key, const EcdhPublicKey& public_key);

  // Returns a representation of the private key stored in this object.
  EcdhPrivateKey PrivateKey() const;

  // Returns a representation of the public key stored in this object. This uses
  // the compressed ECDH public key representation.
  //
  // If object has no public key (i.e. it was constructed with
  // CreateFromPrivateKey), this method will return an empty string.
  EcdhPublicKey PublicKey() const;

  // Returns the shared secret AES key generated by ECDH, using  with the stored
  // ECDH private key and the supplied ECDH public key, and then hashed with
  // SHA-256. The output will be an AES-256 key.
  //
  // If the other_key is not a valid public key, instead returns an error status
  // with code INVALID_ARGUMENT.
  StatusOr<AesKey> ComputeSharedSecret(const EcdhPublicKey& other_key) const;

  // DO NOT USE THESE CONSTRUCTORS.
  // Instead, one of the CreateFrom* factory methods below.
  // These constructors are made public only as an implementation detail.
  // See https://abseil.io/tips/134 for details.
  EcdhKeyAgreement();
  explicit EcdhKeyAgreement(EC_KEY* key);

 private:
  std::unique_ptr<EC_KEY, void (*)(EC_KEY*)> key_;
};

}  // namespace secagg
}  // namespace fcp

#endif  // FCP_SECAGG_SHARED_ECDH_KEY_AGREEMENT_H_
