chromium/chrome/browser/ash/cert_provisioning/cert_provisioning_common.h

// Copyright 2020 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_CERT_PROVISIONING_CERT_PROVISIONING_COMMON_H_
#define CHROME_BROWSER_ASH_CERT_PROVISIONING_CERT_PROVISIONING_COMMON_H_

#include <optional>
#include <string>

#include "base/containers/enum_set.h"
#include "base/functional/callback.h"
#include "base/functional/callback_forward.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
#include "chromeos/ash/components/dbus/constants/attestation_constants.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "net/cert/x509_certificate.h"

class PrefRegistrySimple;
class Profile;

namespace attestation {
enum VerifiedAccessFlow : int;
}  // namespace attestation

namespace ash {

namespace platform_keys {
class KeyPermissionsManager;
class PlatformKeysService;
}  // namespace platform_keys

namespace cert_provisioning {

// A feature to prevent Certificate Provisioning workers from attempting to
// continue the provisioning process on timeout (without receiving an
// invalidation). It is intended to be used for testing only to verify that new
// invalidations actually work. Also see `ShouldOnlyUseInvalidations`.
// TODO(b/336989561): Remove this after the migration to new invalidations is
// done.
BASE_DECLARE_FEATURE(kCertProvisioningUseOnlyInvalidationsForTesting);

// Used for both DeleteVaKey and DeleteVaKeysByPrefix
using DeleteVaKeyCallback = base::OnceCallback<void(bool)>;

const char kKeyNamePrefix[] = "cert-provis-";

// The type for variables containing an error from DM Server response.
using CertProvisioningResponseErrorType =
    enterprise_management::ClientCertificateProvisioningResponse::Error;
// The namespace that contains convenient aliases for error values, e.g.
// UNDEFINED, TIMED_OUT, IDENTITY_VERIFICATION_ERROR, CA_ERROR.
using CertProvisioningResponseError =
    enterprise_management::ClientCertificateProvisioningResponse;

// Numeric values are used in serialization and should not be remapped.
enum class CertScope { kUser = 0, kDevice = 1, kMaxValue = kDevice };

// These values are used in serialization and should be changed carefully. Also
// enums.xml should be updated.
enum class CertProvisioningWorkerState {
  kInitState = 0,
  kKeypairGenerated = 1,
  kStartCsrResponseReceived = 2,  // Unused in "dynamic" flow.
  kVaChallengeFinished = 3,
  kKeyRegistered = 4,
  kKeypairMarked = 5,
  kSignCsrFinished = 6,
  kFinishCsrResponseReceived = 7,  // Unused in "dynamic" flow.
  kSucceeded = 8,
  kInconsistentDataError = 9,
  kFailed = 10,
  kCanceled = 11,

  // The following states are only used in the "dynamic" flow.
  // The worker is ready for next server-provided operation.
  kReadyForNextOperation = 12,
  // The worker has received an "Authorize" instruction.
  kAuthorizeInstructionReceived = 13,
  // The worker has received a "Proof of Possession" instruction.
  kProofOfPossessionInstructionReceived = 14,
  // The worker has received an "Import Certificate" instruction.
  kImportCertificateInstructionReceived = 15,

