<commit_before>/*
 * This file is part of tpm2-pk11.
 * Copyright (C) 2017 <NAME>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "pk11.h"

#include "config.h"
#include "sessions.h"
#include "utils.h"
#include "tpm.h"
#include "object.h"
#include "log.h"

#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
#include <endian.h>

#define SLOT_ID 0x1234

#define get_session(x) ((struct session*) x)

static struct config pk11_config = {0};
static struct session main_session;

CK_RV C_GetInfo(CK_INFO_PTR info) {
  print_log(VERBOSE, "C_GetInfo");
  info->cryptokiVersion.major = CRYPTOKI_VERSION_MAJOR;
  info->cryptokiVersion.minor = CRYPTOKI_VERSION_MINOR;
  strncpy_pad(info->manufacturerID, sizeof(info->manufacturerID), TPM2_PK11_MANUFACTURER, sizeof(info->manufacturerID));
  strncpy_pad(info->libraryDescription, sizeof(info->libraryDescription), TPM2_PK11_LIBRARY_DESCRIPTION, sizeof(info->libraryDescription));
  info->flags = 0;

  return CKR_OK;
}

CK_RV C_GetSlotList(CK_BBOOL present, CK_SLOT_ID_PTR list, CK_ULONG_PTR count) {
  print_log(VERBOSE, "C_GetSlotList: present = %s", present ? "true" : "false");
  if (*count && list)
    *list = SLOT_ID;

  *count = 1;

  return CKR_OK;
}

CK_RV C_OpenSession(CK_SLOT_ID id, CK_FLAGS flags, CK_VOID_PTR application, CK_NOTIFY notify, CK_SESSION_HANDLE_PTR session) {
  print_log(VERBOSE, "C_OpenSession: id = %d, flags = %x", id, flags);
  *session = (unsigned long) malloc(sizeof(struct session));
  if ((void*) *session == NULL)
    return CKR_GENERAL_ERROR;

  int ret = session_init((struct session*) *session, &pk11_config);

  return ret != 0 ? CKR_GENERAL_ERROR : CKR_OK;
}

CK_RV C_CloseSession(CK_SESSION_HANDLE session_handle) {
  print_log(VERBOSE, "C_CloseSession: session = %x", session_handle);
  session_close(get_session(session_handle));
  free(get_session(session_handle));
  return CKR_OK;
}

CK_RV C_GetSessionInfo(CK_SESSION_HANDLE session_handle, CK_SESSION_INFO_PTR info) {
  print_log(VERBOSE, "C_GetSessionInfo: session = %x", session_handle);
  info->slotID = 0;
  info->state = CKS_RO_USER_FUNCTIONS;
  info->flags = CKF_SERIAL_SESSION;
  info->ulDeviceError = 0;
  return CKR_OK;
}

CK_RV C_GetSlotInfo(CK_SLOT_ID id, CK_SLOT_INFO_PTR info) {
  print_log(VERBOSE, "C_GetSlotInfo: id = %d", id);
  TPMS_CAPABILITY_DATA fixed;
  if (tpm_info(main_session.context, TPM2_PT_FIXED, &fixed) != TPM2_RC_SUCCESS)
    return CKR_DEVICE_ERROR;

  TPML_TAGGED_TPM_PROPERTY props = fixed.data.tpmProperties;
  TPMS_TAGGED_PROPERTY* manufacturer = tpm_info_get(props.tpmProperty, props.count, TPM2_PT_MANUFACTURER);
  UINT32 manufacturer_val = manufacturer ? htobe32(manufacturer->value) : 0;
  strncpy_pad(info->manufacturerID, sizeof(info->manufacturerID), manufacturer ? (char*) &manufacturer_val : TPM2_PK11_MANUFACTURER, manufacturer ? 4 : sizeof(info->manufacturerID));
  strncpy_pad(info->slotDescription, sizeof(info->slotDescription), TPM2_PK11_SLOT_DESCRIPTION, sizeof(info->slotDescription));

  info->flags = CKF_TOKEN_PRESENT | CKF_HW_SLOT;
  TPMS_TAGGED_PROPERTY* revision = tpm_info_get(props.tpmProperty, props.count, TPM2_PT_REVISION);
  info->hardwareVersion.major = revision ? revision->value / 100 : 0;
  info->hardwareVersion.minor = revision ? revision->value % 100 : 0;
  TPMS_TAGGED_PROPERTY* major = tpm_info_get(props.tpmProperty, props.count, TPM2_PT_FIRMWARE_VERSION_1);
  info->firmwareVersion.major = major ? major->value : 0;
  TPMS_TAGGED_PROPERTY* minor = tpm_info_get(props.tpmProperty, props.count, TPM2_PT_FIRMWARE_VERSION_2);
  info->firmwareVersion.minor = major ? major->value : 0;
  return CKR_OK;
}

CK_RV C_GetTokenInfo(CK_SLOT_ID id, CK_TOKEN_INFO_PTR info) {
  print_log(VERBOSE, "C_GetTokenInfo: id = %d", id);
  TPMS_CAPABILITY_DATA fixed;
  if (tpm_info(main_session.context, TPM2_PT_FIXED, &fixed) != TPM2_RC_SUCCESS)
    return CKR_DEVICE_ERROR;

  TPML_TAGGED_TPM_PROPERTY props = fixed.data.tpmProperties;
  strncpy_pad(info->label, sizeof(info->label), TPM2_PK11_EMPTY, sizeof(info->label));
  TPMS_TAGGED_PROPERTY* manufacturer = tpm_info_get(props.tpmProperty, props.count, TPM2_PT_MANUFACTURER);
  UINT32 manufacturer_val = manufacturer ? htobe32(manufacturer->value) : 0;
  strncpy_pad(info->manufacturerID, sizeof(info->manufacturerID), manufacturer ? (char*) &manufacturer_val : TPM2_PK11_MANUFACTURER, manufacturer ? 4 : sizeof(info->manufacturerID));
  strncpy_pad(info->model, sizeof(info->label), TPM2_PK11_MODEL, sizeof(info->label));
  strncpy_pad(info->serialNumber, sizeof(info->serialNumber), TPM2_PK11_SERIAL, sizeof(info->serialNumber));
  strncpy_pad(info->utcTime, sizeof(info->utcTime), "", sizeof(info->utcTime));

  info->flags = CKF_TOKEN_INITIALIZED | CKF_WRITE_PROTECTED;
  TPMS_TAGGED_PROPERTY* max_sessions = tpm_info_get(props.tpmProperty, props.count, TPM2_PT_ACTIVE_SESSIONS_MAX);
  info->ulMaxSessionCount = max_sessions ? max_sessions->value : CK_EFFECTIVELY_INFINITE;
  info->ulSessionCount = open_sessions;
  info->ulMaxRwSessionCount = max_sessions ? max_sessions->value : CK_EFFECTIVELY_INFINITE;
  info->ulRwSessionCount = 0;
  info->ulMaxPinLen = 64;
  info->ulMinPinLen = 0;
  info->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION;
  info->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION;
  info->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION;
  info->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION;
  TPMS_TAGGED_PROPERTY* revision = tpm_info_get(props.tpmProperty, props.count, TPM2_PT_REVISION);
  info->hardwareVersion.major = revision ? revision->value / 100 : 0;
  info->hardwareVersion.minor = revision ? revision->value % 100 : 0;
  TPMS_TAGGED_PROPERTY* major = tpm_info_get(props.tpmProperty, props.count, TPM2_PT_FIRMWARE_VERSION_1);
  info->firmwareVersion.major = major ? major->value : 0;
  TPMS_TAGGED_PROPERTY* minor = tpm_info_get(props.tpmProperty, props.count, TPM2_PT_FIRMWARE_VERSION_2);
  info->firmwareVersion.minor = major ? major->value : 0;

  return CKR_OK;
}

CK_RV C_Finalize(CK_VOID_PTR reserved) {
  print_log(VERBOSE, "C_Finalize");
  session_close(&main_session);
  return CKR_OK;
}

CK_RV C_FindObjectsInit(CK_SESSION_HANDLE session_handle, CK_ATTRIBUTE_PTR filters, CK_ULONG count) {
  print_log(VERBOSE, "C_FindObjectsInit: session = %x, count = %d", session_handle, count);
  struct session *session = get_session(session_handle);
  session->find_cursor = session->objects;
  session->filters = filters;
  session->num_filters = count;
  return CKR_OK;
}

CK_RV C_FindObjects(CK_SESSION_HANDLE session_handle, CK_OBJECT_HANDLE_PTR object_handle, CK_ULONG max_objects, CK_ULONG_PTR found) {
  print_log(VERBOSE, "C_FindObjects: session = %x, max = %d", session_handle, max_objects);
  TPMS_CAPABILITY_DATA persistent;
  tpm_info(get_session(session_handle)->context, TPM2_HT_PERSISTENT, &persistent);
  struct session* session = get_session(session_handle);
  *found = 0;

  while (session->find_cursor != NULL && *found < max_objects) {
    pObject object = session->find_cursor->object;
    bool filtered = false;
    for (int j = 0; j < session->num_filters; j++) {
      size_t size = 0;
      void* value = attr_get(object, session->filters[j].type, &size);
      if (session->filters[j].ulValueLen != size || memcmp(session->filters[j].pValue, value, size) != 0) {
        filtered = true;
        break;
      }
    }
    if (!filtered) {
      object_handle[*found] = (CK_OBJECT_HANDLE) session->find_cursor->object;
      (*found)++;
    }
    session->find_cursor = session->find_cursor->next;
  }

  return CKR_OK;
}

CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE session_handle) {
  print_log(VERBOSE, "C_FindObjectsFinal: session = %x", session_handle);
  return CKR_OK;
}

CK_RV C_GetAttributeValue(CK_SESSION_HANDLE session_handle, CK_OBJECT_HANDLE object_handle, CK_ATTRIBUTE_PTR template, CK_ULONG count) {
  print_log(VERBOSE, "C_GetAttributeValue: session = %x, object = %x, count = %d", session_handle, object_handle, count);
  pObject object = (pObject) object_handle;

  for (int i = 0; i < count; i++) {
    void* entry_obj = NULL;
    pAttrIndex entry = NULL;
    for (int j = 0; j < object->num_entries; j++) {
      void *obj = object->entries[j].object;
      pAttrIndex index = object->entries[j].indexes;
      for (int k = 0; k < object->entries[j].num_attrs; k++) {
        if (template[i].type == index[k].type) {
          entry = &index[k];
          entry_obj = obj;
          continue;
        }
      }
      if (entry)
        continue;
    }
    if (!entry) {
      print_log(DEBUG, " attribute not found: type = %x", template[i].type);
      template[i].ulValueLen = 0;
    } else if (entry->size_offset == 0) {
      print_log(DEBUG, " return attribute: type = %x, size = %d, buffer_size = %d", template[i].type, entry->size, template[i].ulValueLen);
      retmem(template[i].pValue, &template[i].ulValueLen, entry_obj + entry->offset, entry->size);
    } else {
      print_log(DEBUG, " return attribute: type = %x, size = %d, buffer_size = %d", template[i].type, *((size_t*) (entry_obj + entry->size_offset)), template[i].ulValueLen);
      retmem(template[i].pValue, &template[i].ulValueLen, *((void**) (entry_obj + entry->offset)), *((size_t*) (entry_obj + entry->size_offset)));
    }
  }

  return CKR_OK;
}

CK_RV C_SignInit(CK_SESSION_HANDLE session_handle, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) {
  print_log(VERBOSE, "C_SignInit: session = %x, key = %x", session_handle, key);
  pObject object = (pObject) key;
  struct session* session = get_session(session_handle);
  session->key_handle = object->tpm_handle;
  session->current_object = object;
  return CKR_OK;
}

CK_RV C_Sign(CK_SESSION_HANDLE session_handle, CK_BYTE_PTR data, CK_ULONG data_len, CK_BYTE_PTR signature, CK_ULONG_PTR signature_len) {
  print_log(VERBOSE, "C_Sign: session = %x, len = %d", session_handle, data_len);
  struct session* session = get_session(session_handle);
  TPM2_RC ret;

  if (pk11_config.sign_using_encrypt) {
    TPM2B_PUBLIC_KEY_RSA message = { .size = TPM2_MAX_RSA_KEY_BYTES };
    pObject object = session->current_object->opposite;
    CK_ULONG_PTR key_size = (CK_ULONG_PTR) attr_get(object, CKA_MODULUS_BITS, NULL);
    ret = tpm_sign_encrypt(session->context, session->key_handle, *key_size / 8, data, data_len, &message);
    retmem(signature, signature_len, message.buffer, message.size);
  } else {
    TPMT_SIGNATURE sign = {0};
    ret = tpm_sign(session->context, session->key_handle, data, data_len, &sign);
    retmem(signature, signature_len, sign.signature.rsassa.sig.buffer, sign.signature.rsassa.sig.size);
  }

  return ret == TPM2_RC_SUCCESS ? CKR_OK : CKR_GENERAL_ERROR;
}

CK_RV C_DecryptInit(CK_SESSION_HANDLE session_handle, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) {
  print_log(VERBOSE, "C_DecryptInit: session = %x, key = %x", session_handle, key);
  pObject object = (pObject) key;
  get_session(session_handle)->key_handle = object->tpm_handle;
  return CKR_OK;
}

CK_RV C_Decrypt(CK_SESSION_HANDLE session_handle, CK_BYTE_PTR enc_data, CK_ULONG enc_data_len, CK_BYTE_PTR data, CK_ULONG_PTR data_len) {
  print_log(VERBOSE, "C_Decrypt: session = %x, len = %d", session_handle, enc_data_len);
  TPM2B_PUBLIC_KEY_RSA message = { .size = TPM2_MAX_RSA_KEY_BYTES };
  struct session* session = get_session(session_handle);
  TPM2_RC ret = tpm_decrypt(session->context, session->key_handle, enc_data, enc_data_len, &message);
  retmem(data, data_len, message.buffer, message.size);

  return ret == TPM2_RC_SUCCESS ? CKR_OK : CKR_GENERAL_ERROR;
}

CK_RV C_Initialize(CK_VOID_PTR pInitArgs) {
  print_log(VERBOSE, "C_Initialize");
  char configfile_path[256];
  snprintf(configfile_path, sizeof(configfile_path), "%s/" TPM2_PK11_CONFIG_DIR "/" TPM2_PK11_CONFIG_FILE, getenv("HOME"));
  if (config_load(configfile_path, &pk11_config) < 0)
    return CKR_GENERAL_ERROR;

  session_init(&main_session, &pk11_config);
  log_init(pk11_config.log_file, pk11_config.log_level);
  return CKR_OK;
}

/* Stubs for not yet supported functions*/
CK_RV C_GetMechanismList(CK_SLOT_ID id, CK_MECHANISM_TYPE_PTR list, CK_ULONG_PTR count) {
  print_log(VERBOSE, "C_GetMechanismList: slot = %d", id);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_GetMechanismInfo (CK_SLOT_ID id, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR info) {
  print_log(VERBOSE, "C_GetMechanismInfo: slot = %d", id);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_InitToken (CK_SLOT_ID id, CK_CHAR_PTR pin, CK_ULONG pin_len, CK_CHAR_PTR label) {
  print_log(VERBOSE, "C_InitToken: slot = %d", id);<commit_msg>Add minimal C_GetMechanismList implementation<commit_after>/*
 * This file is part of tpm2-pk11.
 * Copyright (C) 2017 <NAME>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "pk11.h"

#include "config.h"
#include "sessions.h"
#include "utils.h"
#include "tpm.h"
#include "object.h"
#include "log.h"

#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
#include <endian.h>

#define SLOT_ID 0x1234

#define get_session(x) ((struct session*) x)

static const CK_MECHANISM_TYPE supported_mechanisms[] = {
  CKM_RSA_PKCS,
};

static struct config pk11_config = {0};
static struct session main_session;

CK_RV C_GetInfo(CK_INFO_PTR info) {
  print_log(VERBOSE, "C_GetInfo");
  info->cryptokiVersion.major = CRYPTOKI_VERSION_MAJOR;
  info->cryptokiVersion.minor = CRYPTOKI_VERSION_MINOR;
  strncpy_pad(info->manufacturerID, sizeof(info->manufacturerID), TPM2_PK11_MANUFACTURER, sizeof(info->manufacturerID));
  strncpy_pad(info->libraryDescription, sizeof(info->libraryDescription), TPM2_PK11_LIBRARY_DESCRIPTION, sizeof(info->libraryDescription));
  info->flags = 0;

  return CKR_OK;
}

CK_RV C_GetSlotList(CK_BBOOL present, CK_SLOT_ID_PTR list, CK_ULONG_PTR count) {
  print_log(VERBOSE, "C_GetSlotList: present = %s", present ? "true" : "false");
  if (*count && list)
    *list = SLOT_ID;

  *count = 1;

  return CKR_OK;
}

CK_RV C_OpenSession(CK_SLOT_ID id, CK_FLAGS flags, CK_VOID_PTR application, CK_NOTIFY notify, CK_SESSION_HANDLE_PTR session) {
  print_log(VERBOSE, "C_OpenSession: id = %d, flags = %x", id, flags);
  *session = (unsigned long) malloc(sizeof(struct session));
  if ((void*) *session == NULL)
    return CKR_GENERAL_ERROR;

  int ret = session_init((struct session*) *session, &pk11_config);

  return ret != 0 ? CKR_GENERAL_ERROR : CKR_OK;
}

CK_RV C_CloseSession(CK_SESSION_HANDLE session_handle) {
  print_log(VERBOSE, "C_CloseSession: session = %x", session_handle);
  session_close(get_session(session_handle));
  free(get_session(session_handle));
  return CKR_OK;
}

CK_RV C_GetSessionInfo(CK_SESSION_HANDLE session_handle, CK_SESSION_INFO_PTR info) {
  print_log(VERBOSE, "C_GetSessionInfo: session = %x", session_handle);
  info->slotID = 0;
  info->state = CKS_RO_USER_FUNCTIONS;
  info->flags = CKF_SERIAL_SESSION;
  info->ulDeviceError = 0;
  return CKR_OK;
}

CK_RV C_GetSlotInfo(CK_SLOT_ID id, CK_SLOT_INFO_PTR info) {
  print_log(VERBOSE, "C_GetSlotInfo: id = %d", id);
  TPMS_CAPABILITY_DATA fixed;
  if (tpm_info(main_session.context, TPM2_PT_FIXED, &fixed) != TPM2_RC_SUCCESS)
    return CKR_DEVICE_ERROR;

  TPML_TAGGED_TPM_PROPERTY props = fixed.data.tpmProperties;
  TPMS_TAGGED_PROPERTY* manufacturer = tpm_info_get(props.tpmProperty, props.count, TPM2_PT_MANUFACTURER);
  UINT32 manufacturer_val = manufacturer ? htobe32(manufacturer->value) : 0;
  strncpy_pad(info->manufacturerID, sizeof(info->manufacturerID), manufacturer ? (char*) &manufacturer_val : TPM2_PK11_MANUFACTURER, manufacturer ? 4 : sizeof(info->manufacturerID));
  strncpy_pad(info->slotDescription, sizeof(info->slotDescription), TPM2_PK11_SLOT_DESCRIPTION, sizeof(info->slotDescription));

  info->flags = CKF_TOKEN_PRESENT | CKF_HW_SLOT;
  TPMS_TAGGED_PROPERTY* revision = tpm_info_get(props.tpmProperty, props.count, TPM2_PT_REVISION);
  info->hardwareVersion.major = revision ? revision->value / 100 : 0;
  info->hardwareVersion.minor = revision ? revision->value % 100 : 0;
  TPMS_TAGGED_PROPERTY* major = tpm_info_get(props.tpmProperty, props.count, TPM2_PT_FIRMWARE_VERSION_1);
  info->firmwareVersion.major = major ? major->value : 0;
  TPMS_TAGGED_PROPERTY* minor = tpm_info_get(props.tpmProperty, props.count, TPM2_PT_FIRMWARE_VERSION_2);
  info->firmwareVersion.minor = major ? major->value : 0;
  return CKR_OK;
}

CK_RV C_GetTokenInfo(CK_SLOT_ID id, CK_TOKEN_INFO_PTR info) {
  print_log(VERBOSE, "C_GetTokenInfo: id = %d", id);
  TPMS_CAPABILITY_DATA fixed;
  if (tpm_info(main_session.context, TPM2_PT_FIXED, &fixed) != TPM2_RC_SUCCESS)
    return CKR_DEVICE_ERROR;

  TPML_TAGGED_TPM_PROPERTY props = fixed.data.tpmProperties;
  strncpy_pad(info->label, sizeof(info->label), TPM2_PK11_EMPTY, sizeof(info->label));
  TPMS_TAGGED_PROPERTY* manufacturer = tpm_info_get(props.tpmProperty, props.count, TPM2_PT_MANUFACTURER);
  UINT32 manufacturer_val = manufacturer ? htobe32(manufacturer->value) : 0;
  strncpy_pad(info->manufacturerID, sizeof(info->manufacturerID), manufacturer ? (char*) &manufacturer_val : TPM2_PK11_MANUFACTURER, manufacturer ? 4 : sizeof(info->manufacturerID));
  strncpy_pad(info->model, sizeof(info->label), TPM2_PK11_MODEL, sizeof(info->label));
  strncpy_pad(info->serialNumber, sizeof(info->serialNumber), TPM2_PK11_SERIAL, sizeof(info->serialNumber));
  strncpy_pad(info->utcTime, sizeof(info->utcTime), "", sizeof(info->utcTime));

  info->flags = CKF_TOKEN_INITIALIZED | CKF_WRITE_PROTECTED;
  TPMS_TAGGED_PROPERTY* max_sessions = tpm_info_get(props.tpmProperty, props.count, TPM2_PT_ACTIVE_SESSIONS_MAX);
  info->ulMaxSessionCount = max_sessions ? max_sessions->value : CK_EFFECTIVELY_INFINITE;
  info->ulSessionCount = open_sessions;
  info->ulMaxRwSessionCount = max_sessions ? max_sessions->value : CK_EFFECTIVELY_INFINITE;
  info->ulRwSessionCount = 0;
  info->ulMaxPinLen = 64;
  info->ulMinPinLen = 0;
  info->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION;
  info->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION;
  info->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION;
  info->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION;
  TPMS_TAGGED_PROPERTY* revision = tpm_info_get(props.tpmProperty, props.count, TPM2_PT_REVISION);
  info->hardwareVersion.major = revision ? revision->value / 100 : 0;
  info->hardwareVersion.minor = revision ? revision->value % 100 : 0;
  TPMS_TAGGED_PROPERTY* major = tpm_info_get(props.tpmProperty, props.count, TPM2_PT_FIRMWARE_VERSION_1);
  info->firmwareVersion.major = major ? major->value : 0;
  TPMS_TAGGED_PROPERTY* minor = tpm_info_get(props.tpmProperty, props.count, TPM2_PT_FIRMWARE_VERSION_2);
  info->firmwareVersion.minor = major ? major->value : 0;

  return CKR_OK;
}

CK_RV C_Finalize(CK_VOID_PTR reserved) {
  print_log(VERBOSE, "C_Finalize");
  session_close(&main_session);
  return CKR_OK;
}

CK_RV C_FindObjectsInit(CK_SESSION_HANDLE session_handle, CK_ATTRIBUTE_PTR filters, CK_ULONG count) {
  print_log(VERBOSE, "C_FindObjectsInit: session = %x, count = %d", session_handle, count);
  struct session *session = get_session(session_handle);
  session->find_cursor = session->objects;
  session->filters = filters;
  session->num_filters = count;
  return CKR_OK;
}

CK_RV C_FindObjects(CK_SESSION_HANDLE session_handle, CK_OBJECT_HANDLE_PTR object_handle, CK_ULONG max_objects, CK_ULONG_PTR found) {
  print_log(VERBOSE, "C_FindObjects: session = %x, max = %d", session_handle, max_objects);
  TPMS_CAPABILITY_DATA persistent;
  tpm_info(get_session(session_handle)->context, TPM2_HT_PERSISTENT, &persistent);
  struct session* session = get_session(session_handle);
  *found = 0;

  while (session->find_cursor != NULL && *found < max_objects) {
    pObject object = session->find_cursor->object;
    bool filtered = false;
    for (int j = 0; j < session->num_filters; j++) {
      size_t size = 0;
      void* value = attr_get(object, session->filters[j].type, &size);
      if (session->filters[j].ulValueLen != size || memcmp(session->filters[j].pValue, value, size) != 0) {
        filtered = true;
        break;
      }
    }
    if (!filtered) {
      object_handle[*found] = (CK_OBJECT_HANDLE) session->find_cursor->object;
      (*found)++;
    }
    session->find_cursor = session->find_cursor->next;
  }

  return CKR_OK;
}

CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE session_handle) {
  print_log(VERBOSE, "C_FindObjectsFinal: session = %x", session_handle);
  return CKR_OK;
}

CK_RV C_GetAttributeValue(CK_SESSION_HANDLE session_handle, CK_OBJECT_HANDLE object_handle, CK_ATTRIBUTE_PTR template, CK_ULONG count) {
  print_log(VERBOSE, "C_GetAttributeValue: session = %x, object = %x, count = %d", session_handle, object_handle, count);
  pObject object = (pObject) object_handle;

  for (int i = 0; i < count; i++) {
    void* entry_obj = NULL;
    pAttrIndex entry = NULL;
    for (int j = 0; j < object->num_entries; j++) {
      void *obj = object->entries[j].object;
      pAttrIndex index = object->entries[j].indexes;
      for (int k = 0; k < object->entries[j].num_attrs; k++) {
        if (template[i].type == index[k].type) {
          entry = &index[k];
          entry_obj = obj;
          continue;
        }
      }
      if (entry)
        continue;
    }
    if (!entry) {
      print_log(DEBUG, " attribute not found: type = %x", template[i].type);
      template[i].ulValueLen = 0;
    } else if (entry->size_offset == 0) {
      print_log(DEBUG, " return attribute: type = %x, size = %d, buffer_size = %d", template[i].type, entry->size, template[i].ulValueLen);
      retmem(template[i].pValue, &template[i].ulValueLen, entry_obj + entry->offset, entry->size);
    } else {
      print_log(DEBUG, " return attribute: type = %x, size = %d, buffer_size = %d", template[i].type, *((size_t*) (entry_obj + entry->size_offset)), template[i].ulValueLen);
      retmem(template[i].pValue, &template[i].ulValueLen, *((void**) (entry_obj + entry->offset)), *((size_t*) (entry_obj + entry->size_offset)));
    }
  }

  return CKR_OK;
}

CK_RV C_SignInit(CK_SESSION_HANDLE session_handle, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) {
  print_log(VERBOSE, "C_SignInit: session = %x, key = %x", session_handle, key);
  pObject object = (pObject) key;
  struct session* session = get_session(session_handle);
  session->key_handle = object->tpm_handle;
  session->current_object = object;
  return CKR_OK;
}

CK_RV C_Sign(CK_SESSION_HANDLE session_handle, CK_BYTE_PTR data, CK_ULONG data_len, CK_BYTE_PTR signature, CK_ULONG_PTR signature_len) {
  print_log(VERBOSE, "C_Sign: session = %x, len = %d", session_handle, data_len);
  struct session* session = get_session(session_handle);
  TPM2_RC ret;

  if (pk11_config.sign_using_encrypt) {
    TPM2B_PUBLIC_KEY_RSA message = { .size = TPM2_MAX_RSA_KEY_BYTES };
    pObject object = session->current_object->opposite;
    CK_ULONG_PTR key_size = (CK_ULONG_PTR) attr_get(object, CKA_MODULUS_BITS, NULL);
    ret = tpm_sign_encrypt(session->context, session->key_handle, *key_size / 8, data, data_len, &message);
    retmem(signature, signature_len, message.buffer, message.size);
  } else {
    TPMT_SIGNATURE sign = {0};
    ret = tpm_sign(session->context, session->key_handle, data, data_len, &sign);
    retmem(signature, signature_len, sign.signature.rsassa.sig.buffer, sign.signature.rsassa.sig.size);
  }

  return ret == TPM2_RC_SUCCESS ? CKR_OK : CKR_GENERAL_ERROR;
}

CK_RV C_DecryptInit(CK_SESSION_HANDLE session_handle, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) {
  print_log(VERBOSE, "C_DecryptInit: session = %x, key = %x", session_handle, key);
  pObject object = (pObject) key;
  get_session(session_handle)->key_handle = object->tpm_handle;
  return CKR_OK;
}

CK_RV C_Decrypt(CK_SESSION_HANDLE session_handle, CK_BYTE_PTR enc_data, CK_ULONG enc_data_len, CK_BYTE_PTR data, CK_ULONG_PTR data_len) {
  print_log(VERBOSE, "C_Decrypt: session = %x, len = %d", session_handle, enc_data_len);
  TPM2B_PUBLIC_KEY_RSA message = { .size = TPM2_MAX_RSA_KEY_BYTES };
  struct session* session = get_session(session_handle);
  TPM2_RC ret = tpm_decrypt(session->context, session->key_handle, enc_data, enc_data_len, &message);
  retmem(data, data_len, message.buffer, message.size);

  return ret == TPM2_RC_SUCCESS ? CKR_OK : CKR_GENERAL_ERROR;
}

CK_RV C_Initialize(CK_VOID_PTR pInitArgs) {
  print_log(VERBOSE, "C_Initialize");
  char configfile_path[256];
  snprintf(configfile_path, sizeof(configfile_path), "%s/" TPM2_PK11_CONFIG_DIR "/" TPM2_PK11_CONFIG_FILE, getenv("HOME"));
  if (config_load(configfile_path, &pk11_config) < 0)
    return CKR_GENERAL_ERROR;

  session_init(&main_session, &pk11_config);
  log_init(pk11_config.log_file, pk11_config.log_level);
  return CKR_OK;
}

/* Stubs for not yet supported functions*/
CK_RV C_GetMechanismList(CK_SLOT_ID id, CK_MECHANISM_TYPE_PTR list, CK_ULONG_PTR count) {
  print_log(VERBOSE, "C_GetMechanismList: slot = %d", id);

  if (count == NULL_PTR)
    return CKR_ARGUMENTS_BAD;

  int countSupported = sizeof(supported_mechanisms) / sizeof(CK_MECHANISM_TYPE);

  if (list != NULL_PTR && *count < countSupported) {
    *count = countSupported;
    return CKR_BUFFER_TOO_SMALL;
  }

  *count = countSupported;
  if (list != NULL_PTR) {
    for (CK_ULONG i = 0; i < countSupported; i++) {
      list[i] = supported_mechanisms[i];
    }
  }

  return CKR_OK;
}

CK_RV C_GetMechanismInfo (CK_SLOT_ID id, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR info) {
  print_log(VERBOSE, "C_GetMechanismInfo: slot = %d", id);
  return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_InitToken (CK_SLOT_ID id, CK_CHAR_PTR pin, CK_ULONG pin_len, CK_CHAR_PTR label) {
  print_log(VERBOSE, "C_InitToken: slot = %d", id);
{'fraction_non_alphanumeric': 0.11770356140091622, 'fraction_numerical': 0.01141569380818679, 'mean_word_length': 5.3467760844079715, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 227, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1937735', 'n_tokens_mistral': 10246, 'n_tokens_neox': 9814, 'n_words': 1987}