kmsp11/sample/sample.c (116 lines of code) (raw):
/*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>
// Begin: Loading the OASIS PKCS#11 headers.
// Several macros must be defined before loading pkcs11.h.
#define CK_PTR *
#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 "pkcs11.h"
// End: Loading the OASIS PKCS#11 headers.
// run_sample is a sample function that demonstrates loading the library
// dynamically and using the loaded library to locate a key and create a
// digital signature. It returns 0 on success, and a non-zero value on failure.
int run_sample(const char* library_path, const char* config_file_path,
const char* ec_p256_signing_key_id) {
// Dynamically load the PKCS#11 shared library.
// Note that there should be no corresponding dlclose call. Our library does
// not support being dynamically unloaded.
void* library = dlopen(library_path, RTLD_LAZY | RTLD_NODELETE);
if (!library) {
fprintf(stderr, "error loading libkmsp11.so");
return 1;
}
// Dynamically load the function list table from the loaded library.
CK_C_GetFunctionList get_function_list =
(CK_C_GetFunctionList)dlsym(library, "C_GetFunctionList");
if (!get_function_list) {
fprintf(stderr, "error locating C_GetFunctionList in the loaded library");
return 1;
}
// Load the function list into 'f'.
CK_FUNCTION_LIST* f;
CK_RV rv = get_function_list(&f);
if (rv != CKR_OK) {
fprintf(stderr, "CK_RV=%lX calling C_GetFunctionList", rv);
return 1;
}
// Initialize the library.
CK_C_INITIALIZE_ARGS init_args = {0};
init_args.flags = CKF_OS_LOCKING_OK;
init_args.pReserved = (char*)config_file_path;
rv = f->C_Initialize(&init_args);
if (rv != CKR_OK) {
fprintf(stderr, "CK_RV=%lX calling C_Initialize", rv);
return 1;
}
// Open a session handle.
CK_SESSION_HANDLE sess;
rv = f->C_OpenSession(/*slotID=*/0, /*flags=*/CKF_SERIAL_SESSION,
/*pApplication=*/0, /*Notify=*/0, &sess);
if (rv != CKR_OK) {
fprintf(stderr, "CK_RV=%lX calling C_OpenSession", rv);
goto library_cleanup;
}
// Begin searching for our signing key.
CK_OBJECT_CLASS object_class = CKO_PRIVATE_KEY;
CK_ATTRIBUTE template[] = {
{CKA_CLASS, &object_class, sizeof(object_class)},
{CKA_LABEL, (CK_UTF8CHAR*)ec_p256_signing_key_id,
strlen(ec_p256_signing_key_id)},
};
rv = f->C_FindObjectsInit(sess, template, 2);
if (rv != CKR_OK) {
fprintf(stderr, "CK_RV=%lX calling C_FindObjectsInit", rv);
goto session_cleanup;
}
// Retrieve the handle to the signing key.
CK_OBJECT_HANDLE private_key;
CK_ULONG found_count;
rv = f->C_FindObjects(sess, &private_key, 1, &found_count);
if (rv != CKR_OK) {
fprintf(stderr, "CK_RV=%lX calling C_FindObjects", rv);
goto session_cleanup;
}
if (found_count != 1) {
fprintf(stderr, "found_count=%ld after calling C_FindObjects", found_count);
goto session_cleanup;
}
// End the search.
rv = f->C_FindObjectsFinal(sess);
if (rv != CKR_OK) {
fprintf(stderr, "CK_RV=%lX calling C_FindObjectsFinal", rv);
goto session_cleanup;
}
// Initialize our signing operation.
CK_MECHANISM mech = {CKM_ECDSA, 0, 0};
rv = f->C_SignInit(sess, &mech, private_key);
if (rv != CKR_OK) {
fprintf(stderr, "CK_RV=%lX calling C_SignInit", rv);
goto session_cleanup;
}
// Prepare to call 'Sign'.
// When calling Sign, `data` should be filled with the SHA-256 digest of the
// data to be signed over.
CK_BYTE data[32] = {0};
// An output buffer to hold the computed signature.
CK_BYTE signature[64] = {0};
// Call 'Sign'.
CK_ULONG signature_length = sizeof(signature);
rv = f->C_Sign(sess, data, sizeof(data), signature, &signature_length);
if (rv != CKR_OK) {
fprintf(stderr, "CK_RV=%lX calling C_Sign", rv);
goto session_cleanup;
}
if (signature_length != sizeof(signature)) {
fprintf(stderr,
"unexpected signature length = %ld (want %ld) after calling C_Sign",
signature_length, sizeof(signature));
goto session_cleanup;
}
printf("computed signature: ");
for (size_t i = 0; i < sizeof(signature); i++) {
printf("%X", signature[i]);
}
printf("\n");
CK_RV cleanup_rv;
session_cleanup:
cleanup_rv = f->C_CloseSession(sess);
if (cleanup_rv != CKR_OK) {
fprintf(stderr, "CK_RV=%lX calling C_CloseSession", rv);
if (rv == CKR_OK) {
rv = cleanup_rv;
}
}
library_cleanup:
cleanup_rv = f->C_Finalize(0);
if (cleanup_rv != CKR_OK) {
fprintf(stderr, "CK_RV=%lX calling C_Finalize", rv);
if (rv == CKR_OK) {
rv = cleanup_rv;
}
}
return rv;
}