  kMaxValue = kImportCertificateInstructionReceived,
};

// All states that are allowed in a "static" flow.
inline constexpr base::EnumSet<CertProvisioningWorkerState,
                               CertProvisioningWorkerState::kInitState,
                               CertProvisioningWorkerState::kMaxValue>
    kStaticWorkerStates = {
        CertProvisioningWorkerState::kInitState,
        CertProvisioningWorkerState::kKeypairGenerated,
        CertProvisioningWorkerState::kStartCsrResponseReceived,
        CertProvisioningWorkerState::kVaChallengeFinished,
        CertProvisioningWorkerState::kKeyRegistered,
        CertProvisioningWorkerState::kKeypairMarked,
        CertProvisioningWorkerState::kSignCsrFinished,
        CertProvisioningWorkerState::kFinishCsrResponseReceived,
        CertProvisioningWorkerState::kSucceeded,
        CertProvisioningWorkerState::kInconsistentDataError,
        CertProvisioningWorkerState::kFailed,
        CertProvisioningWorkerState::kCanceled};

// All states that are allowed in a "dynamic" flow.
inline constexpr base::EnumSet<CertProvisioningWorkerState,
                               CertProvisioningWorkerState::kInitState,
                               CertProvisioningWorkerState::kMaxValue>
    kDynamicWorkerStates = {
        CertProvisioningWorkerState::kInitState,
        CertProvisioningWorkerState::kKeypairGenerated,
        CertProvisioningWorkerState::kVaChallengeFinished,
        CertProvisioningWorkerState::kKeyRegistered,
        CertProvisioningWorkerState::kKeypairMarked,
        CertProvisioningWorkerState::kSignCsrFinished,
        CertProvisioningWorkerState::kSucceeded,
        CertProvisioningWorkerState::kInconsistentDataError,
        CertProvisioningWorkerState::kFailed,
        CertProvisioningWorkerState::kCanceled,
        CertProvisioningWorkerState::kReadyForNextOperation,
        CertProvisioningWorkerState::kAuthorizeInstructionReceived,
        CertProvisioningWorkerState::kProofOfPossessionInstructionReceived,
        CertProvisioningWorkerState::kImportCertificateInstructionReceived};

// Location where a generated key has been persisted by a "dynamic" flow worker.
// These values are used in serialization and should be changed carefully.
enum class KeyLocation {
  kNone = 0,
  kVaDatabase = 1,
  kPkcs11Token = 2,
  kMaxValue = kPkcs11Token
};

// Types of the requests sent from the certificate provisioning client to the
// device management server.
enum class DeviceManagementServerRequestType {
  kStartCsr = 0,
  kFinishCsr = 1,
  kDownloadCert = 2,
};

// Converts the worker |state| to a string. This is mainly for logging purposes.
std::string CertificateProvisioningWorkerStateToString(
    CertProvisioningWorkerState state);

// Returns true if the |state| is one of final states, i. e. worker should
// finish its task in one of them.
bool IsFinalState(CertProvisioningWorkerState state);

using CertProfileId = std::string;

// Names of CertProfile fields in a base::Value representation. Must be in sync
// with policy schema definitions in RequiredClientCertificateForDevice.yaml and
// RequiredClientCertificateForUser.yaml.
const char kCertProfileIdKey[] = "cert_profile_id";
const char kCertProfileNameKey[] = "name";
const char kCertProfileRenewalPeroidSec[] = "renewal_period_seconds";
const char kCertProfilePolicyVersionKey[] = "policy_version";
const char kCertProfileProtocolVersion[] = "protocol_version";
const char kCertProfileIsVaEnabledKey[] = "enable_remote_attestation_check";
const char kCertProfileKeyType[] = "key_algorithm";

// The version of the certificate provisioning protocol between ChromeOS client
// and device management server.
// The values must match the description in
// RequiredClientCertificateForDevice.yaml and
// RequiredClientCertificateForUser.yaml.
// They are also used in serialization so they should not be renumbered.
enum class ProtocolVersion {
  // Original "static" protocol.
  kStatic = 1,
  // "Dynamic" protocol.
  kDynamic = 2,
};

struct CertProfile {
  static std::optional<CertProfile> MakeFromValue(
      const base::Value::Dict& value);

  CertProfile();
  // For tests.
  CertProfile(CertProfileId profile_id,
              std::string name,
              std::string policy_version,
              bool is_va_enabled,
              base::TimeDelta renewal_period,
              ProtocolVersion protocol_version);
  CertProfile(const CertProfile& other);
  CertProfile& operator=(const CertProfile&);
  CertProfile(CertProfile&& source);
  CertProfile& operator=(CertProfile&&);
  ~CertProfile();

