| /* |
| * exported pkcs11 functions for the software token |
| * |
| * Copyright (C) 2019 James.Bottomley@HansenPartnership.com |
| * |
| * SPDX-License-Identifier: LGPL-2.1-only |
| */ |
| |
| #include <string.h> |
| #include <stdio.h> |
| |
| #include "openssl-pkcs11.h" |
| |
| /* according to PKCS11 attribute values have to be space padded */ |
| #define min(x, y) ((x) < (y)?(x):(y)) |
| #define ATTRIB(x, y) do { \ |
| memset(x, ' ', sizeof(x)); \ |
| memcpy(x, y, min(strlen(y), sizeof(x))); \ |
| } while(0) |
| |
| static CK_FUNCTION_LIST module_functions; |
| |
| CK_RV |
| C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR list) |
| { |
| if (!list) |
| return CKR_ARGUMENTS_BAD; |
| |
| *list = &module_functions; |
| |
| return CKR_OK; |
| } |
| |
| CK_RV |
| C_Initialize(CK_VOID_PTR args) |
| { |
| if (!args) { |
| /* args communicate threading information, just say we |
| * can't do threading */ |
| return CKR_CANT_LOCK; |
| } |
| parse_ini_file(); |
| |
| return CKR_OK; |
| } |
| |
| CK_RV |
| C_Finalize(CK_VOID_PTR dummy) |
| { |
| free_ini_file(); |
| |
| return CKR_OK; |
| } |
| |
| CK_RV |
| C_GetInfo(CK_INFO_PTR info) |
| { |
| if (!info) |
| return CKR_ARGUMENTS_BAD; |
| |
| memset(info, 0, sizeof(*info)); |
| /* conform to pkcs11 spec version 2.20 */ |
| info->cryptokiVersion.major = 2; |
| info->cryptokiVersion.minor = 20; |
| |
| info->libraryVersion.major = LIBRARY_VERSION_MAJOR; |
| info->libraryVersion.minor = LIBRARY_VERSION_MINOR; |
| info->flags = 0; |
| ATTRIB(info->manufacturerID, |
| cache_get_default(GLOBAL_SECTION, "manufacturer id", |
| PACKAGE_NAME)); |
| ATTRIB(info->libraryDescription, |
| cache_get_default(GLOBAL_SECTION, "library description", |
| PACKAGE_STRING)); |
| |
| return CKR_OK; |
| } |
| |
| /* |
| * Each non global section corresponds to a key slot. We number the |
| * slots from 1...n because slot 0 would be the global section |
| */ |
| CK_RV |
| C_GetSlotList(CK_BBOOL present, CK_SLOT_ID_PTR list, CK_ULONG_PTR count) |
| { |
| int c = cache_get_sections(), i; |
| |
| if (c == 0 || c == 1) { |
| /* no slots can cause a failure so we need to pretend |
| * to have at least one */ |
| if (list) |
| *list = 0; |
| *count = 1; |
| |
| return CKR_OK; |
| } |
| if (list) |
| for (i = 1; i < c; i++) |
| *list++ = i; |
| if (count) |
| *count = c - 1; |
| return CKR_OK; |
| } |
| |
| CK_RV |
| C_GetTokenInfo(CK_SLOT_ID slot, CK_TOKEN_INFO_PTR info) |
| { |
| const char *section, *serial; |
| int count = cache_get_sections(); |
| |
| if (!info) |
| return CKR_ARGUMENTS_BAD; |
| memset(info, 0, sizeof(*info)); |
| if (slot < 1) { |
| /* pretend slot is empty */ |
| return CKR_OK; |
| } else if (slot > count) |
| return CKR_ARGUMENTS_BAD; |
| |
| section = cache_get_section(slot); |
| serial = cache_get_by_secnum(slot, "serial", NULL); |
| |
| ATTRIB(info->manufacturerID, PACKAGE_NAME); |
| ATTRIB(info->model, PACKAGE_VERSION); |
| ATTRIB(info->label, section); |
| ATTRIB(info->serialNumber, serial); |
| |
| info->flags = CKF_LOGIN_REQUIRED|CKF_TOKEN_INITIALIZED|CKF_USER_PIN_INITIALIZED; |
| |
| return CKR_OK; |
| } |
| |
| CK_RV |
| C_GetSlotInfo(CK_SLOT_ID slot, CK_SLOT_INFO_PTR info) |
| { |
| if (!info) |
| return CKR_ARGUMENTS_BAD; |
| memset(info, 0, sizeof(*info)); |
| info->firmwareVersion.major = LIBRARY_VERSION_MAJOR; |
| info->firmwareVersion.minor = LIBRARY_VERSION_MINOR; |
| |
| info->hardwareVersion.major = LIBRARY_VERSION_MAJOR; |
| info->hardwareVersion.minor = LIBRARY_VERSION_MINOR; |
| info->flags = 0; |
| ATTRIB(info->manufacturerID, |
| cache_get_default(GLOBAL_SECTION, "manufacturer id", |
| PACKAGE_NAME)); |
| ATTRIB(info->slotDescription, |
| cache_get_default(GLOBAL_SECTION, "slot description", |
| PACKAGE_STRING)); |
| info->flags = CKF_TOKEN_PRESENT; |
| |
| return CKR_OK; |
| } |
| |
| static int session_count = 0; |
| |
| static int logged_in(CK_SESSION_HANDLE handle) |
| { |
| const char *pkey = cache_get_by_secnum(handle, "pkey", NULL); |
| |
| return pkey != NULL; |
| } |
| |
| CK_RV C_OpenSession(CK_SLOT_ID slot, CK_FLAGS flags, CK_VOID_PTR app, |
| CK_NOTIFY notify, CK_SESSION_HANDLE_PTR handle) |
| { |
| if (!handle) |
| return CKR_ARGUMENTS_BAD; |
| if (slot > cache_get_sections() - 1) |
| return CKR_ARGUMENTS_BAD; |
| if (session_count > 2) { |
| fprintf(stderr, "token is out of sessions, total=%d\n", |
| session_count); |
| return CKR_SESSION_COUNT; |
| } |
| session_count++; |
| *handle = slot; |
| |
| return CKR_OK; |
| } |
| |
| CK_RV |
| C_CloseSession(CK_SESSION_HANDLE handle) |
| { |
| session_count--; |
| return CKR_OK; |
| } |
| |
| CK_RV |
| C_CloseAllSessions(CK_SLOT_ID slot) |
| { |
| session_count = 0; |
| return CKR_OK; |
| } |
| |
| static void attr_long_from_cache(int sec, CK_ATTRIBUTE_PTR attr, |
| const char *key, int key_type) |
| { |
| int len = 0; |
| const char *value = cache_get_by_secnum(sec, key, &len); |
| |
| if (len != CACHE_INT) { |
| attr->ulValueLen = 0; |
| return; |
| } |
| |
| if (attr->pValue) |
| *((CK_LONG *)attr->pValue) = (unsigned long)value; |
| attr->ulValueLen = sizeof(CK_LONG); |
| } |
| |
| static void attr_bool_from_cache(int sec, CK_ATTRIBUTE_PTR attr, |
| const char *key, int key_type) |
| { |
| int len = 0; |
| unsigned long value = (unsigned long)cache_get_by_secnum(sec, key, &len); |
| |
| if (len != CACHE_INT) { |
| /* all booleans return false from not found */ |
| value = 0; |
| } else if (value & key_type) { |
| value = 1; |
| } else { |
| value = 0; |
| } |
| |
| if (attr->pValue) |
| *((CK_BYTE *)attr->pValue) = value; |
| |
| attr->ulValueLen = sizeof(CK_BYTE); |
| } |
| |
| static void attr_from_cache(int sec, CK_ATTRIBUTE_PTR attr, const char *key, |
| int key_type) |
| { |
| int len; |
| const char *value = cache_get_by_secnum(sec, key, &len); |
| |
| if (!value) { |
| attr->ulValueLen = 0; |
| return; |
| } |
| |
| if (attr->pValue) |
| memcpy(attr->pValue, value, len); |
| attr->ulValueLen = len; |
| } |
| |
| static void |
| getattribute(unsigned long obj, CK_ATTRIBUTE_PTR attr) |
| { |
| int sec = obj >> 1; |
| int key_type = (obj & 1) ? BOOL_FOR_PRIVATE : BOOL_FOR_PUBLIC; |
| |
| switch (attr->type) { |
| case CKA_CLASS: |
| if (attr->pValue) |
| *((CK_OBJECT_CLASS *)attr->pValue) = (obj & 1) ? CKO_PRIVATE_KEY : CKO_PUBLIC_KEY; |
| attr->ulValueLen = sizeof(CK_OBJECT_CLASS); |
| break; |
| case CKA_ID: |
| attr_from_cache(sec, attr, "id", key_type); |
| break; |
| case CKA_LABEL: { |
| const char *val; |
| int len; |
| |
| val = cache_get_by_secnum(sec, "label", &len); |
| if (!val) { |
| val = cache_get_section(sec); |
| len = strlen(val); |
| } |
| if (attr->pValue) |
| memcpy(attr->pValue, val, len); |
| attr->ulValueLen = len; |
| break; |
| } |
| |
| #define X(x, s, f) \ |
| case x: \ |
| f(sec, attr, s, key_type); \ |
| break |
| #define Xa(x) X(x, #x, attr_from_cache) |
| #define Xl(x) X(x, #x, attr_long_from_cache) |
| #define Xb(x) X(x, #x, attr_bool_from_cache) |
| |
| Xa(CKA_MODULUS); |
| Xa(CKA_PUBLIC_EXPONENT); |
| Xl(CKA_MODULUS_BITS); |
| Xl(CKA_KEY_TYPE); |
| Xb(CKA_TOKEN); |
| Xb(CKA_WRAP); |
| Xb(CKA_UNWRAP); |
| Xb(CKA_TRUSTED); |
| Xb(CKA_SENSITIVE); |
| Xb(CKA_EXTRACTABLE); |
| Xb(CKA_NEVER_EXTRACTABLE); |
| Xb(CKA_ALWAYS_AUTHENTICATE); |
| Xb(CKA_ENCRYPT); |
| Xb(CKA_VERIFY); |
| Xb(CKA_VERIFY_RECOVER); |
| Xb(CKA_DERIVE); |
| Xb(CKA_PRIVATE); |
| default: |
| attr->ulValueLen = 0; |
| break; |
| } |
| } |
| |
| static int cur_find = -1; |
| CK_OBJECT_CLASS find_restriction = 0; |
| |
| CK_RV |
| C_FindObjectsInit(CK_SESSION_HANDLE handle, CK_ATTRIBUTE_PTR template, |
| CK_ULONG count) |
| { |
| int i; |
| CK_ATTRIBUTE attr; |
| char *buf[1024]; |
| const int obj = handle<<1; /* only look up public attributes */ |
| |
| attr.pValue = buf; |
| cur_find = -1; |
| find_restriction = 0; |
| |
| for (i = 0; i < count; i++) { |
| if (template[i].type == CKA_CLASS) { |
| find_restriction = *((CK_OBJECT_CLASS *)template[i].pValue); |
| continue; |
| } |
| attr.type = template[i].type; |
| getattribute(obj, &attr); |
| if (attr.ulValueLen != template[i].ulValueLen || |
| memcmp(attr.pValue, template[i].pValue, attr.ulValueLen) != 0) |
| goto fail; |
| } |
| if (!logged_in(handle)) { |
| if (find_restriction == CKO_PRIVATE_KEY) |
| goto fail; |
| else |
| find_restriction = CKO_PUBLIC_KEY; |
| } |
| |
| if (!find_restriction) |
| cur_find = 2; |
| else |
| /* only one key */ |
| cur_find = 1; |
| fail: |
| return CKR_OK; |
| } |
| |
| CK_RV |
| C_FindObjects(CK_SESSION_HANDLE handle, CK_OBJECT_HANDLE_PTR objs, |
| CK_ULONG max, CK_ULONG_PTR count) |
| { |
| const int pub_obj = handle << 1; |
| const int priv_obj = pub_obj | 1; |
| if (cur_find > 0 && max >= cur_find) { |
| if (find_restriction == CKO_PRIVATE_KEY) { |
| *count = 1; |
| objs[0] = priv_obj; |
| } else if (find_restriction == CKO_PUBLIC_KEY) { |
| *count = 1; |
| objs[0] = pub_obj; |
| } else { |
| *count = 2; |
| objs[0] = pub_obj; |
| objs[1] = priv_obj; |
| } |
| } else { |
| *count = 0; |
| } |
| cur_find -= *count; |
| |
| return CKR_OK; |
| } |
| |
| CK_RV |
| C_FindObjectsFinal(CK_SESSION_HANDLE handle) |
| { |
| cur_find = -1; |
| return CKR_OK; |
| } |
| |
| CK_RV |
| C_GetAttributeValue(CK_SESSION_HANDLE handle, CK_OBJECT_HANDLE obj, |
| CK_ATTRIBUTE_PTR attr, CK_ULONG count) |
| { |
| int i; |
| |
| if (obj >> 1 != handle && handle >= cache_get_sections()) |
| return CKR_ARGUMENTS_BAD; |
| |
| for (i = 0; i < count; i++) { |
| getattribute(obj, &attr[i]); |
| } |
| |
| return CKR_OK; |
| } |
| |
| CK_RV |
| C_GetSessionInfo(CK_SESSION_HANDLE handle, CK_SESSION_INFO_PTR info) |
| { |
| if (!info) |
| return CKR_ARGUMENTS_BAD; |
| memset(info, 0, sizeof(*info)); |
| info->state = logged_in(handle) ? |
| CKS_RO_USER_FUNCTIONS : CKS_RO_PUBLIC_SESSION; |
| |
| return CKR_OK; |
| } |
| |
| CK_RV |
| C_Login(CK_SESSION_HANDLE handle, CK_USER_TYPE type, |
| CK_UTF8CHAR_PTR pin, CK_ULONG pin_len) |
| { |
| int rc; |
| |
| if (type != CKU_USER) |
| return CKR_ARGUMENTS_BAD; |
| rc = crypto_load_private_key(handle, pin, pin_len); |
| if (rc) |
| return CKR_PIN_INCORRECT; |
| |
| return CKR_OK; |
| } |
| |
| CK_RV |
| C_Logout(CK_SESSION_HANDLE handle) |
| { |
| crypto_free_private_key(handle); |
| |
| return CKR_OK; |
| } |
| |
| CK_RV |
| C_GetMechanismList(CK_SLOT_ID slot, CK_MECHANISM_TYPE_PTR mechs, |
| CK_ULONG_PTR count) |
| { |
| crypto_fill_mechanism_list(slot, mechs, count); |
| |
| return CKR_OK; |
| } |
| |
| CK_RV |
| C_GetMechanismInfo(CK_SLOT_ID slot, CK_MECHANISM_TYPE type, |
| CK_MECHANISM_INFO_PTR info) |
| { |
| if (type != CKM_RSA_PKCS) { |
| fprintf(stderr, "Bad Mechanism: 0x%lx\n", type); |
| return CKR_ARGUMENTS_BAD; |
| } |
| info->ulMinKeySize = 2048; |
| info->ulMaxKeySize = 2048; |
| info->flags = CKF_HW|CKF_ENCRYPT|CKF_DECRYPT; |
| |
| return CKR_OK; |
| } |
| |
| void *opstate; |
| |
| CK_RV |
| C_SignInit(CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mech, |
| CK_OBJECT_HANDLE key) |
| { |
| if ((key & 1) != 1 || key >> 1 != handle) |
| return CKR_ARGUMENTS_BAD; |
| opstate = crypto_sign_init(handle, mech); |
| if (opstate) |
| return CKR_OK; |
| return CKR_ARGUMENTS_BAD; |
| } |
| |
| CK_RV |
| C_Sign(CK_SESSION_HANDLE handle, CK_BYTE_PTR data, CK_ULONG data_len, |
| CK_BYTE_PTR sig, CK_ULONG_PTR sig_len) |
| { |
| if (crypto_sign(opstate, data, data_len, sig, sig_len)) |
| return CKR_ARGUMENTS_BAD; |
| return CKR_OK; |
| } |
| |
| CK_RV |
| C_DecryptInit(CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mech, |
| CK_OBJECT_HANDLE key) |
| { |
| if ((key & 1) != 1 || key >> 1 != handle) |
| return CKR_ARGUMENTS_BAD; |
| opstate = crypto_decrypt_init(handle, mech); |
| if (opstate) |
| return CKR_OK; |
| return CKR_ARGUMENTS_BAD; |
| } |
| |
| CK_RV |
| C_Decrypt(CK_SESSION_HANDLE handle, CK_BYTE_PTR enc_data, CK_ULONG enc_len, |
| CK_BYTE_PTR data, CK_ULONG_PTR data_len) |
| { |
| if (crypto_decrypt(opstate, enc_data, enc_len, data, data_len)) |
| return CKR_ARGUMENTS_BAD; |
| return CKR_OK; |
| } |
| |
| #define F(X) \ |
| static CK_RV U_##X(void) \ |
| { \ |
| fprintf(stderr, "Unimplemented: " #X "\n"); \ |
| return CKR_FUNCTION_NOT_SUPPORTED; \ |
| } |
| |
| #define U(X) \ |
| .X = (CK_##X)U_##X |
| |
| F(C_DecryptDigestUpdate) |
| F(C_DecryptFinal) |
| F(C_DecryptUpdate) |
| F(C_DecryptVerifyUpdate) |
| F(C_DeriveKey) |
| F(C_DestroyObject) |
| F(C_Digest) |
| F(C_DigestEncryptUpdate) |
| F(C_DigestFinal) |
| F(C_DigestInit) |
| F(C_DigestKey) |
| F(C_DigestUpdate) |
| F(C_Encrypt) |
| F(C_EncryptFinal) |
| F(C_EncryptInit) |
| F(C_EncryptUpdate) |
| F(C_GenerateKey) |
| F(C_GenerateKeyPair) |
| F(C_GenerateRandom) |
| F(C_GetFunctionStatus) |
| F(C_GetObjectSize) |
| F(C_GetOperationState) |
| F(C_InitPIN) |
| F(C_SeedRandom) |
| F(C_SetOperationState) |
| F(C_SetPIN) |
| F(C_SignEncryptUpdate) |
| F(C_SignFinal) |
| F(C_SignRecover) |
| F(C_SignRecoverInit) |
| F(C_SignUpdate) |
| F(C_UnwrapKey) |
| F(C_Verify) |
| F(C_VerifyFinal) |
| F(C_VerifyInit) |
| F(C_VerifyRecover) |
| F(C_VerifyRecoverInit) |
| F(C_VerifyUpdate) |
| F(C_WrapKey) |
| |
| static CK_FUNCTION_LIST module_functions = { |
| .version = (CK_VERSION) { |
| .major = LIBRARY_VERSION_MAJOR, |
| .minor = LIBRARY_VERSION_MINOR, |
| }, |
| .C_GetFunctionList = C_GetFunctionList, |
| .C_Initialize = C_Initialize, |
| .C_Finalize = C_Finalize, |
| .C_GetInfo = C_GetInfo, |
| .C_GetSlotList = C_GetSlotList, |
| .C_GetTokenInfo = C_GetTokenInfo, |
| .C_GetSlotInfo = C_GetSlotInfo, |
| .C_OpenSession = C_OpenSession, |
| .C_CloseSession = C_CloseSession, |
| .C_CloseAllSessions = C_CloseAllSessions, |
| .C_FindObjectsInit = C_FindObjectsInit, |
| .C_FindObjects = C_FindObjects, |
| .C_FindObjectsFinal = C_FindObjectsFinal, |
| .C_GetAttributeValue = C_GetAttributeValue, |
| .C_GetSessionInfo = C_GetSessionInfo, |
| .C_Login = C_Login, |
| .C_Logout = C_Logout, |
| .C_GetMechanismList = C_GetMechanismList, |
| .C_GetMechanismInfo = C_GetMechanismInfo, |
| .C_SignInit = C_SignInit, |
| .C_Sign = C_Sign, |
| .C_DecryptInit = C_DecryptInit, |
| .C_Decrypt = C_Decrypt, |
| U(C_DecryptDigestUpdate), |
| U(C_DecryptFinal), |
| U(C_DecryptUpdate), |
| U(C_DecryptVerifyUpdate), |
| U(C_DeriveKey), |
| U(C_DestroyObject), |
| U(C_Digest), |
| U(C_DigestEncryptUpdate), |
| U(C_DigestFinal), |
| U(C_DigestInit), |
| U(C_DigestKey), |
| U(C_DigestUpdate), |
| U(C_Encrypt), |
| U(C_EncryptFinal), |
| U(C_EncryptInit), |
| U(C_EncryptUpdate), |
| U(C_GenerateKey), |
| U(C_GenerateKeyPair), |
| U(C_GenerateRandom), |
| U(C_GetFunctionStatus), |
| U(C_GetObjectSize), |
| U(C_GetOperationState), |
| U(C_InitPIN), |
| U(C_SeedRandom), |
| U(C_SetOperationState), |
| U(C_SetPIN), |
| U(C_SignEncryptUpdate), |
| U(C_SignFinal), |
| U(C_SignRecover), |
| U(C_SignRecoverInit), |
| U(C_SignUpdate), |
| U(C_UnwrapKey), |
| U(C_Verify), |
| U(C_VerifyFinal), |
| U(C_VerifyInit), |
| U(C_VerifyRecover), |
| U(C_VerifyRecoverInit), |
| U(C_VerifyUpdate), |
| U(C_WrapKey), |
| }; |