/*
 *  Copyright 2009 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#ifndef PC_SRTP_FILTER_H_
#define PC_SRTP_FILTER_H_

#include <list>
#include <map>
#include <memory>
#include <string>
#include <vector>

#include "absl/types/optional.h"
#include "api/array_view.h"
#include "api/crypto_params.h"
#include "api/jsep.h"
#include "pc/session_description.h"
#include "rtc_base/buffer.h"
#include "rtc_base/constructor_magic.h"
#include "rtc_base/critical_section.h"
#include "rtc_base/ssl_stream_adapter.h"
#include "rtc_base/thread_checker.h"

// Forward declaration to avoid pulling in libsrtp headers here
struct srtp_event_data_t;
struct srtp_ctx_t_;

namespace cricket {

// A helper class used to negotiate SDES crypto params.
// TODO(zhihuang): Find a better name for this class, like "SdesNegotiator".
class SrtpFilter {
 public:
  enum Mode { PROTECT, UNPROTECT };
  enum Error {
    ERROR_NONE,
    ERROR_FAIL,
    ERROR_AUTH,
    ERROR_REPLAY,
  };

  SrtpFilter();
  ~SrtpFilter();

  // Whether the filter is active (i.e. crypto has been properly negotiated).
  bool IsActive() const;

  // Handle the offer/answer negotiation of the crypto parameters internally.
  // TODO(zhihuang): Make SetOffer/ProvisionalAnswer/Answer private as helper
  // methods once start using Process.
  bool Process(const std::vector<CryptoParams>& cryptos,
               webrtc::SdpType type,
               ContentSource source);

  // Indicates which crypto algorithms and keys were contained in the offer.
  // offer_params should contain a list of available parameters to use, or none,
  // if crypto is not desired. This must be called before SetAnswer.
  bool SetOffer(const std::vector<CryptoParams>& offer_params,
                ContentSource source);
  // Same as SetAnwer. But multiple calls are allowed to SetProvisionalAnswer
  // after a call to SetOffer.
  bool SetProvisionalAnswer(const std::vector<CryptoParams>& answer_params,
                            ContentSource source);
  // Indicates which crypto algorithms and keys were contained in the answer.
  // answer_params should contain the negotiated parameters, which may be none,
  // if crypto was not desired or could not be negotiated (and not required).
  // This must be called after SetOffer. If crypto negotiation completes
  // successfully, this will advance the filter to the active state.
  bool SetAnswer(const std::vector<CryptoParams>& answer_params,
                 ContentSource source);

  bool ResetParams();

  static bool ParseKeyParams(const std::string& params,
                             uint8_t* key,
                             size_t len);

  absl::optional<int> send_cipher_suite() { return send_cipher_suite_; }
  absl::optional<int> recv_cipher_suite() { return recv_cipher_suite_; }

  rtc::ArrayView<const uint8_t> send_key() { return send_key_; }
  rtc::ArrayView<const uint8_t> recv_key() { return recv_key_; }

 protected:
  bool ExpectOffer(ContentSource source);

  bool StoreParams(const std::vector<CryptoParams>& params,
                   ContentSource source);

  bool ExpectAnswer(ContentSource source);

  bool DoSetAnswer(const std::vector<CryptoParams>& answer_params,
                   ContentSource source,
                   bool final);

  bool NegotiateParams(const std::vector<CryptoParams>& answer_params,
                       CryptoParams* selected_params);

 private:
  bool ApplySendParams(const CryptoParams& send_params);

  bool ApplyRecvParams(const CryptoParams& recv_params);

  enum State {
    ST_INIT,                    // SRTP filter unused.
    ST_SENTOFFER,               // Offer with SRTP parameters sent.
    ST_RECEIVEDOFFER,           // Offer with SRTP parameters received.
    ST_SENTPRANSWER_NO_CRYPTO,  // Sent provisional answer without crypto.
    // Received provisional answer without crypto.
    ST_RECEIVEDPRANSWER_NO_CRYPTO,
    ST_ACTIVE,  // Offer and answer set.
    // SRTP filter is active but new parameters are offered.
    // When the answer is set, the state transitions to ST_ACTIVE or ST_INIT.
    ST_SENTUPDATEDOFFER,
    // SRTP filter is active but new parameters are received.
    // When the answer is set, the state transitions back to ST_ACTIVE.
    ST_RECEIVEDUPDATEDOFFER,
    // SRTP filter is active but the sent answer is only provisional.
    // When the final answer is set, the state transitions to ST_ACTIVE or
    // ST_INIT.
    ST_SENTPRANSWER,
    // SRTP filter is active but the received answer is only provisional.
    // When the final answer is set, the state transitions to ST_ACTIVE or
    // ST_INIT.
    ST_RECEIVEDPRANSWER
  };
  State state_ = ST_INIT;
  std::vector<CryptoParams> offer_params_;
  CryptoParams applied_send_params_;
  CryptoParams applied_recv_params_;
  absl::optional<int> send_cipher_suite_;
  absl::optional<int> recv_cipher_suite_;
  rtc::ZeroOnFreeBuffer<uint8_t> send_key_;
  rtc::ZeroOnFreeBuffer<uint8_t> recv_key_;
};

}  // namespace cricket

#endif  // PC_SRTP_FILTER_H_