  CertProfileId profile_id;
  // Human-readable name (UTF-8).
  std::string name;
  std::string policy_version;
  bool is_va_enabled = true;
  // Default renewal period 0 means that a certificate will be renewed only
  // after the previous one has expired (0 seconds before it is expires).
  base::TimeDelta renewal_period = base::Seconds(0);
  ProtocolVersion protocol_version = ProtocolVersion::kStatic;

  // IMPORTANT:
  // Increment this when you add/change any member in CertProfile (and update
  // all functions that fail to compile because of it).
  static constexpr int kVersion = 6;

  bool operator==(const CertProfile& other) const;
  bool operator!=(const CertProfile& other) const;
};

struct CertProfileComparator {
  bool operator()(const CertProfile& a, const CertProfile& b) const;
};

// Parses `protocol_version_value` as ProtocolVersion enum.
std::optional<ProtocolVersion> ParseProtocolVersion(
    std::optional<int> protocol_version_value);

void RegisterProfilePrefs(PrefRegistrySimple* registry);
void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
const char* GetPrefNameForCertProfiles(CertScope scope);
const char* GetPrefNameForSerialization(CertScope scope);

// Returns the nickname (CKA_LABEL) for keys created for the |profile_id|.
std::string GetKeyName(CertProfileId profile_id);
// Returns the flow type type for VA API calls for |scope|.
::attestation::VerifiedAccessFlow GetVaFlowType(CertScope scope);
chromeos::platform_keys::TokenId GetPlatformKeysTokenId(CertScope scope);

// This functions should be used to delete keys that were created by
// TpmChallengeKey* and were not registered yet. (To delete registered keys
// PlatformKeysService should be used.)
void DeleteVaKey(CertScope scope,
                 Profile* profile,
                 const std::string& key_name,
                 DeleteVaKeyCallback callback);
void DeleteVaKeysByPrefix(CertScope scope,
                          Profile* profile,
                          const std::string& key_prefix,
                          DeleteVaKeyCallback callback);

// Parses |data| using net::X509Certificate::FORMAT_AUTO as format specifier.
// Expects exactly one certificate to be the result and returns it.  If parsing
// fails or if more than one certificates were in |data|, returns an null ptr.
scoped_refptr<net::X509Certificate> CreateSingleCertificateFromBytes(
    const char* data,
    size_t length);

// Returns the PlatformKeysService to be used.
// If |scope| is CertScope::kDevice, |profile| is ignored and the
// device-wide PlatformKeysService is returned.
// If |scope| is CertScope::kUser, returns the service for |profile|.
// The returned object is owned by the Profile (user-specific) or globally
// (device-wide) and may only be used until it notifies its observers that it is
// being shut down.
platform_keys::PlatformKeysService* GetPlatformKeysService(CertScope scope,
                                                           Profile* profile);

// Returns the KeyPermissionsManager to be used.
// If |scope| is CertScope::kDevice, |profile| is ignored and the
// system token key permissions manager is returned.
// If |scope| is CertScope::kUser, returns the user private slot key permissions
// manager for |profile|.
platform_keys::KeyPermissionsManager* GetKeyPermissionsManager(
    CertScope scope,
    Profile* profile);

// Generates a random unique identifier for a certificate provisioning process.
// It is used to receive invalidations (to wake up waiting workers) and for
// consistent logging with the server-side code (see "cppId" in the logs).
std::string GenerateCertProvisioningId();

// Creates an invalidation listener type based the cert provisioning process id
// (see `GenerateCertProvisioningId()`). The type is a string that is
// constructed both server- and client-side and is used to deliver FCM
// invalidations from the server-side.
std::string MakeInvalidationListenerType(
    const std::string& cert_prov_process_id);

// Returns true if workers should only progress when they receive an
// invalidation (not on timeout).
bool ShouldOnlyUseInvalidations();

}  // namespace cert_provisioning
}  // namespace ash

#endif  // CHROME_BROWSER_ASH_CERT_PROVISIONING_CERT_PROVISIONING_COMMON_H_