Ctrl K
ComunidadGuías…

server/src/crypto.h

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/**
 * @file src/crypto.h
 * @brief Declarations for cryptography functions.
 */
#pragma once

// standard includes
#include <array>
#include <cstdint>
#include <optional>
#include <unordered_map>

// lib includes
#include <list>
#include <nlohmann/json.hpp>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include <openssl/x509.h>

// local includes
#include "utility.h"

namespace crypto {
  struct creds_t {
    std::string x509;
    std::string pkey;
  };

  void md_ctx_destroy(EVP_MD_CTX *);

  using sha256_t = std::array<std::uint8_t, SHA256_DIGEST_LENGTH>;

  using aes_t = std::vector<std::uint8_t>;
  using x509_t = util::safe_ptr<X509, X509_free>;
  using x509_store_t = util::safe_ptr<X509_STORE, X509_STORE_free>;
  using x509_store_ctx_t = util::safe_ptr<X509_STORE_CTX, X509_STORE_CTX_free>;
  using cipher_ctx_t = util::safe_ptr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free>;
  using md_ctx_t = util::safe_ptr<EVP_MD_CTX, md_ctx_destroy>;
  using bio_t = util::safe_ptr<BIO, BIO_free_all>;
  using pkey_t = util::safe_ptr<EVP_PKEY, EVP_PKEY_free>;
  using pkey_ctx_t = util::safe_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_free>;
  using bignum_t = util::safe_ptr<BIGNUM, BN_free>;

  /**
   * @brief The permissions of a client.
   */
  enum class PERM : uint32_t {
    _reserved = 1,

    _input = _reserved << 8,  // Input permission group
    input_controller = _input << 0,  // Allow controller input
    input_touch = _input << 1,  // Allow touch input
    input_pen = _input << 2,  // Allow pen input
    input_mouse = _input << 3,  // Allow mouse input
    input_kbd = _input << 4,  // Allow keyboard input
    _all_inputs = input_controller | input_touch | input_pen | input_mouse | input_kbd,

    _operation = _input << 8,  // Operation permission group
    clipboard_set = _operation << 0,  // Allow set clipboard from client
    clipboard_read = _operation << 1,  // Allow read clipboard from host
    file_upload = _operation << 2,  // Allow upload files to host
    file_dwnload = _operation << 3,  // Allow download files from host
    server_cmd = _operation << 4,  // Allow execute server cmd
    _all_opeiations = clipboard_set | clipboard_read | file_upload | file_dwnload | server_cmd,

    _action = _operation << 8,  // Action permission group
    list = _action << 0,  // Allow list apps
    view = _action << 1,  // Allow view streams
    launch = _action << 2,  // Allow launch apps
    _allow_view = view | launch,  // If no view permission is granted, disconnect the device upon permission update
    _all_actions = list | view | launch,

    _default = view | list,  // Default permissions for new clients
    _no = 0,  // No permissions are granted
    _all = _all_inputs | _all_opeiations | _all_actions,  // All current permissions
  };

  inline constexpr PERM
    operator&(PERM x, PERM y) {
    return static_cast<PERM>(static_cast<uint32_t>(x) & static_cast<uint32_t>(y));
  }

  inline constexpr bool
    operator!(PERM p) {
    return static_cast<uint32_t>(p) == 0;
  }

  struct command_entry_t {
    std::string cmd;
    bool elevated;

    // Serialize method using nlohmann::json
    static inline nlohmann::json serialize(const command_entry_t &entry) {
      nlohmann::json node;
      node["cmd"] = entry.cmd;
      node["elevated"] = entry.elevated;
      return node;
    }
  };

  struct named_cert_t {
    std::string name;
    std::string uuid;
    std::string cert;
    std::string display_mode;
    std::string hdr_profile;
    std::string output_name_override;
    std::string virtual_display_mode_override;
    std::string virtual_display_layout_override;
    std::list<command_entry_t> do_cmds;
    std::list<command_entry_t> undo_cmds;
    PERM perm;
    bool enable_legacy_ordering;
    bool allow_client_commands;
    bool always_use_virtual_display;
    // Per-client opt-in to serve HDR requests as 10-bit SDR.
    bool prefer_10bit_sdr = false;
    std::optional<std::int64_t> last_seen;
    std::unordered_map<std::string, std::string> config_overrides;
  };

  using p_named_cert_t = std::shared_ptr<named_cert_t>;

  /**
   * @brief Hashes the given plaintext using SHA-256.
   * @param plaintext
   * @return The SHA-256 hash of the plaintext.
   */
  sha256_t hash(const std::string_view &plaintext);

