// 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 CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS_PLATFORM_KEYS_H_
#define CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS_PLATFORM_KEYS_H_

#include <stdint.h>

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

#include "base/component_export.h"
#include "base/functional/callback.h"
#include "base/values.h"
#include "chromeos/ash/components/platform_keys/keystore_types.h"
#include "net/cert/x509_certificate.h"

namespace chromeos::platform_keys {

// Supported key types.
enum class KeyType { kRsassaPkcs1V15, kEcdsa, kRsaOaep };

// Supported symmetric key types.
enum class SymKeyType { kAesCbc, kHmac, kSp800Kdf };

// Supported key attribute types.
enum class KeyAttributeType {
  kCertificateProvisioningId,
  kKeyPermissions,
  kPlatformKeysTag
};

// Supported hash algorithms.
enum HashAlgorithm {
  HASH_ALGORITHM_NONE,  // The value if no hash function is selected.
  HASH_ALGORITHM_SHA1,
  HASH_ALGORITHM_SHA256,
  HASH_ALGORITHM_SHA384,
  HASH_ALGORITHM_SHA512
};

enum class OperationType { kEncrypt, kDecrypt };

// Supported token IDs.
// A token is a store for keys or certs and can provide cryptographic
// operations.
// ChromeOS provides itself a user token and conditionally a system wide token.
enum class TokenId { kUser, kSystem };

// The service possible statuses.
// For every platform keys service operation callback, a status is passed,
// signaling the success or failure of the operation.
enum class Status {
  kSuccess,
  kErrorAlgorithmNotSupported,
  kErrorAlgorithmNotPermittedByCertificate,
  kErrorCertificateNotFound,
  kErrorCertificateInvalid,
  kErrorInputTooLong,
  kErrorGrantKeyPermissionForExtension,
  kErrorInternal,
  kErrorKeyAttributeRetrievalFailed,
  kErrorKeyAttributeSettingFailed,
  kErrorKeyNotAllowedForOperation,
  kErrorKeyNotFound,
  kErrorShutDown,
  // kNetError* are for errors occurred during net::* operations.
  kNetErrorAddUserCertFailed,
  kNetErrorCertificateDateInvalid,
  kNetErrorCertificateInvalid,
};

// These strings can be used to be passed to extensions as well as for logging
// purposes.
// Note: Do not change already existing status-to-string translations, since
// extensions may hardcode specific messages.
COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS)
std::string StatusToString(Status status);

// Convert platform_keys::Status into a KeystoreError. Status::kSuccess should
// not be passed in the function.
COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS)
KeystoreError StatusToKeystoreError(Status status);

// Creates platform_keys::Status into a KeystoreError. Keystore specific errors
// are not supported and should be processed separately.
COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS)
Status StatusFromKeystoreError(KeystoreError error);

// Converts KeystoreError code into an error message.
// Note: Do not change already existing error-to-string translations, since
// extensions may hardcode specific messages.
COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS)
std::string KeystoreErrorToString(KeystoreError error);

// Returns the DER encoding of the X.509 Subject Public Key Info of the public
// key in |certificate|.
COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS)
std::vector<uint8_t> GetSubjectPublicKeyInfo(
    const scoped_refptr<net::X509Certificate>& certificate);

// Intersects the two certificate lists |certs1| and |certs2| and passes the
// intersection to |callback|. The intersection preserves the order of |certs1|.
COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS)
void IntersectCertificates(
    const net::CertificateList& certs1,
    const net::CertificateList& certs2,
    base::OnceCallback<void(std::unique_ptr<net::CertificateList>)> callback);

// The output for GetPublicKeyAndAlgorithm.
struct COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS)
    GetPublicKeyAndAlgorithmOutput {
  GetPublicKeyAndAlgorithmOutput();
  GetPublicKeyAndAlgorithmOutput(GetPublicKeyAndAlgorithmOutput&&);
  ~GetPublicKeyAndAlgorithmOutput();

  Status status = Status::kSuccess;
  std::vector<uint8_t> public_key;  // Only set if status == kSuccess
  base::DictValue algorithm;        // Only set if status == kSuccess
};

