chromium/chrome/browser/ash/child_accounts/parent_access_code/authenticator.h

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_ASH_CHILD_ACCOUNTS_PARENT_ACCESS_CODE_AUTHENTICATOR_H_
#define CHROME_BROWSER_ASH_CHILD_ACCOUNTS_PARENT_ACCESS_CODE_AUTHENTICATOR_H_

#include <memory>
#include <optional>
#include <ostream>
#include <string>

#include "base/time/time.h"
#include "base/values.h"
#include "components/account_id/account_id.h"
#include "crypto/hmac.h"

namespace ash {
namespace parent_access {

// Configuration used to generate and verify parent access code.
class AccessCodeConfig {
 public:
  // Returns AccessCodeConfig created from a |dictionary|, if the |dictionary|
  // contains valid config data.
  static std::optional<AccessCodeConfig> FromDictionary(
      const base::Value::Dict& dictionary);

  // TODO(agawronska): Make constructor private.
  // To create valid AccessCodeConfig:
  // * |shared_secret| cannot be empty
  // * |code_validity| needs to be in between 30s and 3600s
  // * |clock_drift_tolerance| needs to be between 0 and 1800s
  // The above restrictions are applied to AccessCodeConfig policy that is the
  // main source of this configuration.
  AccessCodeConfig(const std::string& shared_secret,
                   base::TimeDelta code_validity,
                   base::TimeDelta clock_drift_tolerance);
  AccessCodeConfig(AccessCodeConfig&&);
  AccessCodeConfig& operator=(AccessCodeConfig&&);

  AccessCodeConfig(const AccessCodeConfig&) = delete;
  AccessCodeConfig& operator=(const AccessCodeConfig&) = delete;

  ~AccessCodeConfig();

  // Secret shared between child and parent devices.
  const std::string& shared_secret() const { return shared_secret_; }

  // Time that access code is valid for.
  base::TimeDelta code_validity() const { return code_validity_; }

  // The allowed difference between the clock on child and parent devices.
  base::TimeDelta clock_drift_tolerance() const {
    return clock_drift_tolerance_;
  }

  // Converts the AccessCodeConfig object to its dictionary equivalent.
  base::Value::Dict ToDictionary() const;

 private:
  std::string shared_secret_;
  base::TimeDelta code_validity_;
  base::TimeDelta clock_drift_tolerance_;
};

// Parent access code that can be used to authorize various actions on child
// user's device.
// Typical lifetime of the code is 10 minutes and clock difference between
// generating and validating device is half of the code lifetime. Clock
// difference is accounted for during code validation.
class AccessCode {
 public:
  // To create valid AccessCode:
  // * |code| needs to be 6 characters long
  // * |valid_to| needs to be greater than |valid_from|
  AccessCode(const std::string& code,
             base::Time valid_from,
             base::Time valid_to);
  AccessCode(const AccessCode&);
  AccessCode& operator=(const AccessCode&);
  ~AccessCode();

  // Parent access code.
  const std::string& code() const { return code_; }

  // Code validity start time.
  base::Time valid_from() const { return valid_from_; }

  // Code expiration time.
  base::Time valid_to() const { return valid_to_; }

  bool operator==(const AccessCode&) const;
  bool operator!=(const AccessCode&) const;
  friend std::ostream& operator<<(std::ostream&, const AccessCode&);

 private:
  std::string code_;
  base::Time valid_from_;
  base::Time valid_to_;
};

// Generates and validates parent access codes.
// Does not support timestamp from before Unix Epoch.
class Authenticator {
 public:
  // Granularity of which generation and verification of access code are carried
  // out. Should not exceed code validity period.
  static constexpr base::TimeDelta kAccessCodeGranularity = base::Minutes(1);

  explicit Authenticator(AccessCodeConfig config);

  Authenticator(const Authenticator&) = delete;
  Authenticator& operator=(const Authenticator&) = delete;

  ~Authenticator();

  // Generates parent access code from the given |timestamp|. Returns the code
  // if generation was successful. |timestamp| needs to be greater or equal Unix
  // Epoch.
  std::optional<AccessCode> Generate(base::Time timestamp) const;

  // Returns AccessCode structure with validity information, if |code| is
  // valid for the given timestamp. |timestamp| needs to be greater or equal
  // Unix Epoch.
  std::optional<AccessCode> Validate(const std::string& code,
                                     base::Time timestamp) const;

 private:
  // Returns AccessCode structure with validity information, if |code| is valid
  // for the range [|valid_from|, |valid_to|). |valid_to| needs to be greater or
  // equal to |valid_from|. |valid_from| needs to be greater or equal Unix
  // Epoch.
  std::optional<AccessCode> ValidateInRange(const std::string& code,
                                            base::Time valid_from,
                                            base::Time valid_to) const;

  // Configuration used to generate and validate parent access code.
  const AccessCodeConfig config_;

  // Keyed-hash message authentication generator.
  crypto::HMAC hmac_{crypto::HMAC::SHA1};
};

}  // namespace parent_access
}  // namespace ash

#endif  // CHROME_BROWSER_ASH_CHILD_ACCOUNTS_PARENT_ACCESS_CODE_AUTHENTICATOR_H_