// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_QUIC_TEST_TOOLS_CRYPTO_TEST_UTILS_H_
#define NET_QUIC_TEST_TOOLS_CRYPTO_TEST_UTILS_H_

#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>

#include <utility>
#include <vector>

#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/string_piece.h"
#include "net/quic/crypto/crypto_framer.h"
#include "net/quic/quic_framer.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/test_tools/quic_test_utils.h"

namespace net {

class ChannelIDSource;
class CommonCertSets;
class ProofSource;
class ProofVerifier;
class ProofVerifyContext;
class QuicClock;
class QuicConfig;
class QuicCryptoClientStream;
class QuicCryptoServerConfig;
class QuicCryptoServerStream;
class QuicCryptoStream;
class QuicRandom;
class QuicServerId;

namespace test {

class PacketSavingConnection;

class CryptoTestUtils {
 public:
  // An interface for a source of callbacks. This is used for invoking
  // callbacks asynchronously.
  //
  // Call the RunPendingCallbacks method regularly to run the callbacks from
  // this source.
  class CallbackSource {
   public:
    virtual ~CallbackSource() {}

    // Runs pending callbacks from this source. If there is no pending
    // callback, does nothing.
    virtual void RunPendingCallbacks() = 0;
  };

  // FakeServerOptions bundles together a number of options for configuring the
  // server in HandshakeWithFakeServer.
  struct FakeServerOptions {
    FakeServerOptions();

    // If token_binding_enabled is true, then the server will attempt to
    // negotiate Token Binding.
    bool token_binding_enabled;
  };

  // FakeClientOptions bundles together a number of options for configuring
  // HandshakeWithFakeClient.
  struct FakeClientOptions {
    FakeClientOptions();

    // If channel_id_enabled is true then the client will attempt to send a
    // ChannelID.
    bool channel_id_enabled;

    // If channel_id_source_async is true then the client will use an async
    // ChannelIDSource for testing. Ignored if channel_id_enabled is false.
    bool channel_id_source_async;

    // If token_binding_enabled is true, then the client will attempt to
    // negotiate Token Binding.
    bool token_binding_enabled;
  };

  // returns: the number of client hellos that the client sent.
  static int HandshakeWithFakeServer(QuicConfig* server_quic_config,
                                     MockQuicConnectionHelper* helper,
                                     MockAlarmFactory* alarm_factory,
                                     PacketSavingConnection* client_conn,
                                     QuicCryptoClientStream* client,
                                     const FakeServerOptions& options);

  // returns: the number of client hellos that the client sent.
  static int HandshakeWithFakeClient(MockQuicConnectionHelper* helper,
                                     MockAlarmFactory* alarm_factory,
                                     PacketSavingConnection* server_conn,
                                     QuicCryptoServerStream* server,
                                     const QuicServerId& server_id,
                                     const FakeClientOptions& options);

  // SetupCryptoServerConfigForTest configures |config| and |crypto_config|
  // with sensible defaults for testing.
  static void SetupCryptoServerConfigForTest(
      const QuicClock* clock,
      QuicRandom* rand,
      QuicConfig* config,
      QuicCryptoServerConfig* crypto_config,
      const FakeServerOptions& options);

  // CommunicateHandshakeMessages moves messages from |client| to |server| and
  // back until |clients|'s handshake has completed.
  static void CommunicateHandshakeMessages(PacketSavingConnection* client_conn,
                                           QuicCryptoStream* client,
                                           PacketSavingConnection* server_conn,
                                           QuicCryptoStream* server);

  // CommunicateHandshakeMessagesAndRunCallbacks moves messages from |client|
  // to |server| and back until |client|'s handshake has completed. If
  // |callback_source| is not nullptr,
  // CommunicateHandshakeMessagesAndRunCallbacks also runs callbacks from
  // |callback_source| between processing messages.
  static void CommunicateHandshakeMessagesAndRunCallbacks(
      PacketSavingConnection* client_conn,
      QuicCryptoStream* client,
      PacketSavingConnection* server_conn,
      QuicCryptoStream* server,
      CallbackSource* callback_source);