// This is a convenient wrapper around GetPublicKey which also builds a
// WebCrypto algorithm dictionary and performs error checking.
COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS)
GetPublicKeyAndAlgorithmOutput GetPublicKeyAndAlgorithm(
    const std::vector<uint8_t>& possibly_invalid_cert_der,
    const std::string& algorithm_name);

struct COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS) PublicKeyInfo {
  PublicKeyInfo();
  ~PublicKeyInfo();

  // The X.509 Subject Public Key Info of the key in DER encoding.
  std::vector<uint8_t> public_key_spki_der;

  // The type of the key.
  net::X509Certificate::PublicKeyType key_type =
      net::X509Certificate::kPublicKeyTypeUnknown;

  // The size of the key in bits.
  size_t key_size_bits = 0;
};

// Checks if the certificate key type and the algorithm are
//    - valid
//    - supported
//    - compatible
// Returns Status::kSuccess if they are, or the correct error reason if they
// are not.
COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS)
Status CheckKeyTypeAndAlgorithm(net::X509Certificate::PublicKeyType key_type,
                                const std::string& algorithm_name);

// Returns the certificate key type that supports the given algorithm,
// or |kPublicKeyTypeUnknown| if the algorithm is unknown or unsupported.
COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS)
net::X509Certificate::PublicKeyType GetKeyTypeForAlgorithm(
    const std::string& algorithm_name);

// Builds a partial WebCrypto Algorithm object from the parameters available in
// |key_info|. This supports both RSA and EC keys.
// Returns std::nullopt if the key is of an unsupported type (so not RSA or
// EC).
COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS)
std::optional<base::DictValue> BuildWebCryptoAlgorithmDictionary(
    const PublicKeyInfo& key_info);

// Builds a partial WebCrypto Algorithm object from the parameters available in
// |key_info|, which must be the info of an RSA key. This doesn't include
// sign/hash parameters and thus isn't complete. platform_keys::GetPublicKey()
// enforced the public exponent 65537.
COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS)
void BuildWebCryptoRSAAlgorithmDictionary(const PublicKeyInfo& key_info,
                                          base::DictValue* algorithm);

// Builds a partial WebCrypto Algorithm object from the parameters available in
// |key_info|, which must be the info of an EC key. For more information about
// EcKeyAlgorithm dictionary, please refer to:
// https://www.w3.org/TR/WebCryptoAPI/#EcKeyAlgorithm-dictionary
COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS)
void BuildWebCryptoEcdsaAlgorithmDictionary(const PublicKeyInfo& key_info,
                                            base::DictValue* algorithm);

// Obtains information about the public key in |certificate|.
// If |certificate| contains an RSA key, sets |key_size_bits| to the modulus
// length, and |key_type| to type RSA and returns true.
// If |certificate| contains any other key type, or if the public exponent of
// the RSA key in |certificate| is not F4, returns false and does not update any
// of the output parameters.
// All pointer arguments must not be null.
COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS)
bool GetPublicKey(const scoped_refptr<net::X509Certificate>& certificate,
                  net::X509Certificate::PublicKeyType* key_type,
                  size_t* key_size_bits);

// Obtains information about the public key in |spki|.
// If |spki| is an RSA key, sets |key_size_bits| to the modulus
// length, and |key_type| to type RSA and returns true.
// If |spki| is any other key type, returns false and does not update any
// of the output parameters.
// All pointer arguments must not be null.
COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS)
bool GetPublicKeyBySpki(base::span<const uint8_t> spki,
                        net::X509Certificate::PublicKeyType* key_type,
                        size_t* key_size_bits);

struct COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS)
    ClientCertificateRequest {
  ClientCertificateRequest();
  ClientCertificateRequest(const ClientCertificateRequest& other);
  ~ClientCertificateRequest();

  // The list of the types of certificates requested, sorted in order of the
  // server's preference.
  std::vector<net::X509Certificate::PublicKeyType> certificate_key_types;

  // List of distinguished names of certificate authorities allowed by the
  // server. Each entry must be a DER-encoded X.509 DistinguishedName.
  std::vector<std::vector<uint8_t>> certificate_authorities;
};

}  // namespace chromeos::platform_keys

#endif  // CHROMEOS_ASH_COMPONENTS_PLATFORM_KEYS_PLATFORM_KEYS_H_
