/* * 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 #include #include /** * @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 ); } }