/* * Program to add, delete, and view a data object via PKCS#11 */ #include #include #include #include #include /* unix defns for pkcs11.h */ #define CK_PTR * #define CK_DEFINE_FUNCTION(returnType, name) \ returnType name #define CK_DECLARE_FUNCTION(returnType, name) \ returnType name #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \ returnType (* name) #define CK_CALLBACK_FUNCTION(returnType, name) \ returnType (* name) #ifndef NULL_PTR #define NULL_PTR 0 #endif #include typedef struct Session { CK_FUNCTION_LIST_PTR functions; CK_SESSION_HANDLE handle; } Session; static Session *open_session (const char *module, int slot, char *pin); static void close_session (const Session *session); static void create_data_objects (const Session *session, int objects); static void delete_data_objects (const Session *session); static void view_data_objects (const Session *session); static const char * get_attr_type_str (const CK_ATTRIBUTE_TYPE type); static const char * get_attr_value_str (const CK_ATTRIBUTE *attr); static const char *get_rv_string(const CK_RV rv); static void dump_attributes (const Session *session, CK_OBJECT_HANDLE hObject); static void printUsage (const char *progname) { printf ("Usage: %s {option}\n", progname); printf ("Options:\n"); printf (" --module Path to PKCS#11 module\n"); printf (" --slot Slot number to use [DEFAULT=0]\n"); printf (" --pin PIN for user login\n"); printf (" --create {x|y|z} Create data object x, y, or z\n"); printf (" --delete Delete data object(s)\n"); printf (" --view View data objects\n"); printf (" --help Show this help\n"); } /* Actions that may be performed */ typedef enum { ACTION_NONE, /* No action defined */ ACTION_CREATE, /* Create data objects */ ACTION_DELETE, /* Delete all data objects */ ACTION_VIEW, /* View all data objects */ } Action; #define OBJECT_X 0x0001 #define OBJECT_Y 0x0002 #define OBJECT_Z 0x0004 int main (int argc, char **argv) { const char *module = NULL; int slot = 0; char *pin = NULL; int i; Action action = ACTION_NONE; int objects = 0; Session *session; /* Process args. */ for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (strcmp (arg, "--module") == 0) { module = argv[++i]; } else if (strcmp (arg, "--slot") == 0) { slot = atoi (argv[++i]); } else if (strcmp (arg, "--pin") == 0) { pin = argv[++i]; } else if (strcmp (arg, "--create") == 0) { action = ACTION_CREATE; while (i < (argc-1) && strcmp (argv[i+1], "--") != 0) { const char *s = argv[++i]; if (strcmp (s, "x") == 0) { objects |= OBJECT_X; } else if (strcmp (s, "y") == 0) { objects |= OBJECT_Y; } else if (strcmp (s, "z") == 0) { objects |= OBJECT_Z; } else { printf ("*** Unknown object: %s ***\n", s); printUsage (argv[0]); exit (1); } } } else if (strcmp (arg, "--delete") == 0) { action = ACTION_DELETE; } else if (strcmp (arg, "--view") == 0) { action = ACTION_VIEW; } else if (strcmp (arg, "--help") == 0) { printUsage (argv[0]); exit (0); } else { printf ("*** Unknown option: '%s' ***\n", arg); printUsage (argv[0]); exit (-1); } } /* Make sure a module was specified. */ if (module == NULL) { printf ("*** Must specify a module ***\n"); printUsage (argv[0]); exit (-1); } /* Make sure an action was specified */ if (action == ACTION_NONE) { printf ("*** Must specify an action (--create, --delete, or --view) ***\n"); printUsage (argv[0]); exit (-1); } session = open_session (module, slot, pin); if (session == NULL) { exit (-1); } /* Perform the appropriate action */ switch (action) { case ACTION_CREATE: create_data_objects (session, objects); break; case ACTION_DELETE: delete_data_objects (session); break; case ACTION_VIEW: view_data_objects (session); break; default: assert (0); /* should not get here */ } close_session (session); return 0; } /** * Create data object with given attributes */ static CK_OBJECT_HANDLE create_data_object (const Session *session, CK_BBOOL attr_private, CK_CHAR *attr_application, CK_CHAR *attr_label, CK_CHAR *attr_value) { CK_RV rv; CK_BBOOL true = TRUE; CK_OBJECT_HANDLE hObject; CK_OBJECT_CLASS objectClass = CKO_DATA; CK_ATTRIBUTE template[] = { { CKA_CLASS, &objectClass, sizeof (CK_OBJECT_CLASS) }, { CKA_TOKEN, &true, sizeof (CK_BBOOL) }, { CKA_PRIVATE, &attr_private, sizeof (CK_BBOOL) }, { CKA_MODIFIABLE, &true, sizeof (CK_BBOOL) }, { CKA_APPLICATION, attr_application, strlen (attr_application) }, { CKA_LABEL, attr_label, strlen (attr_label) }, { CKA_VALUE, attr_value, strlen (attr_value) }, { CKA_OBJECT_ID, NULL, 0 }, }; CK_ULONG templateSize = sizeof (template) / sizeof (template[0]); CK_ULONG i; /* Pre-condition checks. */ assert (session != NULL); printf ("## Creating object with template:\n"); for (i = 0; i < templateSize; i++) { CK_ATTRIBUTE *attr = &template[i]; printf ("%s: length = %u, value = %s\n", get_attr_type_str (attr->type), (unsigned int) attr->ulValueLen, get_attr_value_str (attr)); } printf ("## Creating object ... "); rv = (*session->functions->C_CreateObject) (session->handle, template, templateSize, &hObject); if (rv != CKR_OK) { printf ("failed: %s\n", get_rv_string (rv)); return CK_INVALID_HANDLE; } printf ("ok: handle = %04lX\n", hObject); return hObject; } /** * Create data objects */ static void create_data_objects (const Session *session, int objects) { printf ("========== Creating data objects ==========\n"); /* private="TRUE", application="test", label="x", value="foo" */ if (objects & OBJECT_X) { printf ("=== Creating object X ===\n"); create_data_object (session, TRUE, "test", "x", "foo"); } /* private="FALSE", application="test", label="y", value="bar" */ if (objects & OBJECT_Y) { printf ("=== Creating object Y ===\n"); create_data_object (session, FALSE, "test", "y", "bar"); } /* private="false", application="test", label="z", value="baz" */ if (objects & OBJECT_Z) { printf ("=== Creating object Z ===\n"); create_data_object (session, FALSE, "test", "z", "baz"); } printf ("===========================================\n"); /* Now view all data objects */ view_data_objects (session); } typedef struct { CK_OBJECT_HANDLE *hObjects; CK_ULONG ulCount; } ObjectHandles; #define MAX_OBJECTS 128 /** * Get handles of all data objects. */ static ObjectHandles *get_data_object_handles (const Session *session) { CK_RV rv; ObjectHandles *handles; CK_OBJECT_CLASS objClass = CKO_DATA; /* Template matches all objects with CKA_CLASS == CKO_DATA */ CK_ATTRIBUTE template[] = { { CKA_CLASS, &objClass, sizeof (objClass) }, }; CK_ULONG ulTemplateCount = sizeof (template) / sizeof (template[0]); /* Pre-condition checks. */ assert (session != NULL); /* Allocate object handles structure */ handles = (ObjectHandles *) malloc (sizeof (ObjectHandles)); assert (handles != NULL); /* Allocate array of object handles */ handles->ulCount = MAX_OBJECTS; handles->hObjects = (CK_OBJECT_HANDLE *) malloc (sizeof (CK_OBJECT_HANDLE) * MAX_OBJECTS); assert (handles->hObjects != NULL); /* Initiate the object search */ printf ("## Searching for data objects ... "); rv = (*session->functions->C_FindObjectsInit)(session->handle, template, ulTemplateCount); if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID) { printf ("failed: %s\n", get_rv_string (rv)); return NULL; } /* Perform the search */ rv = (*session->functions->C_FindObjects) (session->handle, handles->hObjects, handles->ulCount, &handles->ulCount); if (rv != CKR_OK) { printf ("failed: %s\n", get_rv_string (rv)); return NULL; } printf ("ok\n"); printf ("## Found %lu data object(s)\n", handles->ulCount); /* Remember to call C_FindObjectsFinal to terminate the search */ printf ("## Finalizing search ... "); rv = (*session->functions->C_FindObjectsFinal)(session->handle); if (rv != CKR_OK) { printf ("failed: %s\n", get_rv_string (rv)); return NULL; } printf ("ok\n"); return handles; } /** * Delete all data objects */ static void delete_data_objects (const Session *session) { ObjectHandles *handles; CK_ULONG i; /* Pre-condition checks. */ assert (session != NULL); printf ("========== Deleting data objects ==========\n"); /* Get handles to all data objects */ handles = get_data_object_handles (session); assert (handles != NULL); /* For each handle, delete the corresponding object */ for (i = 0; i < handles->ulCount; i++) { CK_OBJECT_HANDLE hObject = handles->hObjects[i]; assert (hObject != CK_INVALID_HANDLE); printf ("## Object %04lX shall be deleted, attributes are:\n", hObject); dump_attributes (session, hObject); printf ("## Deleting object %04lX\n", hObject); (*session->functions->C_DestroyObject) (session->handle, hObject); } handles = get_data_object_handles (session); assert (handles != NULL); /* Emit a warning if data objects can still be found */ if (handles->ulCount > 0) { printf ("WARNING: there are still %lu data object(s) present\n", handles->ulCount); } printf ("==========================================\n"); } /** * View all data objects. */ static void view_data_objects (const Session *session) { ObjectHandles *handles; CK_ULONG i; /* Pre-condition checks. */ assert (session != NULL); printf ("========== Viewing data objects ==========\n"); /* Get handles to all data objects */ handles = get_data_object_handles (session); assert (handles != NULL); /* View attributes of corresponding object */ for (i = 0; i < handles->ulCount; i++) { CK_OBJECT_HANDLE hObject = handles->hObjects[i]; assert (hObject != CK_INVALID_HANDLE); printf ("## Viewing attributes of object %04lX\n", hObject); dump_attributes (session, hObject); } printf ("==========================================\n"); } /* =================== Attribute display functions ====================== */ typedef const char * (*AttributeToStringFn) (const CK_ATTRIBUTE *attr); /* Information about an attribute */ typedef struct { CK_ATTRIBUTE_TYPE type; const char *name; const AttributeToStringFn converter; } AttributeInfo; /** * Get string representation of an attribute containing a CK_OBJECT_CLASS * value. */ static const char * ObjectClassToString (const CK_ATTRIBUTE *attr) { CK_OBJECT_CLASS *value; char buf[128]; /* Pre-condition checks */ assert (attr != NULL); assert (attr->ulValueLen == sizeof (CK_OBJECT_CLASS)); assert (attr->pValue != NULL); value = (CK_OBJECT_CLASS *) attr->pValue; switch (*value) { case CKO_DATA: return "CKO_DATA"; case CKO_CERTIFICATE: return "CKO_CERTIFICATE"; case CKO_PUBLIC_KEY: return "CKO_PUBLIC_KEY"; case CKO_PRIVATE_KEY: return "CKO_PRIVATE_KEY"; case CKO_SECRET_KEY: return "CKO_SECRET_KEY"; case CKO_HW_FEATURE: return "CKO_HW_FEATURE"; case CKO_DOMAIN_PARAMETERS: return "CKO_DOMAIN_PARAMETERS"; /* Undefined: case CKO_MECHANISM: return "CKO_MECHANISM"; */ } /* Check for vendor-defined value */ if (*value >= CKO_VENDOR_DEFINED) { return "CKO_VENDOR_DEFINED"; } /* Unknown object class */ sprintf (buf, "", *value); return strdup (buf); } /** * Get a string representation of an attribute containing a CK_BBOOL * value. */ static const char * BBoolToString (const CK_ATTRIBUTE *attr) { const CK_BBOOL *value; /* Pre-condition checks */ assert (attr != NULL); assert (attr->ulValueLen == sizeof (CK_BBOOL)); assert (attr->pValue != NULL); value = (CK_BBOOL *) attr->pValue; return (*value) ? "TRUE" : "FALSE"; } /** * Get the string representation of an attribute containing an RFC2279 * string value. */ static const char * RFC2279StringToString (const CK_ATTRIBUTE *attr) { char *buf; /* Pre-condition checks */ assert (attr != NULL); assert (attr->pValue != NULL); assert (attr->ulValueLen != -1); /* Allocate buffer to hold string, and terminating zero */ buf = (char *) malloc (attr->ulValueLen + 1); assert (buf != NULL); /* Copy rfc2279 string, and add terminating zero */ strncpy (buf, attr->pValue, attr->ulValueLen); buf[attr->ulValueLen] = (char) 0; return buf; } /** * Get the string representation of an attribute containing a * byte array. */ static const char * ByteArrayToString (const CK_ATTRIBUTE *attr) { CK_BYTE *value; char *buf; CK_ULONG i; /* Pre-condition checks */ assert (attr != NULL); assert (attr->pValue != NULL); assert (attr->ulValueLen != -1); value = (CK_BYTE *) attr->pValue; /* Allocate buffer to hold hex representation */ buf = (char *) malloc (attr->ulValueLen * 3); assert (buf != NULL); /* Copy byte array to hex buffer */ for (i = 0; i < attr->ulValueLen; i++) { char t[3]; if (i > 0) { buf[(i*3)-1] = ':'; } sprintf (t, "%02X", value[i]); buf[(i*3)] = t[0]; buf[(i*3)+1] = t[1]; } buf[attr->ulValueLen*3] = 0; return buf; } /** Attribute information table */ static const AttributeInfo ATTRIBUTE_INFOS[] = { { CKA_CLASS, "CKA_CLASS", ObjectClassToString }, { CKA_TOKEN, "CKA_TOKEN", BBoolToString }, { CKA_PRIVATE, "CKA_PRIVATE", BBoolToString }, { CKA_MODIFIABLE, "CKA_MODIFIABLE", BBoolToString }, { CKA_LABEL, "CKA_LABEL", RFC2279StringToString }, { CKA_APPLICATION, "CKA_APPLICATION", RFC2279StringToString }, { CKA_OBJECT_ID, "CKA_OBJECT_ID", ByteArrayToString }, { CKA_VALUE, "CKA_VALUE", ByteArrayToString }, }; #define NUM_ATTRIBUTE_INFOS \ (sizeof (ATTRIBUTE_INFOS) / sizeof (ATTRIBUTE_INFOS[0])) /** * Get a string representing the attribute type. */ static const char * get_attr_type_str (const CK_ATTRIBUTE_TYPE type) { char buf[128]; size_t i; for (i = 0; i < NUM_ATTRIBUTE_INFOS; i++) { if (ATTRIBUTE_INFOS[i].type == type) { return ATTRIBUTE_INFOS[i].name; } } sprintf (buf, "Unknown: %lu", type); return strdup (buf); } /** * Get a string representing the attribute value. */ static const char * get_attr_value_str (const CK_ATTRIBUTE *attr) { char buf[128]; size_t i; /* Check for empty value */ if (attr->pValue == NULL || attr->ulValueLen == -1) { return ""; } /* Check for known attribute type, and call converter fn */ for (i = 0; i < NUM_ATTRIBUTE_INFOS; i++) { if (ATTRIBUTE_INFOS[i].type == attr->type) { return ATTRIBUTE_INFOS[i].converter (attr); } } /* Unknown attribute type */ sprintf (buf, "", attr->type); return strdup (buf); } /** * Dump all data attributes for the object with the given handle. */ static void dump_attributes (const Session *session, CK_OBJECT_HANDLE hObject) { CK_RV rv; CK_ATTRIBUTE template[] = { { CKA_CLASS, NULL_PTR, 0 }, { CKA_TOKEN, NULL_PTR, 0 }, { CKA_PRIVATE, NULL_PTR, 0 }, { CKA_MODIFIABLE, NULL_PTR, 0 }, { CKA_LABEL, NULL_PTR, 0 }, { CKA_APPLICATION, NULL_PTR, 0 }, { CKA_OBJECT_ID, NULL_PTR, 0 }, { CKA_VALUE, NULL_PTR, 0 } }; CK_ULONG templateSize = sizeof (template) / sizeof (template[0]); CK_ULONG i; /* Pre-condition checks. */ assert (session != NULL); assert (hObject != CK_INVALID_HANDLE); /* Get the length of the attributes */ printf ("## Getting attribute values ... "); rv = (*session->functions->C_GetAttributeValue) (session->handle, hObject, template, templateSize); if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID) { printf ("failed: %s\n", get_rv_string (rv)); return; } /* Allocate memory for the attribute values. */ for (i = 0; i < templateSize; i++) { CK_ATTRIBUTE *attr = &template[i]; /* Check that the attribute's length can be determined. */ if (attr->ulValueLen != -1) { /* Allocate space for the attribute value. */ attr->pValue = (CK_BYTE_PTR) malloc (attr->ulValueLen); assert (attr->pValue != NULL); } } /* Get the value of the attributes */ rv = (*session->functions->C_GetAttributeValue) (session->handle, hObject, template, templateSize); if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID) { printf ("failed: %s\n", get_rv_string (rv)); return; } printf ("ok\n"); for (i = 0; i < templateSize; i++) { CK_ATTRIBUTE *attr = &template[i]; printf ("%s: length = %lu, value = %s\n", get_attr_type_str (attr->type), attr->ulValueLen, get_attr_value_str (attr)); } } /* ===================== Session management ============================= */ /** * Open a new session. */ static Session *open_session (const char *module, int slot, char *pin) { Session *session; void *hModule; CK_C_GetFunctionList pC_GetFunctionList; CK_RV rv; /* Pre-condition checks */ assert (module != NULL); session = (Session *) calloc (1, sizeof (Session)); assert (session != NULL); /* Load the PKCS #11 module. */ printf ("## Loading module: %s ...", module); fflush (stdout); hModule = dlopen (module, RTLD_LAZY); printf ((hModule == NULL) ? "failed\n" : "ok\n"); assert (hModule!=NULL); /* Get function pointer to C_GetFunctionList. */ pC_GetFunctionList = (CK_C_GetFunctionList) dlsym (hModule, "C_GetFunctionList"); assert (pC_GetFunctionList!=NULL); /* Get function pointers to all PKCS #11 functions. */ rv = (*pC_GetFunctionList) (&session->functions); assert (rv == CKR_OK); /* Initalize Cryptoki. */ rv = (*session->functions->C_Initialize) (NULL); assert (rv==CKR_OK); /* Open a read/write session on the given slot */ printf ("## Opening session for slot = %02d ... ", slot); rv = (*session->functions->C_OpenSession)(slot, CKF_SERIAL_SESSION|CKF_RW_SESSION, 0, 0, &session->handle); if (rv != CKR_OK) { printf ("failed: %s\n", get_rv_string (rv)); return NULL; } printf ("ok\n"); if (pin != NULL) { printf ("## Performing login with PIN '%s' .... ", pin); rv = (*session->functions->C_Login)(session->handle, CKU_USER, pin, strlen(pin)); if (rv != CKR_OK) { printf ("failed: %s\n", get_rv_string (rv)); return NULL; } printf ("ok\n"); } return session; } /** * Close the session. */ static void close_session (const Session *session) { CK_RV rv; /* Pre-condition checks */ assert (session != NULL); printf ("## Finalizing ... "); rv = (*session->functions->C_Finalize)(0); if (rv != CKR_OK) { printf ("failed: %s\n", get_rv_string (rv)); } else { printf ("ok\n"); } } static const char *get_rv_string(const CK_RV rv) { static char buf[128]; switch (rv) { case CKR_ARGUMENTS_BAD: return "bad arguments"; case CKR_CANCEL: return "cancel"; case CKR_CRYPTOKI_NOT_INITIALIZED: return "cryptoki not initialized"; case CKR_DATA_LEN_RANGE: return "data len range"; case CKR_DEVICE_ERROR: return "device error"; case CKR_DEVICE_MEMORY: return "device memory"; case CKR_DEVICE_REMOVED: return "device removed"; case CKR_FUNCTION_CANCELED: return "function canceled"; case CKR_FUNCTION_FAILED: return "function failed"; case CKR_FUNCTION_NOT_SUPPORTED: return "function not supported"; case CKR_GENERAL_ERROR: return "general error"; case CKR_HOST_MEMORY: return "host memory"; case CKR_OK: return "ok"; case CKR_OPERATION_ACTIVE: return "operation active"; case CKR_OPERATION_NOT_INITIALIZED: return "operation not initialized"; case CKR_PIN_INCORRECT: return "pin incorrect"; case CKR_PIN_LOCKED: return "pin locked"; case CKR_RANDOM_SEED_NOT_SUPPORTED: return "random seed not supported"; case CKR_RANDOM_NO_RNG: return "no rng"; case CKR_SESSION_CLOSED: return "session closed"; case CKR_SESSION_COUNT: return "session count"; case CKR_SESSION_HANDLE_INVALID: return "session handle invalid"; case CKR_SESSION_PARALLEL_NOT_SUPPORTED: return "session parallel not supported"; case CKR_SESSION_READ_ONLY: return "session read only"; case CKR_SESSION_EXISTS: return "session exists"; case CKR_SESSION_READ_ONLY_EXISTS: return "session read-only exists"; case CKR_SESSION_READ_WRITE_SO_EXISTS: return "session read write SO exists"; case CKR_SIGNATURE_INVALID: return "signature invalid"; case CKR_SIGNATURE_LEN_RANGE: return "signature len range"; case CKR_SLOT_ID_INVALID: return "slot id invalid"; case CKR_TEMPLATE_INCOMPLETE: return "template incomplete"; case CKR_TEMPLATE_INCONSISTENT: return "template inconsistent"; case CKR_TOKEN_NOT_PRESENT: return "token not present"; case CKR_TOKEN_NOT_RECOGNIZED: return "token not recognized"; case CKR_USER_ALREADY_LOGGED_IN: return "user already logged in"; case CKR_USER_NOT_LOGGED_IN: return "user not logged in"; case CKR_USER_PIN_NOT_INITIALIZED: return "user pin not initialized"; case CKR_USER_TOO_MANY_TYPES: return "user too many types"; case CKR_USER_TYPE_INVALID: return "user type invalid"; } sprintf (buf, "Unknown error: %lu", rv); return strdup (buf); }