corePKCS11 Management And Random Numbers Demo
Introduction
This demo is the first in the corePKCS11 demo series. It covers the section of the PKCS #11 API that manages the PKCS #11 stack, and shows how PKCS #11 can be used to generate random numbers. The PKCS #11 standard can be found
here.
These demos and their associated web pages use the terms defined by PKCS #11 interchangeably with the terms PKCS #11
API, stack, standard, etc. To summarize the terminology defined in the standard, "Cryptoki" refers to the Cryptographic
Token Interface that is defined in the PKCS #11 standard, that is, the PKCS #11 API or functions. An implementation of
Cryptoki is referred to as a "Cryptoki library"; this would be equivalent to the terms "PKCS #11 implementation" or
"PKCS #11 stack".
The PKCS #11 specification is distributed as three header files:
- pkcs11.h
- This is the main header file, and is the only one an application needs to include. It requires some macros to
be defined before it is included, and those definitions can be found in "core_pkcs11.h" (see
<freertos>/libraries/freertos_plus/source/corepkcs11/include/core_pkcs11.h
). So, when you include
the PKCS #11 library, include "core_pkcs11.h" first, and then "pkcs11.h".
- pkcs11f.h
- This contains function prototypes.
- pkcs11t.h
- This contains the various types as defined in the PKCS #11 specification.
The PKCS #11 demo projects use the
FreeRTOS Windows port, so they can be built and evaluated with the free Community version of Visual Studio on Windows without the need
for any particular MCU hardware.
The set of functions presented in this demo are categorized as:
-
- General Purpose Functions
- Slot and Token Management Functions
- Session Management Functions
- Random Number Generation Functions
Source Code Organization
The Visual Studio solution for the PKCS #11 based mutual authentication demo is called
pkcs11_demo.sln
and is located in the
FreeRTOS\FreeRTOS-Plus\Demo\corePKCS11_Windows_Simulator\
directory of the main FreeRTOS download.
Click to enlarge
Configuring the Demo Project
To configure the demo project, set configPKCS11_MANAGEMENT_AND_RNG_DEMO
to 1 in pkcs11_demo_config.h
.
Building the Demo Project
The demo project uses the free community edition of Visual Studio.
- In the Visual Studio IDE, open the Visual Studio solution file
FreeRTOS\FreeRTOS-Plus\Demo\corePKCS11_Windows_Simulator\pkcs11_demos.sln
.
- Select 'build solution' from the IDE's 'build' menu.
Functionality
The entry point for this demo is vPKCS11ManagementAndRNGDemo. This function outlines the basics of how to start
up a PKCS #11 session and use it to generate a random number.
The first step is to acquire a function pointer structure that contains pointers to those functions the PKCS #11 stack
has implemented. Functions that are not implemented are always NULL. This is useful because some PKCS #11
implementations may not define every function. The demos will assert on functions that are not implemented. It may
be useful in a real application to account for this situation in order to make a library compatible between different
PKCS #11 implementations.
Getting a PKCS #11 function list:
CK_FUNCTION_LIST_PTR pxFunctionList = NULL;
xResult = C_GetFunctionList( &pxFunctionList );
configASSERT( xResult == CKR_OK );
configASSERT( pxFunctionList != NULL );
configASSERT( pxFunctionList->C_Initialize != NULL );
configASSERT( pxFunctionList->C_GetSlotList != NULL );
configASSERT( pxFunctionList->C_OpenSession != NULL );
configASSERT( pxFunctionList->C_Login != NULL );
configASSERT( pxFunctionList->C_GenerateRandom != NULL );
configASSERT( pxFunctionList->C_CloseSession != NULL );
configASSERT( pxFunctionList->C_Finalize != NULL );
After the functions are acquired from the PKCS #11 implementation, the PKCS#11 implementation can be initialized with a
call to C_Initialize
. This function makes no guarantees about thread safety, and you should be careful to
consider concurrency when making this call. The PKCS #11 stack used by the demo does not implement the thread safety
mechanisms that are in the specification. So it may be worth revisiting any code that calls C_Initialize
when using a different PKCS #11 stack, to ensure concurrency is handled properly.
Initializing the PKCS #11 stack:
CK_C_INITIALIZE_ARGS xInitArgs = { 0 };
xResult = pxFunctionList->C_Initialize( &xInitArgs );
configASSERT( xResult == CKR_OK );
When initialized, we query for a slot that can be used by the application. A slot is defined by PKCS #11 as
"A logical reader that potentially contains a token". The demo will always use the first
slot returned by C_GetSlotList
, because the implementation that it was written for only has one slot.
The slot allows the application to specify which token to use. Many implementations vary in the relationship of slots
to tokens. The main point is that the SlotID will specify which token we would like to use for the application
session.
Selecting a slot:
CK_SLOT_ID * pxSlotId = NULL;
CK_ULONG xSlotCount = 0;
xResult = pxFunctionList->C_GetSlotList( CK_TRUE,
NULL,
&xSlotCount );
configASSERT( xResult == CKR_OK );
pxSlotId = pvPortMalloc( sizeof( CK_SLOT_ID ) * ( xSlotCount ) );
configASSERT( pxSlotId != NULL );
xResult = pxFunctionList->C_GetSlotList( CK_TRUE,
pxSlotId,
&xSlotCount );
configASSERT( xResult == CKR_OK );
The next step is to create a session that connects the application to the slot returned by the PKCS #11 stack. The
specification defines a session to be "The logical connection between an application and a token." Once again, this
is explained further in the source code and the PKCS #11 specification. The session is established by calling
C_OpenSession
, and specifying which slot the application would like to use.
Opening a PKCS #11 session:
CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE;
xResult = pxFunctionList->C_OpenSession( pxSlotId[0],
CKF_SERIAL_SESSION | CKF_RW_SESSION,
NULL, /* Application defined pointer. */
NULL, /* Callback function. */
&hSession );
configASSERT( xResult == CKR_OK );
Now that the session is established, we can use it to generate a random number. Random numbers are used heavily in
cryptographic operations, and many algorithms used in cryptography rely on the "randomness" of a random number
generator to make sure the cipher isn't broken.
Generating a buffer of random numbers:
CK_BYTE xRandomData[ 10 ] = { 0 };
xResult = pxFunctionList->C_GenerateRandom( hSession,
xRandomData,
sizeof( xRandomData ) );
configASSERT( xResult == CKR_OK );
for( ulIndex = 0; ulIndex < sizeof( xRandomData ); ulIndex++ )
{
configPRINTF( ( "Generated random number: %x\r\n", xRandomData[ ulIndex ] ) );
}
Since the cryptographic operation is now complete, the demo cleans up the resources with these steps:
- close the active session,
- call
C_Finalize
to de-initialize the PKCS #11 stack.
Cleaning up:
xResult = pxFunctionList->C_CloseSession( hSession );
configASSERT( xResult == CKR_OK );
xResult = pxFunctionList->C_Finalize( NULL );
configASSERT( xResult == CKR_OK );
Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.