Download FreeRTOS
 

Quality RTOS & Embedded Software

LIBRARIES
WHAT'S NEW
Simplifying Authenticated Cloud Connectivity for Any Device.
Designing an energy efficient and cloud-connected IoT solution with CoAP.
Introducing FreeRTOS Kernel version 11.0.0:
FreeRTOS Roadmap and Code Contribution process.
OPC-UA over TSN with FreeRTOS.

PKCS #11 Functions

PKCS #11 includes many functions and supports a variety of use cases. The FreeRTOS PKCS #11 library only implements a small subset of functions for the use cases first describe.  Each function is fully documented in the PKCS #11 standard.  The terminology can be difficult for the first reader, so we also provide examples code below. PKCS #11 operational functions:
  • C_Random.  Random numbers are required by the TCP/IP stack (for example, when selecting an initial sequence number), within cryptographic algorithms, and during the TLS handshake.
  • C_Sign.  Signing a message sent to another network node allows that node to identify the messages true origin.
  • C_FindObject.  Finds an object managed by PKCS #11, for example the client certificate.
  • C_GetAttributeValue.  This API gets the value of an object's attribute, for example if the object is a key type or a certificate type.
PKCS #11 administrative functions:
  • C_Initialize.  Initializes the crypto libraries.
  • C_Finalize.  Cleans up resources used by the crypto libraries.
  • C_OpenSession. Opens a session (or connection) between a software application and a particular crypto token.
  • C_CloseSession.  Closes the session opened by C_OpenSession.
  • C_Login.  Logs into a token.
  • C_GetFunctionList.  Obtains a struct containing function pointers for use in calling the PKCS #11 APIs.
  • C_GetSlotList. Obtains the list of slots, which can be used to access any token in a slot.

Code Samples

Here are some example code snippets that showcase how one might use the PKCS #11 API.

Using C_GenerateRandom:

This code snippet shows how to use PKCS #11 to get a random number. For example, this function can be used for FreeRTOS-Plus-TCP to generate random TCP numbers.
CK_RV xGetRandomNumber( uint8_t * pRandomNumber,
                        size_t xNumBytes )
{
  CK_RV xResult = CKR_OK;
  CK_SESSION_HANDLE xSession;
  CK_FUNCTION_LIST_PTR pxFunctionList;

  if( ( pRandomNumber == NULL ) || ( xNumBytes == 0 ) )
  {
    xResult = CKR_ARGUMENTS_BAD;
  }

  if( xResult == CKR_OK )
  {
    xResult = C_GetFunctionList( &pxFunctionList );
  }

  if( xResult == CKR_OK )
  {
    xResult = prvGetRandomnessSession( &xSession );
  }

  if( xResult == CKR_OK )
  {
    xResult = pxFunctionList->C_GenerateRandom( xSession, pRandomNumber, xNumBytes );
  }

  return xResult;
}

Using C_GetAttributeValue to acquire the value of an object's attribute:

static int prvReadCertificateIntoContext( TLSContext_t * pxTlsContext, 
                                          const char * pcLabelName, 
                                          CK_OBJECT_CLASS xClass,
                                          mbedtls_x509_crt * pxCertificateContext )
{
    BaseType_t xResult = CKR_OK;
    CK_ATTRIBUTE xTemplate = { 0 };
    CK_OBJECT_HANDLE xCertObj = 0;

     /* Get the handle of the certificate. */ 
    xResult = xFindObjectWithLabelAndClass( pxTlsContext->xP11Session, 
                                            pcLabelName, 
                                            xClass, 
                                            &xCertObj );

    if ( ( CKR_OK == xResult ) && ( xCertObj == CK_INVALID_HANDLE ) ) 
    {
        xResult = CKR_OBJECT_HANDLE_INVALID;
    }

     /* Query the certificate size. */ 
    if ( 0 == xResult ) 
    {
        xTemplate.type = CKA_VALUE;
        xTemplate.ulValueLen = 0;
        xTemplate.pValue = NULL;
        xResult = ( BaseType_t ) pxTlsContext->
                  pxP11FunctionList->C_GetAttributeValue( pxTlsContext->xP11Session, 
                                                          xCertObj, 
                                                          &xTemplate, 
                                                          1 );
    }

     /* Create a buffer for the certificate. */ 
    if ( 0 == xResult ) 
    { 
        xTemplate.pValue = pvPortMalloc( xTemplate.ulValueLen );  
                                                    
        if ( NULL == xTemplate.pValue ) 
        {
            xResult = ( BaseType_t ) CKR_HOST_MEMORY;
        }
    }

     /* Export the certificate. */ 
    if ( 0 == xResult ) 
    {
        xResult = ( BaseType_t ) pxTlsContext->
                    pxP11FunctionList->C_GetAttributeValue( pxTlsContext->xP11Session, 
                                                            xCertObj, 
                                                            &xTemplate, 
                                                            1 );
    }

     /* Decode the certificate. */ 
    if ( 0 == xResult ) 
    {
        xResult = mbedtls_x509_crt_parse( pxCertificateContext, 
                                          ( const unsigned char * ) xTemplate.pValue, 
                                          xTemplate.ulValueLen );
    }

     /* Free memory. */  
    if ( NULL != xTemplate.pValue ) 
    {
        vPortFree( xTemplate.pValue );
    }

    return xResult;
}

