// Copyright 2016 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 CHROMEOS_SERVICES_SECURE_CHANNEL_FOREGROUND_EID_GENERATOR_H_
#define CHROMEOS_SERVICES_SECURE_CHANNEL_FOREGROUND_EID_GENERATOR_H_

#include <memory>
#include <string>
#include <vector>

#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/time/clock.h"
#include "chromeos/services/secure_channel/data_with_timestamp.h"

namespace cryptauth {
class BeaconSeed;
}  // namespace cryptauth

namespace chromeos {

namespace secure_channel {

class RawEidGenerator;

// Generates ephemeral ID (EID) values that are broadcast for foreground BLE
// advertisements in the ProximityAuth protocol.
//
// When advertising in foreground mode, we don't care about battery consumption
// while advertising. We assume, however, that the scanning side is
// battery-conscious, and is using hardware-based scanning.
//
// For the inverse of this model, in which advertising is battery-sensitive, see
// BackgroundEidGenerator.
//
// A peripheral-role device advertises a 4-byte advertisement with two parts: a
// 2-byte EID which is specific to the central-role device with which it intends
// to communicate, and a 2-byte EID which is specific to the peripheral-role
// device.
//
// This class uses EID seed values synced from the back-end to generate these
// EIDs.
//
// See go/proximity-auth-ble-advertising.
class ForegroundEidGenerator {
 public:
  // Data for both a current and adjacent EID. The current EID *must* be
  // supplied, but adjacent data may be null. Each EID consists of a 2-byte EID
  // value paired with the timestamp at which time this value becomes active or
  // inactive.
  struct EidData {
    enum AdjacentDataType { NONE, PAST, FUTURE };

    EidData(const DataWithTimestamp current_data,
            std::unique_ptr<DataWithTimestamp> adjacent_data);
    ~EidData();

    AdjacentDataType GetAdjacentDataType() const;
    std::string DataInHex() const;

    const DataWithTimestamp current_data;
    const std::unique_ptr<DataWithTimestamp> adjacent_data;
  };

  // The flag used to denote that a Bluetooth 4.0 device has sent an
  // advertisement. This flag indicates to the recipient that the sender cannot
  // act as both a central- and peripheral-role device simultaneously, so the
  // recipient should advertise back instead of initializing a connection.
  static const int8_t kBluetooth4Flag;

  ForegroundEidGenerator();
  virtual ~ForegroundEidGenerator();

  // Generates EID data for the given EID seeds to be used as a background scan
  // filter. In the normal case, two DataWithTimestamp values are returned, one
  // for each EID seed rotation period. If data has not been synced from the
  // backend recently and EID seeds are unavailable, nullptr is returned.
  virtual std::unique_ptr<EidData> GenerateBackgroundScanFilter(
      const std::vector<cryptauth::BeaconSeed>& scanning_device_beacon_seeds)
      const;

  // Generates advertisement data for the given EID seeds. If data has not been
  // synced from the back-end recently and EID seeds are unavailable, nullptr is
  // returned.
  virtual std::unique_ptr<DataWithTimestamp> GenerateAdvertisement(
      const std::string& advertising_device_public_key,
      const std::vector<cryptauth::BeaconSeed>& scanning_device_beacon_seeds)
      const;

  // Generates all possible advertisements that could be created by a device
  // given that device's public key and the beacon seeds of the device which is
  // intended to scan for the advertisement.
  virtual std::vector<std::string> GeneratePossibleAdvertisements(
      const std::string& advertising_device_public_key,
      const std::vector<cryptauth::BeaconSeed>& scanning_device_beacon_seeds)
      const;

  // Given a list of device IDs, returns the device ID which could have
  // produced the supplied advertisement service data. If none of the provided
  // device IDs could have produced the advertisement, an empty string is
  // returned.
  virtual std::string IdentifyRemoteDeviceByAdvertisement(
      const std::string& advertisement_service_data,
      const std::vector<std::string>& device_ids,
      const std::vector<cryptauth::BeaconSeed>& scanning_device_beacon_seeds)
      const;

 private:
  struct EidPeriodTimestamps {
    int64_t current_period_start_timestamp_ms;
    int64_t current_period_end_timestamp_ms;
    int64_t adjacent_period_start_timestamp_ms;
    int64_t adjacent_period_end_timestamp_ms;
  };

  ForegroundEidGenerator(std::unique_ptr<RawEidGenerator> raw_eid_generator,
                         base::Clock* clock);