  aes_t gen_aes_key(const std::array<uint8_t, 16> &salt, const std::string_view &pin);
  x509_t x509(const std::string_view &x);
  pkey_t pkey(const std::string_view &k);
  std::string pem(x509_t &x509);
  std::string pem(pkey_t &pkey);
  std::string subject_name(const X509 *x509);

  std::vector<uint8_t> sign256(const pkey_t &pkey, const std::string_view &data);
  bool verify256(const x509_t &x509, const std::string_view &data, const std::string_view &signature);

  creds_t gen_creds(const std::string_view &cn, std::uint32_t key_bits);

  std::string_view signature(const X509 *x);
  std::string_view signature(const x509_t &x);

  std::string rand(std::size_t bytes);
  std::string rand_alphabet(std::size_t bytes, const std::string_view &alphabet = std::string_view {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!%&()=-"});

  class cert_chain_t {
  public:
    KITTY_DECL_CONSTR(cert_chain_t)

    void add(p_named_cert_t &named_cert_p);

    void clear();

    const char *verify(x509_t::element_type *cert, p_named_cert_t &named_cert_out);

  private:
    std::vector<std::pair<p_named_cert_t, x509_store_t>> _certs;
    x509_store_ctx_t _cert_ctx;
  };

  namespace cipher {
    constexpr std::size_t tag_size = 16;

    constexpr std::size_t round_to_pkcs7_padded(std::size_t size) {
      return ((size + 15) / 16) * 16;
    }

    class cipher_t {
    public:
      cipher_ctx_t decrypt_ctx;
      cipher_ctx_t encrypt_ctx;

      aes_t key;

      bool padding;
    };

    class ecb_t: public cipher_t {
    public:
      ecb_t() = default;
      ecb_t(ecb_t &&) noexcept = default;
      ecb_t &operator=(ecb_t &&) noexcept = default;

      ecb_t(const aes_t &key, bool padding = true);

      int encrypt(const std::string_view &plaintext, std::vector<std::uint8_t> &cipher);
      int decrypt(const std::string_view &cipher, std::vector<std::uint8_t> &plaintext);
    };

    class gcm_t: public cipher_t {
    public:
      gcm_t() = default;
      gcm_t(gcm_t &&) noexcept = default;
      gcm_t &operator=(gcm_t &&) noexcept = default;

      gcm_t(const crypto::aes_t &key, bool padding = true);

      /**
       * @brief Encrypts the plaintext using AES GCM mode.
       * @param plaintext The plaintext data to be encrypted.
       * @param tag The buffer where the GCM tag will be written.
       * @param ciphertext The buffer where the resulting ciphertext will be written.
       * @param iv The initialization vector to be used for the encryption.
       * @return The total length of the ciphertext and GCM tag. Returns -1 in case of an error.
       */
      int encrypt(const std::string_view &plaintext, std::uint8_t *tag, std::uint8_t *ciphertext, aes_t *iv);

      /**
       * @brief Encrypts the plaintext using AES GCM mode.
       * length of cipher must be at least: round_to_pkcs7_padded(plaintext.size()) + crypto::cipher::tag_size
       * @param plaintext The plaintext data to be encrypted.
       * @param tagged_cipher The buffer where the resulting ciphertext and GCM tag will be written.
       * @param iv The initialization vector to be used for the encryption.
       * @return The total length of the ciphertext and GCM tag written into tagged_cipher. Returns -1 in case of an error.
       */
      int encrypt(const std::string_view &plaintext, std::uint8_t *tagged_cipher, aes_t *iv);

      int decrypt(const std::string_view &cipher, std::vector<std::uint8_t> &plaintext, aes_t *iv);
    };

    class cbc_t: public cipher_t {
    public:
      cbc_t() = default;
      cbc_t(cbc_t &&) noexcept = default;
      cbc_t &operator=(cbc_t &&) noexcept = default;

      cbc_t(const crypto::aes_t &key, bool padding = true);

      /**
       * @brief Encrypts the plaintext using AES CBC mode.
       * length of cipher must be at least: round_to_pkcs7_padded(plaintext.size())
       * @param plaintext The plaintext data to be encrypted.
       * @param cipher The buffer where the resulting ciphertext will be written.
       * @param iv The initialization vector to be used for the encryption.
       * @return The total length of the ciphertext written into cipher. Returns -1 in case of an error.
       */
      int encrypt(const std::string_view &plaintext, std::uint8_t *cipher, aes_t *iv);
    };
  }  // namespace cipher
}  // namespace crypto