chromium/chromeos/components/kcer/kcer_nss/test_utils.h

// Copyright 2023 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_COMPONENTS_KCER_KCER_NSS_TEST_UTILS_H_
#define CHROMEOS_COMPONENTS_KCER_KCER_NSS_TEST_UTILS_H_

#include <stdint.h>

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

#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/components/kcer/kcer.h"
#include "chromeos/components/kcer/kcer_impl.h"
#include "chromeos/components/kcer/kcer_nss/kcer_token_impl_nss.h"
#include "chromeos/components/kcer/key_permissions.pb.h"
#include "crypto/scoped_test_nss_db.h"
#include "net/test/cert_builder.h"

namespace kcer {

struct KeyAndCert {
  KeyAndCert(PublicKey key, scoped_refptr<const Cert> cert);
  KeyAndCert(KeyAndCert&&);
  KeyAndCert& operator=(KeyAndCert&&);
  ~KeyAndCert();

  PublicKey key;
  scoped_refptr<const Cert> cert;
};

//==============================================================================

// A helper class to work with tokens (that exist on the IO thread) from the UI
// thread.
class TokenHolder {
 public:
  // Creates a KcerToken of the type `token` and moves it to the IO thread. If
  // `initialize` then the KcerToken will be ready to process requests
  // immediately.
  TokenHolder(Token token,
              HighLevelChapsClient* chaps_client,
              bool initialize_token);
  TokenHolder(Token token,
              HighLevelChapsClient* chaps_client,
              bool initialize_token,
              crypto::ScopedPK11Slot nss_slot);
  ~TokenHolder();

  // If KcerToken was not initialized on construction, this method makes it
  // initialized. Can be used to simulate delayed initialization.
  void InitializeToken();
  // If KcerToken was not initialized on construction, this method simulates
  // initialization failure.
  void FailTokenInitialization();

  // Returns a weak pointer to the token that can be used to post requests for
  // it. The pointer should only be dereferenced on the IO thread.
  base::WeakPtr<internal::KcerToken> GetWeakPtr() { return weak_ptr_; }

  uint32_t GetSlotId();

 private:
  void Initialize(Token token,
                  HighLevelChapsClient* chaps_client,
                  bool initialize,
                  crypto::ScopedPK11Slot nss_slot);

  base::WeakPtr<internal::KcerToken> weak_ptr_;
  std::unique_ptr<internal::KcerTokenImplNss> io_token_;
  crypto::ScopedTestNSSDB nss_db_;
  crypto::ScopedPK11Slot nss_slot_;
  bool is_initialized_ = false;
};

//==============================================================================

// A test helper class that creates and initializes a Kcer instances with a user
// and device NSS slots. In the current implementation the HighLevelChapsClient
// is not configured, so certain functionality will not work (mainly PKCS#12
// import).
class TestKcerHolder {
 public:
  // nullptr can be passed for any/all slots to emulate that Kcer doesn't have
  // access to them.
  TestKcerHolder(PK11SlotInfo* user_slot, PK11SlotInfo* device_slot);
  ~TestKcerHolder();

  base::WeakPtr<Kcer> GetKcer();

 private:
  TokenHolder user_token_;
  TokenHolder device_token_;
  std::unique_ptr<kcer::internal::KcerImpl> kcer_;
};

//==============================================================================

// Compares two KerPermissions, returns true if they are equal.
bool ExpectKeyPermissionsEqual(const std::optional<chaps::KeyPermissions>& a,
                               const std::optional<chaps::KeyPermissions>& b);

// Verifies `signature` created with `signing_scheme` and the public key from
// `spki` for `data_to_sign`. By default (with `strict` == true) only returns
// true if the signature is correct. With `strict` == false, silently ignores
// schemes for which the verification is not implemented yet and also returns
// true for them. Returns false if signature is incorrect.
bool VerifySignature(SigningScheme signing_scheme,
                     PublicKeySpki spki,
                     DataToSign data_to_sign,
                     Signature signature,
                     bool strict = true);

// Returns |hash| prefixed with DER-encoded PKCS#1 DigestInfo with
// AlgorithmIdentifier=id-sha256.
// This is useful for testing Kcer::SignRsaPkcs1Raw which only
// appends PKCS#1 v1.5 padding before signing.
std::vector<uint8_t> PrependSHA256DigestInfo(base::span<const uint8_t> hash);

// Reads a file in the PEM format, decodes it, returns the content of the first
// PEM block in the DER format. Currently supports CERTIFICATE and PRIVATE KEY
// block types.
std::optional<std::vector<uint8_t>> ReadPemFileReturnDer(
    const base::FilePath& path);

// Can be used together with MakeCertBuilder().
std::unique_ptr<net::CertBuilder> MakeCertIssuer();

// Creates a certificate builder that can generate a self-signed certificate for
// the `public_key`. Requires an `issuer` that can be created using
// MakeCertIssuer().
std::unique_ptr<net::CertBuilder> MakeCertBuilder(
    net::CertBuilder* issuer,
    const std::vector<uint8_t>& public_key);

// Reads a file with the `file_name` from net::GetTestCertsDirectory()
// directory.
std::vector<uint8_t> ReadTestFile(const std::string& file_name);

// Reads the key and the cert from disk and imports them into Kcer.
base::expected<KeyAndCert, Error> ImportTestKeyAndCert(
    base::WeakPtr<Kcer> kcer,
    Token token,
    std::string_view key_filename,
    std::string_view cert_filename);

}  // namespace kcer

#endif  // CHROMEOS_COMPONENTS_KCER_KCER_NSS_TEST_UTILS_H_