815 lines
28 KiB
C
815 lines
28 KiB
C
/*
|
|
* FreeRTOS Secure Sockets V1.3.1
|
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
* this software and associated documentation files (the "Software"), to deal in
|
|
* the Software without restriction, including without limitation the rights to
|
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
|
* subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* http://aws.amazon.com/freertos
|
|
* http://www.FreeRTOS.org
|
|
*/
|
|
|
|
/* FreeRTOS includes. */
|
|
#include "FreeRTOS.h"
|
|
#include "FreeRTOSIPConfig.h"
|
|
#include "list.h"
|
|
#include "semphr.h"
|
|
#include "FreeRTOS_IP.h"
|
|
#include "FreeRTOS_Sockets.h"
|
|
#include "iot_secure_sockets.h"
|
|
#include "iot_tls.h"
|
|
#include "task.h"
|
|
#include "core_pkcs11.h"
|
|
#include "iot_crypto.h"
|
|
|
|
/* Internal context structure. */
|
|
typedef struct SSOCKETContext
|
|
{
|
|
Socket_t xSocket;
|
|
char * pcDestination;
|
|
void * pvTLSContext;
|
|
BaseType_t xRequireTLS;
|
|
BaseType_t xSendFlags;
|
|
BaseType_t xRecvFlags;
|
|
char * pcServerCertificate;
|
|
uint32_t ulServerCertificateLength;
|
|
char ** ppcAlpnProtocols;
|
|
uint32_t ulAlpnProtocolsCount;
|
|
BaseType_t xConnectAttempted;
|
|
} SSOCKETContext_t, * SSOCKETContextPtr_t;
|
|
|
|
/*
|
|
* Helper routines.
|
|
*/
|
|
|
|
/*
|
|
* @brief Network send callback.
|
|
*/
|
|
static BaseType_t prvNetworkSend( void * pvContext,
|
|
const unsigned char * pucData,
|
|
size_t xDataLength )
|
|
{
|
|
SSOCKETContextPtr_t pxContext = ( SSOCKETContextPtr_t ) pvContext; /*lint !e9087 cast used for portability. */
|
|
|
|
return FreeRTOS_send( pxContext->xSocket, pucData, xDataLength, pxContext->xSendFlags );
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/*
|
|
* @brief Network receive callback.
|
|
*/
|
|
static BaseType_t prvNetworkRecv( void * pvContext,
|
|
unsigned char * pucReceiveBuffer,
|
|
size_t xReceiveLength )
|
|
{
|
|
SSOCKETContextPtr_t pxContext = ( SSOCKETContextPtr_t ) pvContext; /*lint !e9087 cast used for portability. */
|
|
|
|
return FreeRTOS_recv( pxContext->xSocket, pucReceiveBuffer, xReceiveLength, pxContext->xRecvFlags );
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/*
|
|
* Interface routines.
|
|
*/
|
|
|
|
int32_t SOCKETS_Close( Socket_t xSocket )
|
|
{
|
|
SSOCKETContextPtr_t pxContext = ( SSOCKETContextPtr_t ) xSocket; /*lint !e9087 cast used for portability. */
|
|
uint32_t ulProtocol;
|
|
int32_t lReturn;
|
|
|
|
if( ( xSocket != SOCKETS_INVALID_SOCKET ) && ( NULL != pxContext ) )
|
|
{
|
|
/* Clean-up destination string. */
|
|
if( NULL != pxContext->pcDestination )
|
|
{
|
|
vPortFree( pxContext->pcDestination );
|
|
}
|
|
|
|
/* Clean-up server certificate. */
|
|
if( NULL != pxContext->pcServerCertificate )
|
|
{
|
|
vPortFree( pxContext->pcServerCertificate );
|
|
}
|
|
|
|
/* Clean-up application protocol array. */
|
|
if( NULL != pxContext->ppcAlpnProtocols )
|
|
{
|
|
for( ulProtocol = 0;
|
|
ulProtocol < pxContext->ulAlpnProtocolsCount;
|
|
ulProtocol++ )
|
|
{
|
|
if( NULL != pxContext->ppcAlpnProtocols[ ulProtocol ] )
|
|
{
|
|
vPortFree( pxContext->ppcAlpnProtocols[ ulProtocol ] );
|
|
}
|
|
}
|
|
|
|
vPortFree( pxContext->ppcAlpnProtocols );
|
|
}
|
|
|
|
/* Clean-up TLS context. */
|
|
if( pdTRUE == pxContext->xRequireTLS )
|
|
{
|
|
TLS_Cleanup( pxContext->pvTLSContext );
|
|
}
|
|
|
|
/* Close the underlying socket handle. */
|
|
( void ) FreeRTOS_closesocket( pxContext->xSocket );
|
|
|
|
/* Free the context. */
|
|
vPortFree( pxContext );
|
|
lReturn = SOCKETS_ERROR_NONE;
|
|
}
|
|
else
|
|
{
|
|
lReturn = SOCKETS_EINVAL;
|
|
}
|
|
|
|
return lReturn;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int32_t SOCKETS_Connect( Socket_t xSocket,
|
|
SocketsSockaddr_t * pxAddress,
|
|
Socklen_t xAddressLength )
|
|
{
|
|
int32_t lStatus = SOCKETS_ERROR_NONE;
|
|
SSOCKETContextPtr_t pxContext = ( SSOCKETContextPtr_t ) xSocket; /*lint !e9087 cast used for portability. */
|
|
TLSParams_t xTLSParams = { 0 };
|
|
struct freertos_sockaddr xTempAddress = { 0 };
|
|
|
|
if( ( pxContext != ( SSOCKETContextPtr_t ) SOCKETS_INVALID_SOCKET ) && ( pxAddress != NULL ) )
|
|
{
|
|
/* A connection was attempted. If this function fails, then the socket is invalid and the user
|
|
* must call SOCKETS_Close(), on this socket, and SOCKETS_Socket() to get a new socket. */
|
|
pxContext->xConnectAttempted = pdTRUE;
|
|
|
|
/* Connect the wrapped socket. */
|
|
xTempAddress.sin_addr = pxAddress->ulAddress;
|
|
xTempAddress.sin_family = pxAddress->ucSocketDomain;
|
|
xTempAddress.sin_len = ( uint8_t ) sizeof( xTempAddress );
|
|
xTempAddress.sin_port = pxAddress->usPort;
|
|
lStatus = FreeRTOS_connect( pxContext->xSocket, &xTempAddress, xAddressLength );
|
|
|
|
/* Negotiate TLS if requested. */
|
|
if( ( SOCKETS_ERROR_NONE == lStatus ) && ( pdTRUE == pxContext->xRequireTLS ) )
|
|
{
|
|
xTLSParams.ulSize = sizeof( xTLSParams );
|
|
xTLSParams.pcDestination = pxContext->pcDestination;
|
|
xTLSParams.pcServerCertificate = pxContext->pcServerCertificate;
|
|
xTLSParams.ulServerCertificateLength = pxContext->ulServerCertificateLength;
|
|
xTLSParams.ppcAlpnProtocols = ( const char ** ) pxContext->ppcAlpnProtocols;
|
|
xTLSParams.ulAlpnProtocolsCount = pxContext->ulAlpnProtocolsCount;
|
|
xTLSParams.pvCallerContext = pxContext;
|
|
xTLSParams.pxNetworkRecv = prvNetworkRecv;
|
|
xTLSParams.pxNetworkSend = prvNetworkSend;
|
|
lStatus = TLS_Init( &pxContext->pvTLSContext, &xTLSParams );
|
|
|
|
if( SOCKETS_ERROR_NONE == lStatus )
|
|
{
|
|
lStatus = TLS_Connect( pxContext->pvTLSContext );
|
|
|
|
if( lStatus < 0 )
|
|
{
|
|
lStatus = SOCKETS_TLS_HANDSHAKE_ERROR;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lStatus = SOCKETS_SOCKET_ERROR;
|
|
}
|
|
|
|
return lStatus;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
uint32_t SOCKETS_GetHostByName( const char * pcHostName )
|
|
{
|
|
return FreeRTOS_gethostbyname( pcHostName );
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int32_t SOCKETS_Recv( Socket_t xSocket,
|
|
void * pvBuffer,
|
|
size_t xBufferLength,
|
|
uint32_t ulFlags )
|
|
{
|
|
int32_t lStatus = SOCKETS_SOCKET_ERROR;
|
|
SSOCKETContextPtr_t pxContext = ( SSOCKETContextPtr_t ) xSocket; /*lint !e9087 cast used for portability. */
|
|
|
|
if( ( xSocket != SOCKETS_INVALID_SOCKET ) &&
|
|
( pvBuffer != NULL ) )
|
|
{
|
|
pxContext->xRecvFlags = ( BaseType_t ) ulFlags;
|
|
|
|
if( pdTRUE == pxContext->xRequireTLS )
|
|
{
|
|
/* Receive through TLS pipe, if negotiated. */
|
|
lStatus = TLS_Recv( pxContext->pvTLSContext, pvBuffer, xBufferLength );
|
|
}
|
|
else
|
|
{
|
|
/* Receive unencrypted. */
|
|
lStatus = prvNetworkRecv( pxContext, pvBuffer, xBufferLength );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lStatus = SOCKETS_EINVAL;
|
|
}
|
|
|
|
return lStatus;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int32_t SOCKETS_Send( Socket_t xSocket,
|
|
const void * pvBuffer,
|
|
size_t xDataLength,
|
|
uint32_t ulFlags )
|
|
{
|
|
int32_t lStatus = SOCKETS_SOCKET_ERROR;
|
|
SSOCKETContextPtr_t pxContext = ( SSOCKETContextPtr_t ) xSocket; /*lint !e9087 cast used for portability. */
|
|
|
|
if( ( xSocket != SOCKETS_INVALID_SOCKET ) &&
|
|
( pvBuffer != NULL ) )
|
|
{
|
|
pxContext->xSendFlags = ( BaseType_t ) ulFlags;
|
|
|
|
if( pdTRUE == pxContext->xRequireTLS )
|
|
{
|
|
/* Send through TLS pipe, if negotiated. */
|
|
lStatus = TLS_Send( pxContext->pvTLSContext, pvBuffer, xDataLength );
|
|
}
|
|
else
|
|
{
|
|
/* Send unencrypted. */
|
|
lStatus = prvNetworkSend( pxContext, pvBuffer, xDataLength );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lStatus = SOCKETS_EINVAL;
|
|
}
|
|
|
|
return lStatus;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int32_t SOCKETS_SetSockOpt( Socket_t xSocket,
|
|
int32_t lLevel,
|
|
int32_t lOptionName,
|
|
const void * pvOptionValue,
|
|
size_t xOptionLength )
|
|
{
|
|
int32_t lStatus = SOCKETS_ERROR_NONE;
|
|
TickType_t xTimeout;
|
|
SSOCKETContextPtr_t pxContext = ( SSOCKETContextPtr_t ) xSocket; /*lint !e9087 cast used for portability. */
|
|
char ** ppcAlpnIn = ( char ** ) pvOptionValue;
|
|
size_t xLength = 0;
|
|
uint32_t ulProtocol;
|
|
|
|
if( ( xSocket != SOCKETS_INVALID_SOCKET ) && ( xSocket != NULL ) )
|
|
{
|
|
switch( lOptionName )
|
|
{
|
|
case SOCKETS_SO_SERVER_NAME_INDICATION:
|
|
|
|
/* Do not set the SNI options if the socket is possibly already connected. */
|
|
if( pxContext->xConnectAttempted == pdTRUE )
|
|
{
|
|
lStatus = SOCKETS_EISCONN;
|
|
}
|
|
|
|
/* Non-NULL destination string indicates that SNI extension should
|
|
* be used during TLS negotiation. */
|
|
else if( NULL == ( pxContext->pcDestination =
|
|
( char * ) pvPortMalloc( 1U + xOptionLength ) ) )
|
|
{
|
|
lStatus = SOCKETS_ENOMEM;
|
|
}
|
|
else
|
|
{
|
|
memcpy( pxContext->pcDestination, pvOptionValue, xOptionLength );
|
|
pxContext->pcDestination[ xOptionLength ] = '\0';
|
|
}
|
|
|
|
break;
|
|
|
|
case SOCKETS_SO_TRUSTED_SERVER_CERTIFICATE:
|
|
|
|
/* Do not set the trusted server certificate if the socket is possibly already connected. */
|
|
if( pxContext->xConnectAttempted == pdTRUE )
|
|
{
|
|
lStatus = SOCKETS_EISCONN;
|
|
}
|
|
|
|
/* Non-NULL server certificate field indicates that the default trust
|
|
* list should not be used. */
|
|
else if( NULL == ( pxContext->pcServerCertificate =
|
|
( char * ) pvPortMalloc( xOptionLength ) ) )
|
|
{
|
|
lStatus = SOCKETS_ENOMEM;
|
|
}
|
|
else
|
|
{
|
|
memcpy( pxContext->pcServerCertificate, pvOptionValue, xOptionLength );
|
|
pxContext->ulServerCertificateLength = xOptionLength;
|
|
}
|
|
|
|
break;
|
|
|
|
case SOCKETS_SO_REQUIRE_TLS:
|
|
|
|
/* Do not set the TLS option if the socket is possibly already connected. */
|
|
if( pxContext->xConnectAttempted == pdTRUE )
|
|
{
|
|
lStatus = SOCKETS_EISCONN;
|
|
}
|
|
else
|
|
{
|
|
pxContext->xRequireTLS = pdTRUE;
|
|
}
|
|
|
|
break;
|
|
|
|
case SOCKETS_SO_ALPN_PROTOCOLS:
|
|
|
|
/* Do not set the ALPN option if the socket is already connected. */
|
|
if( pxContext->xConnectAttempted == pdTRUE )
|
|
{
|
|
lStatus = SOCKETS_EISCONN;
|
|
break;
|
|
}
|
|
|
|
/* Allocate a sufficiently long array of pointers. */
|
|
pxContext->ulAlpnProtocolsCount = 1 + xOptionLength;
|
|
|
|
if( NULL == ( pxContext->ppcAlpnProtocols =
|
|
( char ** ) pvPortMalloc( pxContext->ulAlpnProtocolsCount * sizeof( char * ) ) ) )
|
|
{
|
|
lStatus = SOCKETS_ENOMEM;
|
|
}
|
|
else
|
|
{
|
|
/* Zero out the pointers. */
|
|
memset( pxContext->ppcAlpnProtocols,
|
|
0,
|
|
pxContext->ulAlpnProtocolsCount * sizeof( char * ) );
|
|
}
|
|
|
|
/* Copy each protocol string. */
|
|
for( ulProtocol = 0;
|
|
( ulProtocol < pxContext->ulAlpnProtocolsCount - 1 ) &&
|
|
( pdFREERTOS_ERRNO_NONE == lStatus );
|
|
ulProtocol++ )
|
|
{
|
|
xLength = strlen( ppcAlpnIn[ ulProtocol ] );
|
|
|
|
if( NULL == ( pxContext->ppcAlpnProtocols[ ulProtocol ] =
|
|
( char * ) pvPortMalloc( 1 + xLength ) ) )
|
|
{
|
|
lStatus = SOCKETS_ENOMEM;
|
|
}
|
|
else
|
|
{
|
|
memcpy( pxContext->ppcAlpnProtocols[ ulProtocol ],
|
|
ppcAlpnIn[ ulProtocol ],
|
|
xLength );
|
|
pxContext->ppcAlpnProtocols[ ulProtocol ][ xLength ] = '\0';
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case SOCKETS_SO_NONBLOCK:
|
|
xTimeout = 0;
|
|
|
|
/* Non-blocking connect is not supported. Socket may be set to nonblocking
|
|
* only after a connection is made. */
|
|
if( pdTRUE == pxContext->xConnectAttempted )
|
|
{
|
|
lStatus = FreeRTOS_setsockopt( pxContext->xSocket,
|
|
lLevel,
|
|
SOCKETS_SO_RCVTIMEO,
|
|
&xTimeout,
|
|
sizeof( xTimeout ) );
|
|
|
|
if( lStatus == SOCKETS_ERROR_NONE )
|
|
{
|
|
lStatus = FreeRTOS_setsockopt( pxContext->xSocket,
|
|
lLevel,
|
|
SOCKETS_SO_SNDTIMEO,
|
|
&xTimeout,
|
|
sizeof( xTimeout ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lStatus = SOCKETS_EISCONN;
|
|
}
|
|
|
|
break;
|
|
|
|
case SOCKETS_SO_RCVTIMEO:
|
|
case SOCKETS_SO_SNDTIMEO:
|
|
/* Comply with Berkeley standard - a 0 timeout is wait forever. */
|
|
xTimeout = *( ( const TickType_t * ) pvOptionValue ); /*lint !e9087 pvOptionValue passed should be of TickType_t */
|
|
|
|
if( xTimeout == 0U )
|
|
{
|
|
xTimeout = portMAX_DELAY;
|
|
}
|
|
|
|
lStatus = FreeRTOS_setsockopt( pxContext->xSocket,
|
|
lLevel,
|
|
lOptionName,
|
|
&xTimeout,
|
|
xOptionLength );
|
|
break;
|
|
|
|
default:
|
|
lStatus = FreeRTOS_setsockopt( pxContext->xSocket,
|
|
lLevel,
|
|
lOptionName,
|
|
pvOptionValue,
|
|
xOptionLength );
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lStatus = SOCKETS_EINVAL;
|
|
}
|
|
|
|
return lStatus;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int32_t SOCKETS_Shutdown( Socket_t xSocket,
|
|
uint32_t ulHow )
|
|
{
|
|
int32_t lReturn;
|
|
SSOCKETContextPtr_t pxContext = ( SSOCKETContextPtr_t ) xSocket; /*lint !e9087 cast used for portability. */
|
|
|
|
if( ( xSocket != SOCKETS_INVALID_SOCKET ) && ( xSocket != NULL ) )
|
|
{
|
|
lReturn = FreeRTOS_shutdown( pxContext->xSocket, ( BaseType_t ) ulHow );
|
|
}
|
|
else
|
|
{
|
|
lReturn = SOCKETS_EINVAL;
|
|
}
|
|
|
|
return lReturn;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
Socket_t SOCKETS_Socket( int32_t lDomain,
|
|
int32_t lType,
|
|
int32_t lProtocol )
|
|
{
|
|
SSOCKETContextPtr_t pxContext = NULL;
|
|
Socket_t xSocket;
|
|
|
|
/* Ensure that only supported values are supplied. */
|
|
configASSERT( lDomain == SOCKETS_AF_INET );
|
|
configASSERT( lType == SOCKETS_SOCK_STREAM );
|
|
configASSERT( lProtocol == SOCKETS_IPPROTO_TCP );
|
|
|
|
/* Create the wrapped socket. */
|
|
xSocket = FreeRTOS_socket( lDomain, lType, lProtocol );
|
|
|
|
if( xSocket != FREERTOS_INVALID_SOCKET )
|
|
{
|
|
/* Allocate the internal context structure. */
|
|
if( NULL == ( pxContext = pvPortMalloc( sizeof( SSOCKETContext_t ) ) ) )
|
|
{
|
|
/* Need to close socket. */
|
|
( void ) FreeRTOS_closesocket( xSocket );
|
|
pxContext = ( SSOCKETContextPtr_t ) SOCKETS_INVALID_SOCKET;
|
|
}
|
|
else
|
|
{
|
|
memset( pxContext, 0, sizeof( SSOCKETContext_t ) );
|
|
pxContext->xSocket = xSocket;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pxContext = ( SSOCKETContextPtr_t ) SOCKETS_INVALID_SOCKET;
|
|
}
|
|
|
|
return ( Socket_t ) pxContext;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
BaseType_t SOCKETS_Init( void )
|
|
{
|
|
/* Empty initialization for this port. */
|
|
return pdPASS;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Create a static PKCS #11 crypto session handle to share across socket
|
|
* and FreeRTOS+TCP threads. Assume that two or more threads may race to be the
|
|
* first to initialize the static and handle that case accordingly.
|
|
*/
|
|
static CK_RV prvSocketsGetCryptoSession( SemaphoreHandle_t * pxSessionLock,
|
|
CK_SESSION_HANDLE * pxSession,
|
|
CK_FUNCTION_LIST_PTR_PTR ppxFunctionList )
|
|
{
|
|
CK_RV xResult = CKR_OK;
|
|
CK_C_GetFunctionList pxCkGetFunctionList = NULL;
|
|
static CK_SESSION_HANDLE xPkcs11Session = 0;
|
|
static CK_FUNCTION_LIST_PTR pxPkcs11FunctionList = NULL;
|
|
static StaticSemaphore_t xStaticSemaphore;
|
|
static SemaphoreHandle_t xSessionLock = NULL;
|
|
CK_ULONG ulCount = 0;
|
|
CK_SLOT_ID * pxSlotIds = NULL;
|
|
|
|
/* Check if one-time initialization of the lock is needed.*/
|
|
portENTER_CRITICAL();
|
|
|
|
if( NULL == xSessionLock )
|
|
{
|
|
xSessionLock = xSemaphoreCreateMutexStatic( &xStaticSemaphore );
|
|
}
|
|
|
|
*pxSessionLock = xSessionLock;
|
|
portEXIT_CRITICAL();
|
|
|
|
/* Check if one-time initialization of the crypto handle is needed.*/
|
|
xSemaphoreTake( xSessionLock, portMAX_DELAY );
|
|
|
|
if( 0 == xPkcs11Session )
|
|
{
|
|
/* One-time initialization. */
|
|
|
|
/* Ensure that the PKCS#11 module is initialized. We don't keep the
|
|
* scheduler stopped here, since we don't want to make assumptions about hardware
|
|
* requirements for accessing a crypto module. */
|
|
|
|
pxCkGetFunctionList = C_GetFunctionList;
|
|
xResult = pxCkGetFunctionList( &pxPkcs11FunctionList );
|
|
|
|
if( CKR_OK == xResult )
|
|
{
|
|
xResult = xInitializePkcs11Token();
|
|
}
|
|
|
|
/* Get the crypto token slot count. */
|
|
if( CKR_OK == xResult )
|
|
{
|
|
xResult = pxPkcs11FunctionList->C_GetSlotList( CK_TRUE,
|
|
NULL,
|
|
&ulCount );
|
|
}
|
|
|
|
/* Allocate memory to store the token slots. */
|
|
if( CKR_OK == xResult )
|
|
{
|
|
pxSlotIds = ( CK_SLOT_ID * ) pvPortMalloc( sizeof( CK_SLOT_ID ) * ulCount );
|
|
|
|
if( NULL == pxSlotIds )
|
|
{
|
|
xResult = CKR_HOST_MEMORY;
|
|
}
|
|
}
|
|
|
|
/* Get all of the available private key slot identities. */
|
|
if( CKR_OK == xResult )
|
|
{
|
|
xResult = pxPkcs11FunctionList->C_GetSlotList( CK_TRUE,
|
|
pxSlotIds,
|
|
&ulCount );
|
|
}
|
|
|
|
/* Start a session with the PKCS#11 module. */
|
|
if( CKR_OK == xResult )
|
|
{
|
|
xResult = pxPkcs11FunctionList->C_OpenSession( pxSlotIds[ 0 ],
|
|
CKF_SERIAL_SESSION,
|
|
NULL,
|
|
NULL,
|
|
&xPkcs11Session );
|
|
}
|
|
}
|
|
|
|
*pxSession = xPkcs11Session;
|
|
*ppxFunctionList = pxPkcs11FunctionList;
|
|
xSemaphoreGive( xSessionLock );
|
|
|
|
if( NULL != pxSlotIds )
|
|
{
|
|
vPortFree( pxSlotIds );
|
|
}
|
|
|
|
return xResult;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/*uint32_t ulRand( void ) __attribute__ ((deprecated)) */
|
|
#if 0
|
|
|
|
/* Why is this function between #if 0? Because the function ulRand()
|
|
* is now reprecated. The function is still shown here for users who
|
|
* are looking for it.
|
|
* It is deprecated because a return value of 0 would mean that the
|
|
* RND module is broken. This was confusing because 0 is also a
|
|
* possible valid random number. */
|
|
uint32_t ulRand( void )
|
|
{
|
|
uint32_t ulNumber = 0uL;
|
|
BaseType_t xResult = xApplicationGetRandomNumber( &( ulNumber ) );
|
|
|
|
if( xResult == pdFALSE )
|
|
{
|
|
ulNumber = 0uL;
|
|
}
|
|
else
|
|
{
|
|
/* Function succeeded, a random number will be returned. */
|
|
}
|
|
|
|
return ulNumber;
|
|
}
|
|
#endif /* 0 */
|
|
|
|
BaseType_t xApplicationGetRandomNumber( uint32_t * pulNumber )
|
|
{
|
|
CK_RV xResult = 0;
|
|
SemaphoreHandle_t xSessionLock = NULL;
|
|
CK_SESSION_HANDLE xPkcs11Session = 0;
|
|
CK_FUNCTION_LIST_PTR pxPkcs11FunctionList = NULL;
|
|
uint32_t ulRandomValue = 0;
|
|
BaseType_t xReturn; /* Return pdTRUE if successful */
|
|
|
|
xResult = prvSocketsGetCryptoSession( &xSessionLock,
|
|
&xPkcs11Session,
|
|
&pxPkcs11FunctionList );
|
|
|
|
if( 0 == xResult )
|
|
{
|
|
/* Request a sequence of cryptographically random byte values using
|
|
* PKCS#11. */
|
|
xResult = pxPkcs11FunctionList->C_GenerateRandom( xPkcs11Session,
|
|
( CK_BYTE_PTR ) &ulRandomValue,
|
|
sizeof( ulRandomValue ) );
|
|
}
|
|
|
|
/* Check if any of the API calls failed. */
|
|
if( 0 == xResult )
|
|
{
|
|
xReturn = pdTRUE;
|
|
*( pulNumber ) = ulRandomValue;
|
|
}
|
|
else
|
|
{
|
|
xReturn = pdFALSE;
|
|
*( pulNumber ) = 0uL;
|
|
}
|
|
|
|
return xReturn;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Generate a TCP Initial Sequence Number that is reasonably difficult
|
|
* to predict, per https://tools.ietf.org/html/rfc6528.
|
|
*/
|
|
uint32_t ulApplicationGetNextSequenceNumber( uint32_t ulSourceAddress,
|
|
uint16_t usSourcePort,
|
|
uint32_t ulDestinationAddress,
|
|
uint16_t usDestinationPort )
|
|
{
|
|
CK_RV xResult = CKR_OK;
|
|
SemaphoreHandle_t xSessionLock = NULL;
|
|
CK_SESSION_HANDLE xPkcs11Session = 0;
|
|
CK_FUNCTION_LIST_PTR pxPkcs11FunctionList = NULL;
|
|
CK_MECHANISM xMechSha256 = { 0 };
|
|
uint8_t ucSha256Result[ cryptoSHA256_DIGEST_BYTES ];
|
|
CK_ULONG ulLength = sizeof( ucSha256Result );
|
|
uint32_t ulNextSequenceNumber = 0;
|
|
static uint64_t ullKey;
|
|
static CK_BBOOL xKeyIsInitialized = CK_FALSE;
|
|
|
|
/* Acquire a crypto session handle. */
|
|
xResult = prvSocketsGetCryptoSession( &xSessionLock,
|
|
&xPkcs11Session,
|
|
&pxPkcs11FunctionList );
|
|
|
|
if( CKR_OK == xResult )
|
|
{
|
|
xSemaphoreTake( xSessionLock, portMAX_DELAY );
|
|
|
|
if( CK_FALSE == xKeyIsInitialized )
|
|
{
|
|
/* One-time initialization, per boot, of the random seed. */
|
|
xResult = pxPkcs11FunctionList->C_GenerateRandom( xPkcs11Session,
|
|
( CK_BYTE_PTR ) &ullKey,
|
|
sizeof( ullKey ) );
|
|
|
|
if( xResult == CKR_OK )
|
|
{
|
|
xKeyIsInitialized = CK_TRUE;
|
|
}
|
|
}
|
|
|
|
xSemaphoreGive( xSessionLock );
|
|
}
|
|
|
|
/* Lock the shared crypto session. */
|
|
xSemaphoreTake( xSessionLock, portMAX_DELAY );
|
|
|
|
/* Start a hash. */
|
|
if( CKR_OK == xResult )
|
|
{
|
|
xMechSha256.mechanism = CKM_SHA256;
|
|
xResult = pxPkcs11FunctionList->C_DigestInit( xPkcs11Session, &xMechSha256 );
|
|
}
|
|
|
|
/* Hash the seed. */
|
|
if( CKR_OK == xResult )
|
|
{
|
|
xResult = pxPkcs11FunctionList->C_DigestUpdate( xPkcs11Session,
|
|
( CK_BYTE_PTR ) &ullKey,
|
|
sizeof( ullKey ) );
|
|
}
|
|
|
|
/* Hash the source address. */
|
|
if( CKR_OK == xResult )
|
|
{
|
|
xResult = pxPkcs11FunctionList->C_DigestUpdate( xPkcs11Session,
|
|
( CK_BYTE_PTR ) &ulSourceAddress,
|
|
sizeof( ulSourceAddress ) );
|
|
}
|
|
|
|
/* Hash the source port. */
|
|
if( CKR_OK == xResult )
|
|
{
|
|
xResult = pxPkcs11FunctionList->C_DigestUpdate( xPkcs11Session,
|
|
( CK_BYTE_PTR ) &usSourcePort,
|
|
sizeof( usSourcePort ) );
|
|
}
|
|
|
|
/* Hash the destination address. */
|
|
if( CKR_OK == xResult )
|
|
{
|
|
xResult = pxPkcs11FunctionList->C_DigestUpdate( xPkcs11Session,
|
|
( CK_BYTE_PTR ) &ulDestinationAddress,
|
|
sizeof( ulDestinationAddress ) );
|
|
}
|
|
|
|
/* Hash the destination port. */
|
|
if( CKR_OK == xResult )
|
|
{
|
|
xResult = pxPkcs11FunctionList->C_DigestUpdate( xPkcs11Session,
|
|
( CK_BYTE_PTR ) &usDestinationPort,
|
|
sizeof( usDestinationPort ) );
|
|
}
|
|
|
|
/* Get the hash. */
|
|
if( CKR_OK == xResult )
|
|
{
|
|
xResult = pxPkcs11FunctionList->C_DigestFinal( xPkcs11Session,
|
|
ucSha256Result,
|
|
&ulLength );
|
|
}
|
|
|
|
xSemaphoreGive( xSessionLock );
|
|
|
|
/* Use the first four bytes of the hash result as the starting point for
|
|
* all initial sequence numbers for connections based on the input 4-tuple. */
|
|
if( CKR_OK == xResult )
|
|
{
|
|
memcpy( &ulNextSequenceNumber,
|
|
ucSha256Result,
|
|
sizeof( ulNextSequenceNumber ) );
|
|
|
|
/* Add the tick count of four-tick intervals. In theory, per the RFC
|
|
* (see above), this approach still allows server equipment to optimize
|
|
* handling of connections from the same device that haven't fully timed out. */
|
|
ulNextSequenceNumber += xTaskGetTickCount() / 4;
|
|
}
|
|
|
|
return ulNextSequenceNumber;
|
|
}
|
|
/*-----------------------------------------------------------*/ |