blob: 2970edc6bbc68e7f384885c98156f364a511f3c1 [file] [log] [blame] [edit]
/*
* Amazon FreeRTOS TLS V1.1.1
* Copyright (C) 2017 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 "aws_tls.h"
#include "aws_crypto.h"
#include "aws_pkcs11.h"
#include "task.h"
#include "aws_clientcredential.h"
#include "aws_default_root_certificates.h"
/* mbedTLS includes. */
#include "mbedtls/platform.h"
#include "mbedtls/net.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/entropy.h"
#include "mbedtls/sha256.h"
#include "mbedtls/pk.h"
#include "mbedtls/debug.h"
#ifdef MBEDTLS_DEBUG_C
#define tlsDEBUG_VERBOSE 4
#endif
/* C runtime includes. */
#include <string.h>
#include <time.h>
#include <stdio.h>
/**
* @brief Internal context structure.
*
* @param[in] pcDestination Server location, can be a DNS name or IP address.
* @param[in] pcServerCertificate Server X.509 certificate in PEM format to trust.
* @param[in] ulServerCertificateLength Length in bytes of the server certificate.
* @param[in] pxNetworkRecv Callback for receiving data on an open TCP socket.
* @param[in] pxNetworkSend Callback for sending data on an open TCP socket.
* @param[in] pvCallerContext Opaque pointer provided by caller for above callbacks.
* @param[out] mbedSslCtx Connection context for mbedTLS.
* @param[out] mbedSslConfig Configuration context for mbedTLS.
* @param[out] mbedX509CA Server certificate context for mbedTLS.
* @param[out] mbedX509Cli Client certificate context for mbedTLS.
* @param[out] mbedPkAltCtx RSA crypto implementation context for mbedTLS.
* @param[out] pxP11FunctionList PKCS#11 function list structure.
* @param[out] xP11Session PKCS#11 session context.
* @param[out] xP11PrivateKey PKCS#11 private key context.
* @param[out] ulP11ModulusBytes Number of bytes in the client private key modulus.
*/
typedef struct TLSContext
{
const char * pcDestination;
const char * pcServerCertificate;
uint32_t ulServerCertificateLength;
const char ** ppcAlpnProtocols;
uint32_t ulAlpnProtocolsCount;
NetworkRecv_t pxNetworkRecv;
NetworkSend_t pxNetworkSend;
void * pvCallerContext;
/* mbedTLS. */
mbedtls_ssl_context mbedSslCtx;
mbedtls_ssl_config mbedSslConfig;
mbedtls_x509_crt mbedX509CA;
mbedtls_x509_crt mbedX509Cli;
mbedtls_pk_context mbedPkCtx;
/* PKCS#11. */
CK_FUNCTION_LIST_PTR pxP11FunctionList;
CK_SESSION_HANDLE xP11Session;
CK_OBJECT_HANDLE xP11PrivateKey;
CK_ULONG ulP11ModulusBytes;
} TLSContext_t;
/*
* Helper routines.
*/
/**
* @brief Network send callback shim.
*
* @param[in] pvContext Caller context.
* @param[in] pucData Byte buffer to send.
* @param[in] xDataLength Length of byte buffer to send.
*
* @return Number of bytes sent, or a negative value on error.
*/
static int prvNetworkSend( void * pvContext,
const unsigned char * pucData,
size_t xDataLength )
{
TLSContext_t * pCtx = ( TLSContext_t * ) pvContext; /*lint !e9087 !e9079 Allow casting void* to other types. */
return ( int ) pCtx->pxNetworkSend( pCtx->pvCallerContext, pucData, xDataLength );
}
/**
* @brief Network receive callback shim.
*
* @param[in] pvContext Caller context.
* @param[out] pucReceiveBuffer Byte buffer to receive into.
* @param[in] xReceiveLength Length of byte buffer for receive.
*
* @return Number of bytes received, or a negative value on error.
*/
static int prvNetworkRecv( void * pvContext,
unsigned char * pucReceiveBuffer,
size_t xReceiveLength )
{
TLSContext_t * pCtx = ( TLSContext_t * ) pvContext; /*lint !e9087 !e9079 Allow casting void* to other types. */
return ( int ) pCtx->pxNetworkRecv( pCtx->pvCallerContext, pucReceiveBuffer, xReceiveLength );
}
/**
* @brief Callback that wraps PKCS#11 for pseudo-random number generation.
*
* @param[in] pvCtx Caller context.
* @param[in] pucRandom Byte array to fill with random data.
* @param[in] xRandomLength Length of byte array.
*
* @return Zero on success.
*/
static int prvGenerateRandomBytes( void * pvCtx,
unsigned char * pucRandom,
size_t xRandomLength )
{
TLSContext_t * pCtx = ( TLSContext_t * ) pvCtx; /*lint !e9087 !e9079 Allow casting void* to other types. */
return ( int ) pCtx->pxP11FunctionList->C_GenerateRandom( pCtx->xP11Session, pucRandom, xRandomLength );
}
/**
* @brief Callback that enforces a worst-case expiration check on TLS server
* certificates.
*
* @param[in] pvCtx Caller context.
* @param[in] pxCertificate Certificate to check.
* @param[in] lPathCount Location of this certificate in the chain.
* @param[in] pulFlags Verification status flags.
*
* @return Zero on success.
*/
static int prvCheckCertificate( void * pvCtx,
mbedtls_x509_crt * pxCertificate,
int lPathCount,
uint32_t * pulFlags )
{
int lCompilationYear = 0;
#define tlsCOMPILER_DATE_STRING_MONTH_LENGTH 4
char pcCompilationMonth[ tlsCOMPILER_DATE_STRING_MONTH_LENGTH ];
int lCompilationMonth = 0;
int lCompilationDay = 0;
const char pcMonths[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
/* Unreferenced parameters. */
( void ) ( pvCtx );
( void ) ( lPathCount );
/* Parse the date string fields. */
sscanf( __DATE__,
"%3s %d %d",
pcCompilationMonth,
&lCompilationDay,
&lCompilationYear );
pcCompilationMonth[ tlsCOMPILER_DATE_STRING_MONTH_LENGTH - 1 ] = '\0';
/* Check for server expiration. First check the year. */
if( pxCertificate->valid_to.year < lCompilationYear )
{
*pulFlags |= MBEDTLS_X509_BADCERT_EXPIRED;
}
else if( pxCertificate->valid_to.year == lCompilationYear )
{
/* Convert the month. */
lCompilationMonth =
( ( strstr( pcMonths, pcCompilationMonth ) - pcMonths ) /
( tlsCOMPILER_DATE_STRING_MONTH_LENGTH - 1 ) ) + 1;
/* Check the month. */
if( pxCertificate->valid_to.mon < lCompilationMonth )
{
*pulFlags |= MBEDTLS_X509_BADCERT_EXPIRED;
}
else if( pxCertificate->valid_to.mon == lCompilationMonth )
{
/* Check the day. */
if( pxCertificate->valid_to.day < lCompilationDay )
{
*pulFlags |= MBEDTLS_X509_BADCERT_EXPIRED;
}
}
}
return 0;
}
/**
* @brief Helper for setting up potentially hardware-based cryptographic context
* for the client TLS certificate and private key.
*
* @param Caller context.
*
* @return Zero on success.
*/
static int prvInitializeClientCredential( TLSContext_t * pCtx )
{
BaseType_t xResult = 0;
CK_C_GetFunctionList pxCkGetFunctionList = NULL;
CK_SLOT_ID xSlotId = 0;
CK_ULONG ulCount = 1;
CK_ATTRIBUTE xTemplate = { 0 };
CK_OBJECT_CLASS xObjClass = 0;
CK_OBJECT_HANDLE xCertObj = 0;
CK_BYTE * pucCertificate = NULL;
/* Initialize the mbed contexts. */
mbedtls_x509_crt_init( &pCtx->mbedX509Cli );
/* Ensure that the PKCS#11 module is initialized. */
if( 0 == xResult )
{
pxCkGetFunctionList = C_GetFunctionList;
xResult = ( BaseType_t ) pxCkGetFunctionList( &pCtx->pxP11FunctionList );
}
if( 0 == xResult )
{
xResult = ( BaseType_t ) pCtx->pxP11FunctionList->C_Initialize( NULL );
}
/* Get the default private key storage ID. */
if( 0 == xResult )
{
xResult = ( BaseType_t ) pCtx->pxP11FunctionList->C_GetSlotList( CK_TRUE, &xSlotId, &ulCount );
}
/* Start a private session with the P#11 module. */
if( 0 == xResult )
{
xResult = ( BaseType_t ) pCtx->pxP11FunctionList->C_OpenSession( xSlotId,
CKF_SERIAL_SESSION,
NULL,
NULL,
&pCtx->xP11Session );
}
/* Enumerate the first private key. */
if( 0 == xResult )
{
xTemplate.type = CKA_CLASS;
xTemplate.ulValueLen = sizeof( CKA_CLASS );
xTemplate.pValue = &xObjClass;
xObjClass = CKO_PRIVATE_KEY;
xResult = ( BaseType_t ) pCtx->pxP11FunctionList->C_FindObjectsInit( pCtx->xP11Session, &xTemplate, 1 );
}
if( 0 == xResult )
{
xResult = ( BaseType_t ) pCtx->pxP11FunctionList->C_FindObjects( pCtx->xP11Session, &pCtx->xP11PrivateKey, 1, &ulCount );
}
if( 0 == xResult )
{
xResult = ( BaseType_t ) pCtx->pxP11FunctionList->C_FindObjectsFinal( pCtx->xP11Session );
}
/* Get the internal key context. */
if( 0 == xResult )
{
xTemplate.type = CKA_VENDOR_DEFINED;
xTemplate.ulValueLen = sizeof( pCtx->mbedPkCtx );
xTemplate.pValue = &pCtx->mbedPkCtx;
xResult = ( BaseType_t ) pCtx->pxP11FunctionList->C_GetAttributeValue(
pCtx->xP11Session, pCtx->xP11PrivateKey, &xTemplate, 1 );
}
/* Get the key size. */
if( 0 == xResult )
{
xTemplate.type = CKA_MODULUS_BITS;
xTemplate.ulValueLen = sizeof( pCtx->ulP11ModulusBytes );
xTemplate.pValue = &pCtx->ulP11ModulusBytes;
xResult = ( BaseType_t ) pCtx->pxP11FunctionList->C_GetAttributeValue(
pCtx->xP11Session, pCtx->xP11PrivateKey, &xTemplate, 1 );
}
if( 0 == xResult )
{
pCtx->ulP11ModulusBytes /= 8;
/* Enumerate the first client certificate. */
xTemplate.type = CKA_CLASS;
xTemplate.ulValueLen = sizeof( CKA_CLASS );
xTemplate.pValue = &xObjClass;
xObjClass = CKO_CERTIFICATE;
xResult = ( BaseType_t ) pCtx->pxP11FunctionList->C_FindObjectsInit( pCtx->xP11Session, &xTemplate, 1 );
}
if( 0 == xResult )
{
xResult = ( BaseType_t ) pCtx->pxP11FunctionList->C_FindObjects( pCtx->xP11Session, &xCertObj, 1, &ulCount );
}
if( 0 == xResult )
{
xResult = ( BaseType_t ) pCtx->pxP11FunctionList->C_FindObjectsFinal( pCtx->xP11Session );
}
if( 0 == xResult )
{
/* Query the certificate size. */
xTemplate.type = CKA_VALUE;
xTemplate.ulValueLen = 0;
xTemplate.pValue = NULL;
xResult = ( BaseType_t ) pCtx->pxP11FunctionList->C_GetAttributeValue( pCtx->xP11Session, xCertObj, &xTemplate, 1 );
}
if( 0 == xResult )
{
/* Create a buffer for the certificate. */
pucCertificate = ( CK_BYTE_PTR ) pvPortMalloc( xTemplate.ulValueLen ); /*lint !e9079 Allow casting void* to other types. */
if( NULL == pucCertificate )
{
xResult = ( BaseType_t ) CKR_HOST_MEMORY;
}
}
if( 0 == xResult )
{
/* Export the certificate. */
xTemplate.pValue = pucCertificate;
xResult = ( BaseType_t ) pCtx->pxP11FunctionList->C_GetAttributeValue(
pCtx->xP11Session, xCertObj, &xTemplate, 1 );
}
/* Decode the client certificate. */
if( 0 == xResult )
{
xResult = mbedtls_x509_crt_parse( &pCtx->mbedX509Cli,
( const unsigned char * ) pucCertificate,
xTemplate.ulValueLen );
}
/*
* Add a JITR device issuer certificate, if present.
*/
if( ( 0 == xResult ) &&
( NULL != clientcredentialJITR_DEVICE_CERTIFICATE_AUTHORITY_PEM ) )
{
/* Decode the JITR issuer. The device client certificate will get
* inserted as the first certificate in this chain below. */
xResult = mbedtls_x509_crt_parse(
&pCtx->mbedX509Cli,
( const unsigned char * ) clientcredentialJITR_DEVICE_CERTIFICATE_AUTHORITY_PEM,
1 + strlen( clientcredentialJITR_DEVICE_CERTIFICATE_AUTHORITY_PEM ) );
}
/*
* Attach the client certificate and private key to the TLS configuration.
*/
if( 0 == xResult )
{
xResult = mbedtls_ssl_conf_own_cert(
&pCtx->mbedSslConfig, &pCtx->mbedX509Cli, &pCtx->mbedPkCtx );
}
if( NULL != pucCertificate )
{
vPortFree( pucCertificate );
}
return xResult;
}
/*
* Interface routines.
*/
BaseType_t TLS_Init( void ** ppvContext,
TLSParams_t * pxParams )
{
BaseType_t xResult = 0;
TLSContext_t * pCtx = NULL;
/* Allocate an internal context. */
pCtx = ( TLSContext_t * ) pvPortMalloc( sizeof( TLSContext_t ) ); /*lint !e9087 !e9079 Allow casting void* to other types. */
if( NULL != pCtx )
{
memset( pCtx, 0, sizeof( TLSContext_t ) );
*ppvContext = pCtx;
/* Initialize the context. */
pCtx->pcDestination = pxParams->pcDestination;
pCtx->pcServerCertificate = pxParams->pcServerCertificate;
pCtx->ulServerCertificateLength = pxParams->ulServerCertificateLength;
pCtx->ppcAlpnProtocols = pxParams->ppcAlpnProtocols;
pCtx->ulAlpnProtocolsCount = pxParams->ulAlpnProtocolsCount;
pCtx->pxNetworkRecv = pxParams->pxNetworkRecv;
pCtx->pxNetworkSend = pxParams->pxNetworkSend;
pCtx->pvCallerContext = pxParams->pvCallerContext;
}
else
{
xResult = ( BaseType_t ) CKR_HOST_MEMORY;
}
return xResult;
}
/*-----------------------------------------------------------*/
#ifdef MBEDTLS_DEBUG_C
static void prvTlsDebugPrint( void * ctx,
int level,
const char * file,
int line,
const char * str )
{
/* Unused parameters. */
( void ) ctx;
( void ) file;
( void ) line;
/* Send the debug string to the portable logger. */
vLoggingPrintf( "mbedTLS: |%d| %s", level, str );
}
#endif /* ifdef MBEDTLS_DEBUG_C */
/*-----------------------------------------------------------*/
BaseType_t TLS_Connect( void * pvContext )
{
BaseType_t xResult = 0;
TLSContext_t * pCtx = ( TLSContext_t * ) pvContext; /*lint !e9087 !e9079 Allow casting void* to other types. */
/* Ensure that the FreeRTOS heap is used. */
CRYPTO_ConfigureHeap();
/* Initialize mbedTLS structures. */
mbedtls_ssl_init( &pCtx->mbedSslCtx );
mbedtls_ssl_config_init( &pCtx->mbedSslConfig );
mbedtls_x509_crt_init( &pCtx->mbedX509CA );
/* Decode the root certificate: either the default or the override. */
if( NULL != pCtx->pcServerCertificate )
{
xResult = mbedtls_x509_crt_parse( &pCtx->mbedX509CA,
( const unsigned char * ) pCtx->pcServerCertificate,
pCtx->ulServerCertificateLength );
}
else
{
xResult = mbedtls_x509_crt_parse( &pCtx->mbedX509CA,
( const unsigned char * ) tlsVERISIGN_ROOT_CERTIFICATE_PEM,
tlsVERISIGN_ROOT_CERTIFICATE_LENGTH );
if( 0 == xResult )
{
xResult = mbedtls_x509_crt_parse( &pCtx->mbedX509CA,
( const unsigned char * ) tlsATS1_ROOT_CERTIFICATE_PEM,
tlsATS1_ROOT_CERTIFICATE_LENGTH );
}
}
/* Start with protocol defaults. */
if( 0 == xResult )
{
xResult = mbedtls_ssl_config_defaults( &pCtx->mbedSslConfig,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT );
}
if( 0 == xResult )
{
/* Use a callback for additional server certificate validation. */
mbedtls_ssl_conf_verify( &pCtx->mbedSslConfig,
&prvCheckCertificate,
pCtx );
/* Server certificate validation is mandatory. */
mbedtls_ssl_conf_authmode( &pCtx->mbedSslConfig, MBEDTLS_SSL_VERIFY_REQUIRED );
/* Set the RNG callback. */
mbedtls_ssl_conf_rng( &pCtx->mbedSslConfig, &prvGenerateRandomBytes, pCtx ); /*lint !e546 Nothing wrong here. */
/* Set issuer certificate. */
mbedtls_ssl_conf_ca_chain( &pCtx->mbedSslConfig, &pCtx->mbedX509CA, NULL );
/* Setup the client credential. */
xResult = prvInitializeClientCredential( pCtx );
}
if( ( 0 == xResult ) && ( NULL != pCtx->ppcAlpnProtocols ) )
{
/* Include an application protocol list in the TLS ClientHello
* message. */
xResult = mbedtls_ssl_conf_alpn_protocols(
&pCtx->mbedSslConfig,
pCtx->ppcAlpnProtocols );
}
#ifdef MBEDTLS_DEBUG_C
/* If mbedTLS is being compiled with debug support, assume that the
* runtime configuration should use verbose output. */
mbedtls_ssl_conf_dbg( &pCtx->mbedSslConfig, prvTlsDebugPrint, NULL );
mbedtls_debug_set_threshold( tlsDEBUG_VERBOSE );
#endif
if( 0 == xResult )
{
/* Set the resulting protocol configuration. */
xResult = mbedtls_ssl_setup( &pCtx->mbedSslCtx, &pCtx->mbedSslConfig );
}
/* Set the hostname, if requested. */
if( ( 0 == xResult ) && ( NULL != pCtx->pcDestination ) )
{
xResult = mbedtls_ssl_set_hostname( &pCtx->mbedSslCtx, pCtx->pcDestination );
}
/* Set the socket callbacks. */
if( 0 == xResult )
{
mbedtls_ssl_set_bio( &pCtx->mbedSslCtx,
pCtx,
prvNetworkSend,
prvNetworkRecv,
NULL );
/* Negotiate. */
while( 0 != ( xResult = mbedtls_ssl_handshake( &pCtx->mbedSslCtx ) ) )
{
if( ( MBEDTLS_ERR_SSL_WANT_READ != xResult ) &&
( MBEDTLS_ERR_SSL_WANT_WRITE != xResult ) )
{
break;
}
}
}
/* Free up allocated memory. */
mbedtls_x509_crt_free( &pCtx->mbedX509CA );
mbedtls_x509_crt_free( &pCtx->mbedX509Cli );
return xResult;
}
/*-----------------------------------------------------------*/
BaseType_t TLS_Recv( void * pvContext,
unsigned char * pucReadBuffer,
size_t xReadLength )
{
BaseType_t xResult = 0;
TLSContext_t * pCtx = ( TLSContext_t * ) pvContext; /*lint !e9087 !e9079 Allow casting void* to other types. */
size_t xRead = 0;
if( NULL != pCtx )
{
while( xRead < xReadLength )
{
xResult = mbedtls_ssl_read( &pCtx->mbedSslCtx,
pucReadBuffer + xRead,
xReadLength - xRead );
if( 0 < xResult )
{
/* Got data, so update the tally and keep looping. */
xRead += ( size_t ) xResult;
}
else
{
if( ( 0 == xResult ) || ( MBEDTLS_ERR_SSL_WANT_READ != xResult ) )
{
/* No data and no error or call read again, if indicated, otherwise return error. */
break;
}
}
}
}
if( 0 <= xResult )
{
xResult = ( BaseType_t ) xRead;
}
return xResult;
}
/*-----------------------------------------------------------*/
BaseType_t TLS_Send( void * pvContext,
const unsigned char * pucMsg,
size_t xMsgLength )
{
BaseType_t xResult = 0;
TLSContext_t * pCtx = ( TLSContext_t * ) pvContext; /*lint !e9087 !e9079 Allow casting void* to other types. */
size_t xWritten = 0;
if( NULL != pCtx )
{
while( xWritten < xMsgLength )
{
xResult = mbedtls_ssl_write( &pCtx->mbedSslCtx,
pucMsg + xWritten,
xMsgLength - xWritten );
if( 0 < xResult )
{
/* Sent data, so update the tally and keep looping. */
xWritten += ( size_t ) xResult;
}
else
{
if( ( 0 == xResult ) || ( MBEDTLS_ERR_SSL_WANT_WRITE != xResult ) )
{
/* No data and no error or call read again, if indicated, otherwise return error. */
break;
}
}
}
}
if( 0 <= xResult )
{
xResult = ( BaseType_t ) xWritten;
}
return xResult;
}
/*-----------------------------------------------------------*/
void TLS_Cleanup( void * pvContext )
{
TLSContext_t * pCtx = ( TLSContext_t * ) pvContext; /*lint !e9087 !e9079 Allow casting void* to other types. */
if( NULL != pCtx )
{
/* Cleanup mbedTLS. */
mbedtls_ssl_close_notify( &pCtx->mbedSslCtx ); /*lint !e534 The error is already taken care of inside mbedtls_ssl_close_notify*/
mbedtls_ssl_free( &pCtx->mbedSslCtx );
mbedtls_ssl_config_free( &pCtx->mbedSslConfig );
/* Cleanup PKCS#11. */
if( ( NULL != pCtx->pxP11FunctionList ) &&
( NULL != pCtx->pxP11FunctionList->C_CloseSession ) )
{
pCtx->pxP11FunctionList->C_CloseSession( pCtx->xP11Session ); /*lint !e534 This function always return CKR_OK. */
pCtx->pxP11FunctionList->C_Finalize( NULL ); /*lint !e534 This function always return CKR_OK. */
}
/* Free memory. */
vPortFree( pCtx );
}
}