Using C_Sign to add our private key's signature to our message:

This is a snippet from the TLS layer of our code that uses PKCS #11 for the C_Sign operation. In this example, just before we send a TLS message, we call into the PKCS #11 layer and ask it to sign our message with our private key.

static int prvPrivateKeySigningCallback( void * pvContext, 
                                         mbedtls_md_type_t xMdAlg, 
                                         const unsigned char * pucHash,
                                         size_t xHashLen, 
                                         unsigned char* pucSig, 
                                         size_t * pxSigLen,
                                         int ( *piRng ) ( void *, unsigned char *, size_t ),
                                         void * pvRng )
{
  CK_RV xResult = CKR_OK;
  int lFinalResult = 0;
  TLSContext_t * pxTLSContext = ( TLSContext_t* ) pvContext;
  CK_MECHANISM xMech = { 0 };
  CK_BYTE xToBeSigned[ 256 ];
  CK_ULONG xToBeSignedLen = sizeof( xToBeSigned );

  /* Unreferenced parameters. */
  ( void )( piRng );
  ( void )( pvRng );
  ( void )( xMdAlg);

  /* Sanity check buffer length. */
  if ( xHashLen > sizeof( xToBeSigned ) ) 
  {
    xResult = CKR_ARGUMENTS_BAD;
  }

  /* Format the hash data to be signed. */
  if ( CKK_RSA == pxTLSContext->xKeyType ) 
  {
    xMech.mechanism = CKM_RSA_PKCS;

    /* mbedTLS expects hashed data without padding, but PKCS #11 C_Sign
    * function performs a hash & sign if hash algorithm is specified. This
    * helper function applies padding indicating data was hashed with
    * SHA-256 while still allowing pre-hashed data to be provided. */
    xResult = vAppendSHA256AlgorithmIdentifierSequence( ( uint8_t * ) pucHash, xToBeSigned );
    xToBeSignedLen = pkcs11RSA_SIGNATURE_INPUT_LENGTH;
  } 
  else if ( CKK_EC == pxTLSContext->xKeyType ) 
  { 
    xMech.mechanism = CKM_ECDSA;
    memcpy( xToBeSigned, pucHash, xHashLen );
    xToBeSignedLen = xHashLen;
  } 
  else 
  {
     xResult = CKR_ARGUMENTS_BAD;
  }

  if ( CKR_OK == xResult ) 
  {
    /* Use the PKCS#11 module to sign. */
    xResult = pxTLSContext->
              pxP11FunctionList->C_SignInit( pxTLSContext->xP11Session, 
                                             &xMech, 
                                             pxTLSContext->xP11PrivateKey );
  }

  if ( CKR_OK == xResult ) 
  {
    *pxSigLen = sizeof( xToBeSigned );
    xResult = pxTLSContext->
              pxP11FunctionList->C_Sign( ( CK_SESSION_HANDLE ) pxTLSContext->xP11Session, 
                                           xToBeSigned, 
                                           xToBeSignedLen, 
                                           pucSig, 
                                           ( CK_ULONG_PTR ) pxSigLen );
  }

  if ( ( xResult == CKR_OK ) && ( CKK_EC == pxTLSContext->xKeyType ) ) 
  {
      /* PKCS #11 for P256 returns a 64-byte signature with 32 bytes for R and
       * 32 bytes for S. This must be converted to an ASN.1 encoded array. */
      if (*pxSigLen != pkcs11ECDSA_P256_SIGNATURE_LENGTH) 
      {
        xResult = CKR_FUNCTION_FAILED;
      }

      if ( xResult == CKR_OK ) 
      {
        PKI_pkcs11SignatureTombedTLSSignature( pucSig, pxSigLen );
      }
  }

  if ( xResult != CKR_OK ) 
  {
    TLS_PRINT( ( "ERROR: Failure in signing callback: %d \r\n", xResult ) );
    lFinalResult = TLS_ERROR_SIGN;
  }

  return lFinalResult;
}
Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.