blob: 6311e3a9fb7108a2e3bf57f497a439cb05e54d9e [file] [log] [blame]
/*
* 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_init(CK_SESSION_HANDLE handle)
{
return (int)(unsigned long)cache_get_by_secnum(handle, "session_init", NULL);
}
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)
{
int init;
if (!handle)
return CKR_ARGUMENTS_BAD;
if (slot > cache_get_sections() - 1)
return CKR_ARGUMENTS_BAD;
init = session_init(slot);
init++;
cache_add_by_secnum(slot, "session_init", (const char *)(long)init,
CACHE_INT);
*handle = slot;
return CKR_OK;
}
CK_RV
C_CloseSession(CK_SESSION_HANDLE handle)
{
int init = session_init(handle);
if (!init)
return CKR_SESSION_HANDLE_INVALID;
--init;
cache_add_by_secnum(handle, "session_init", (const char *)(long)init,
CACHE_INT);
return CKR_OK;
}
CK_RV
C_CloseAllSessions(CK_SLOT_ID slot)
{
int i;
const int count = cache_get_sections() - 1;
for (i = 1; i < count; i++)
cache_add_by_secnum(slot, "session_init", 0, CACHE_INT);
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);
Xb(CKA_SIGN);
Xb(CKA_DECRYPT);
default:
attr->ulValueLen = 0;
break;
}
}
static void set_find(CK_SESSION_HANDLE handle, int cur_find,
CK_OBJECT_CLASS find_restriction)
{
cache_add_by_secnum(handle, "cur_find",
(const char *)(unsigned long)cur_find, CACHE_INT);
cache_add_by_secnum(handle, "find_restriction",
(const char *)(unsigned long)find_restriction,
CACHE_INT);
}
static void get_find(CK_SESSION_HANDLE handle, int *cur_find,
CK_OBJECT_CLASS *find_restriction)
{
*cur_find = (int)(long)cache_get_by_secnum(handle, "cur_find", NULL);
*find_restriction = (CK_OBJECT_CLASS)(long)
cache_get_by_secnum(handle, "find_restriction", NULL);
}
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 */
int cur_find = -1;
CK_OBJECT_CLASS find_restriction = 0;
if (!session_init(handle))
return CKR_SESSION_HANDLE_INVALID;
attr.pValue = buf;
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:
set_find(handle, cur_find, find_restriction);
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;
int cur_find;
CK_OBJECT_CLASS find_restriction;
get_find(handle, &cur_find, &find_restriction);
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;
set_find(handle, cur_find, find_restriction);
return CKR_OK;
}
CK_RV
C_FindObjectsFinal(CK_SESSION_HANDLE handle)
{
set_find(handle, -1, 0);
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;
if (!session_init(handle))
return CKR_SESSION_HANDLE_INVALID;
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 (!crypto_check_mechanism(slot, type, info)) {
fprintf(stderr, "Bad Mechanism: 0x%lx\n", type);
return CKR_ARGUMENTS_BAD;
}
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),
};