  std::unique_ptr<DataWithTimestamp> GenerateAdvertisement(
      const std::string& advertising_device_public_key,
      const std::vector<cryptauth::BeaconSeed>& scanning_device_beacon_seeds,
      const int64_t start_of_period_timestamp_ms,
      const int64_t end_of_period_timestamp_ms) const;

  std::unique_ptr<DataWithTimestamp> GenerateEidDataWithTimestamp(
      const std::vector<cryptauth::BeaconSeed>& scanning_device_beacon_seeds,
      const int64_t start_of_period_timestamp_ms,
      const int64_t end_of_period_timestamp_ms) const;

  std::unique_ptr<DataWithTimestamp> GenerateEidDataWithTimestamp(
      const std::vector<cryptauth::BeaconSeed>& scanning_device_beacon_seeds,
      const int64_t start_of_period_timestamp_ms,
      const int64_t end_of_period_timestamp_ms,
      std::string const* extra_entropy) const;

  std::unique_ptr<std::string> GetEidSeedForPeriod(
      const std::vector<cryptauth::BeaconSeed>& scanning_device_beacon_seeds,
      const int64_t start_of_period_timestamp_ms) const;

  std::unique_ptr<EidPeriodTimestamps> GetEidPeriodTimestamps(
      const std::vector<cryptauth::BeaconSeed>& scanning_device_beacon_seeds)
      const;

  std::unique_ptr<EidPeriodTimestamps> GetEidPeriodTimestamps(
      const std::vector<cryptauth::BeaconSeed>& scanning_device_beacon_seeds,
      const bool allow_non_current_periods) const;

  std::unique_ptr<cryptauth::BeaconSeed> GetBeaconSeedForCurrentPeriod(
      const std::vector<cryptauth::BeaconSeed>& scanning_device_beacon_seeds,
      const int64_t current_time_ms) const;

  std::unique_ptr<EidPeriodTimestamps> GetClosestPeriod(
      const std::vector<cryptauth::BeaconSeed>& scanning_device_beacon_seeds,
      const int64_t current_time_ms) const;

  static bool IsCurrentTimeAtStartOfEidPeriod(
      const int64_t start_of_period_timestamp_ms,
      const int64_t end_of_period_timestamp_ms,
      const int64_t current_timestamp_ms);

  base::Clock* clock_;

  std::unique_ptr<RawEidGenerator> raw_eid_generator_;

  DISALLOW_COPY_AND_ASSIGN(ForegroundEidGenerator);

  friend class SecureChannelForegroundEidGeneratorTest;
  FRIEND_TEST_ALL_PREFIXES(SecureChannelForegroundEidGeneratorTest,
                           GenerateBackgroundScanFilter_UsingRealEids);
  FRIEND_TEST_ALL_PREFIXES(
      SecureChannelForegroundEidGeneratorTest,
      GeneratePossibleAdvertisements_CurrentAndPastAdjacentPeriods);
  FRIEND_TEST_ALL_PREFIXES(
      SecureChannelForegroundEidGeneratorTest,
      testGeneratePossibleAdvertisements_CurrentAndFutureAdjacentPeriods);
  FRIEND_TEST_ALL_PREFIXES(SecureChannelForegroundEidGeneratorTest,
                           GeneratePossibleAdvertisements_OnlyCurrentPeriod);
  FRIEND_TEST_ALL_PREFIXES(SecureChannelForegroundEidGeneratorTest,
                           GeneratePossibleAdvertisements_OnlyFuturePeriod);
  FRIEND_TEST_ALL_PREFIXES(
      SecureChannelForegroundEidGeneratorTest,
      GeneratePossibleAdvertisements_NoAdvertisements_SeedsTooFarInFuture);
  FRIEND_TEST_ALL_PREFIXES(SecureChannelForegroundEidGeneratorTest,
                           GeneratePossibleAdvertisements_OnlyPastPeriod);
  FRIEND_TEST_ALL_PREFIXES(
      SecureChannelForegroundEidGeneratorTest,
      GeneratePossibleAdvertisements_NoAdvertisements_SeedsTooFarInPast);
  FRIEND_TEST_ALL_PREFIXES(
      SecureChannelForegroundEidGeneratorTest,
      GeneratePossibleAdvertisements_NoAdvertisements_EmptySeeds);
};

}  // namespace secure_channel

}  // namespace chromeos

#endif  // CHROMEOS_SERVICES_SECURE_CHANNEL_FOREGROUND_EID_GENERATOR_H_