  // AdvanceHandshake attempts to moves messages from |client| to |server| and
  // |server| to |client|. Returns the number of messages moved.
  static std::pair<size_t, size_t> AdvanceHandshake(
      PacketSavingConnection* client_conn,
      QuicCryptoStream* client,
      size_t client_i,
      PacketSavingConnection* server_conn,
      QuicCryptoStream* server,
      size_t server_i);

  // Returns the value for the tag |tag| in the tag value map of |message|.
  static std::string GetValueForTag(const CryptoHandshakeMessage& message,
                                    QuicTag tag);

  // Returns a |ProofSource| that serves up test certificates.
  static ProofSource* ProofSourceForTesting();

  // Returns a |ProofVerifier| that uses the QUIC testing root CA.
  static ProofVerifier* ProofVerifierForTesting();

  // Returns a real ProofVerifier (not a fake proof verifier) for testing.
  static ProofVerifier* RealProofVerifierForTesting();

  // Returns a hash of the leaf test certificate.
  static uint64_t LeafCertHashForTesting();

  // Returns a |ProofVerifyContext| that must be used with the verifier
  // returned by |ProofVerifierForTesting|.
  static ProofVerifyContext* ProofVerifyContextForTesting();

  // MockCommonCertSets returns a CommonCertSets that contains a single set with
  // hash |hash|, consisting of the certificate |cert| at index |index|.
  static CommonCertSets* MockCommonCertSets(base::StringPiece cert,
                                            uint64_t hash,
                                            uint32_t index);

  // Creates a minimal dummy reject message that will pass the client-config
  // validation tests. This will include a server config, but no certs, proof
  // source address token, or server nonce.
  static void FillInDummyReject(CryptoHandshakeMessage* rej,
                                bool reject_is_stateless);

  // ParseTag returns a QuicTag from parsing |tagstr|. |tagstr| may either be
  // in the format "EXMP" (i.e. ASCII format), or "#11223344" (an explicit hex
  // format). It CHECK fails if there's a parse error.
  static QuicTag ParseTag(const char* tagstr);

  // Message constructs a handshake message from a variable number of
  // arguments. |message_tag| is passed to |ParseTag| and used as the tag of
  // the resulting message. The arguments are taken in pairs and nullptr
  // terminated. The first of each pair is the tag of a tag/value and is given
  // as an argument to |ParseTag|. The second is the value of the tag/value
  // pair and is either a hex dump, preceeded by a '#', or a raw value.
  //
  //   Message(
  //       "CHLO",
  //       "NOCE", "#11223344",
  //       "SNI", "www.example.com",
  //       nullptr);
  static CryptoHandshakeMessage Message(const char* message_tag, ...);

  // ChannelIDSourceForTesting returns a ChannelIDSource that generates keys
  // deterministically based on the hostname given in the GetChannelIDKey call.
  // This ChannelIDSource works in synchronous mode, i.e., its GetChannelIDKey
  // method never returns QUIC_PENDING.
  static ChannelIDSource* ChannelIDSourceForTesting();

  // MovePackets parses crypto handshake messages from packet number
  // |*inout_packet_index| through to the last packet (or until a packet fails
  // to decrypt) and has |dest_stream| process them. |*inout_packet_index| is
  // updated with an index one greater than the last packet processed.
  static void MovePackets(PacketSavingConnection* source_conn,
                          size_t* inout_packet_index,
                          QuicCryptoStream* dest_stream,
                          PacketSavingConnection* dest_conn,
                          Perspective dest_perspective);

 private:
  static void CompareClientAndServerKeys(QuicCryptoClientStream* client,
                                         QuicCryptoServerStream* server);

  DISALLOW_COPY_AND_ASSIGN(CryptoTestUtils);
};

}  // namespace test

}  // namespace net

#endif  // NET_QUIC_TEST_TOOLS_CRYPTO_TEST_UTILS_H_
