Jim Flynn
/
aws-iot-device-sdk-mbed-c
Changes to enabled on-line compiler
Diff: platform/mbedtls/network_mbedtls_wrapper.cpp
- Revision:
- 0:082731ede69f
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/mbedtls/network_mbedtls_wrapper.cpp Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,434 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "mbed.h" + +#include "easy-connect.h" + +#define MBEDTLS_FS_IO 1 + +#include <stdbool.h> +#include <string.h> +#include <timer_platform.h> +#include <network_interface.h> + +#include "mbedtls/platform.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/error.h" +#include "mbedtls/x509_crt.h" +#include "mbedtls/pk.h" + +#if DEBUG_LEVEL > 0 +#include "mbedtls/debug.h" +#endif + +#include "aws_iot_error.h" +#include "aws_iot_log.h" +#include "network_interface.h" +#include "network_platform.h" + +#include "awscerts.h" + +/* This is the value used for ssl read timeout (in msec) */ +#define IOT_SSL_READ_TIMEOUT 10000 + +/* This defines the value of the debug buffer that gets allocated. + * The value can be altered based on memory constraints + */ +#ifdef ENABLE_IOT_DEBUG +#define MBEDTLS_DEBUG_BUFFER_SIZE 2048 +#endif + + +void mbedtls_aws_init( mbedtls_net_context * ); +int mbedtls_aws_connect( mbedtls_net_context *, const char *host, uint16_t port, int proto ); +int mbedtls_aws_bind( mbedtls_net_context *, const char *bind_ip, const char *port, int proto ); +int mbedtls_aws_accept( mbedtls_net_context *bind_ctx, mbedtls_net_context *client_ctx, void *client_ip, size_t buf_size, size_t *ip_len ) ; +void mbedtls_aws_usleep( unsigned long ); +int mbedtls_aws_recv( void *, unsigned char *, size_t ); +int mbedtls_aws_recv_timeout( void *, unsigned char *, size_t , uint32_t ); +int mbedtls_aws_send( void *, const unsigned char *, size_t ); +void mbedtls_aws_free( mbedtls_net_context * ); + +/* + * This is a function to do further verification if needed on the cert received + */ + +static int _iot_tls_verify_cert(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags) +{ + char buf[1024]; + ((void) data); + FUNC_ENTRY; + + IOT_DEBUG("Verify requested for (Depth %d):", depth); + mbedtls_x509_crt_info(buf, sizeof(buf) - 1, "", crt); + IOT_DEBUG("%s", buf); + + if((*flags) == 0) { + IOT_DEBUG("This certificate has no flags"); + } else { + IOT_DEBUG(buf, sizeof(buf), " ! ", *flags); + IOT_DEBUG("%s", buf); + } + + FUNC_EXIT_RC( 0); +} + +void _iot_tls_set_connect_params(Network *pNetwork, char *pRootCALocation, char *pDeviceCertLocation, + char *pDevicePrivateKeyLocation, char *pDestinationURL, + uint16_t destinationPort, uint32_t timeout_ms, bool ServerVerificationFlag) +{ + FUNC_ENTRY; + pNetwork->tlsConnectParams.DestinationPort = destinationPort; + pNetwork->tlsConnectParams.pDestinationURL = pDestinationURL; + pNetwork->tlsConnectParams.pDeviceCertLocation = pDeviceCertLocation; + pNetwork->tlsConnectParams.pDevicePrivateKeyLocation = pDevicePrivateKeyLocation; + pNetwork->tlsConnectParams.pRootCALocation = pRootCALocation; + pNetwork->tlsConnectParams.timeout_ms = timeout_ms; + pNetwork->tlsConnectParams.ServerVerificationFlag = ServerVerificationFlag; +} + +IoT_Error_t iot_tls_init(Network *pNetwork, char *pRootCALocation, char *pDeviceCertLocation, + char *pDevicePrivateKeyLocation, char *pDestinationURL, + uint16_t destinationPort, uint32_t timeout_ms, bool ServerVerificationFlag) +{ + FUNC_ENTRY; + _iot_tls_set_connect_params(pNetwork, pRootCALocation, pDeviceCertLocation, pDevicePrivateKeyLocation, + pDestinationURL, destinationPort, timeout_ms, ServerVerificationFlag); + + pNetwork->connect = iot_tls_connect; + pNetwork->read = iot_tls_read; + pNetwork->write = iot_tls_write; + pNetwork->disconnect = iot_tls_disconnect; + pNetwork->isConnected = iot_tls_is_connected; + pNetwork->destroy = iot_tls_destroy; + + pNetwork->tlsDataParams.flags = 0; + + FUNC_EXIT_RC( AWS_SUCCESS); +} + +IoT_Error_t iot_tls_is_connected(Network *pNetwork) +{ + FUNC_ENTRY; + /* Use this to add implementation which can check for physical layer disconnect */ + FUNC_EXIT_RC( NETWORK_PHYSICAL_LAYER_CONNECTED); +} + +IoT_Error_t iot_tls_connect(Network *pNetwork, TLSConnectParams *params) +{ + int ret = 0; + const char *pers = "aws_iot_tls_wrapper"; + TLSDataParams *tlsDataParams = NULL; + char vrfy_buf[512]; + const char *alpnProtocols[] = { "x-amzn-mqtt-ca", NULL }; + + FUNC_ENTRY; + + if(pNetwork == NULL) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if( params != NULL) { + _iot_tls_set_connect_params(pNetwork, params->pRootCALocation, params->pDeviceCertLocation, + params->pDevicePrivateKeyLocation, params->pDestinationURL, + params->DestinationPort, params->timeout_ms, params->ServerVerificationFlag); + } + + tlsDataParams = &(pNetwork->tlsDataParams); + + + mbedtls_entropy_init(&(tlsDataParams->entropy)); + mbedtls_ctr_drbg_init(&(tlsDataParams->ctr_drbg)); + mbedtls_x509_crt_init(&(tlsDataParams->clicert)); + mbedtls_x509_crt_init(&(tlsDataParams->cacert)); + mbedtls_ssl_init(&(tlsDataParams->ssl)); + mbedtls_ssl_config_init(&(tlsDataParams->conf)); + mbedtls_pk_init(&(tlsDataParams->pkey)); + + IOT_DEBUG("\nSeed the random number generator..."); + if((ret = mbedtls_ctr_drbg_seed(&(tlsDataParams->ctr_drbg), mbedtls_entropy_func, &(tlsDataParams->entropy), + (const unsigned char *) pers, + strlen(pers))) != 0) { + IOT_ERROR(" failed\n ! mbedtls_ctr_drbg_seed returned -0x%x\n", -ret); + FUNC_EXIT_RC(NETWORK_MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED); + } + + IOT_DEBUG("Load the IoT certificate ..."); + #ifdef USING_SD_CARD + ret = mbedtls_x509_crt_parse_file(&(tlsDataParams->clicert), pNetwork->tlsConnectParams.pDeviceCertLocation); + #else + ret = mbedtls_x509_crt_parse(&(tlsDataParams->clicert), (const unsigned char*)aws_iot_certificate, + strlen(aws_iot_certificate)+1); + #endif + if(ret != 0) { + IOT_ERROR(" failed\n ! mbedtls_x509_crt_parse returned -0x%x while parsing device cert\n\n", -ret); + FUNC_EXIT_RC(NETWORK_X509_DEVICE_CRT_PARSE_ERROR); + } + + IOT_DEBUG("Load the private key ..."); + #ifdef USING_SD_CARD + ret = mbedtls_pk_parse_keyfile(&(tlsDataParams->pkey), pNetwork->tlsConnectParams.pDevicePrivateKeyLocation, ""); + #else + ret = mbedtls_pk_parse_key(&(tlsDataParams->pkey), (const unsigned char*)aws_iot_private_key, + strlen(aws_iot_private_key)+1, NULL, 0 ); + #endif + if(ret != 0) { + IOT_ERROR(" failed\n ! mbedtls_pk_parse_key returned -0x%x while parsing private key\n\n", -ret); + IOT_DEBUG(" path : %s ", pNetwork->tlsConnectParams.pDevicePrivateKeyLocation); + FUNC_EXIT_RC(NETWORK_PK_PRIVATE_KEY_PARSE_ERROR); + } + + IOT_DEBUG("Load the CA root certificate ..."); + #ifdef USING_SD_CARD + ret = mbedtls_x509_crt_parse_file(&(tlsDataParams->cacert), pNetwork->tlsConnectParams.pRootCALocation); + #else + ret = mbedtls_x509_crt_parse(&(tlsDataParams->cacert), (const unsigned char*)aws_iot_rootCA, + sizeof(aws_iot_rootCA)); + #endif + if(ret < 0) { + IOT_ERROR(" failed\n ! mbedtls_x509_crt_parse returned -0x%x while parsing root cert\n\n", -ret); + FUNC_EXIT_RC(NETWORK_X509_ROOT_CRT_PARSE_ERROR); + } + + IOT_DEBUG("done.\nSetting up the SSL/TLS structure..."); + if((ret = mbedtls_ssl_config_defaults(&(tlsDataParams->conf), + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + IOT_ERROR(" failed\n ! mbedtls_ssl_config_defaults returned -0x%x\n\n", -ret); + FUNC_EXIT_RC(SSL_CONNECTION_ERROR); + } + + mbedtls_ssl_conf_ca_chain(&(tlsDataParams->conf), &(tlsDataParams->cacert), NULL); + mbedtls_ssl_conf_rng(&(tlsDataParams->conf), mbedtls_ctr_drbg_random, &(tlsDataParams->ctr_drbg)); + + if((ret = mbedtls_ssl_setup(&(tlsDataParams->ssl), &(tlsDataParams->conf))) != 0) { + IOT_ERROR(" failed\n ! mbedtls_ssl_setup returned -0x%x\n\n", -ret); + FUNC_EXIT_RC(SSL_CONNECTION_ERROR); + } + + if((ret = mbedtls_ssl_set_hostname(&(tlsDataParams->ssl), pNetwork->tlsConnectParams.pDestinationURL)) != 0) { + IOT_ERROR(" failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret); + FUNC_EXIT_RC(SSL_CONNECTION_ERROR); + } + IOT_DEBUG("Set the SSL BIO ..."); + mbedtls_ssl_set_bio(&(tlsDataParams->ssl), &(tlsDataParams->server_fd), mbedtls_aws_send, NULL, + mbedtls_aws_recv_timeout); + mbedtls_ssl_conf_verify(&(tlsDataParams->conf), _iot_tls_verify_cert, NULL); + if(pNetwork->tlsConnectParams.ServerVerificationFlag == true) + mbedtls_ssl_conf_authmode(&(tlsDataParams->conf), MBEDTLS_SSL_VERIFY_REQUIRED); + else + mbedtls_ssl_conf_authmode(&(tlsDataParams->conf), MBEDTLS_SSL_VERIFY_OPTIONAL); + + if((ret = mbedtls_ssl_conf_own_cert(&(tlsDataParams->conf), &(tlsDataParams->clicert), + &(tlsDataParams->pkey))) != 0) { + IOT_ERROR(" failed\n!!! mbedtls_ssl_conf_own_cert returned %d\n\n", ret); + FUNC_EXIT_RC(SSL_CONNECTION_ERROR); + } + + mbedtls_ssl_conf_read_timeout(&(tlsDataParams->conf), pNetwork->tlsConnectParams.timeout_ms); + + /* Use the AWS IoT ALPN extension for MQTT if port 443 is requested. */ + if(443 == pNetwork->tlsConnectParams.DestinationPort) { + if((ret = mbedtls_ssl_conf_alpn_protocols(&(tlsDataParams->conf), alpnProtocols)) != 0) { + IOT_ERROR(" failed\n ! mbedtls_ssl_conf_alpn_protocols returned -0x%x\n\n", -ret); + FUNC_EXIT_RC(SSL_CONNECTION_ERROR); + } + } + + mbedtls_aws_init(&(tlsDataParams->server_fd)); + if( (tlsDataParams->server_fd).fd == -1 ) { + IOT_ERROR(" Network connected failed!\n"); + FUNC_EXIT_RC(NETWORK_ERR_NET_CONNECT_FAILED); + } + + IOT_DEBUG("Connecting to %s/%d...", pNetwork->tlsConnectParams.pDestinationURL, pNetwork->tlsConnectParams.DestinationPort); + if((ret = mbedtls_aws_connect(&(tlsDataParams->server_fd), pNetwork->tlsConnectParams.pDestinationURL, + pNetwork->tlsConnectParams.DestinationPort, + MBEDTLS_NET_PROTO_TCP)) != 0) { + IOT_ERROR(" failed\n ! mbedtls_aws_connect returned -0x%x\n\n", -ret); + switch(ret) { + case MBEDTLS_ERR_NET_SOCKET_FAILED: + FUNC_EXIT_RC(NETWORK_ERR_NET_SOCKET_FAILED); + case MBEDTLS_ERR_NET_UNKNOWN_HOST: + FUNC_EXIT_RC(NETWORK_ERR_NET_UNKNOWN_HOST); + case MBEDTLS_ERR_NET_CONNECT_FAILED: + default: + FUNC_EXIT_RC(NETWORK_ERR_NET_CONNECT_FAILED); + }; + } + + IOT_DEBUG("\n\nPerform the SSL/TLS handshake...\n\n\n\n"); + + while((ret = mbedtls_ssl_handshake(&(tlsDataParams->ssl))) != 0) { + if(ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + IOT_ERROR(" failed\n ! mbedtls_ssl_handshake returned -0x%x (%d)\n", -ret, ret); + if(ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) { + IOT_ERROR( "Unable to verify the server's certificate. Either it is invalid, or you\n" + "didn't set ca_file or ca_path to an appropriate value. Alternatively,\n" + "you may want to use auth_mode=optional for testing purposes.\n" ); + } + FUNC_EXIT_RC(SSL_CONNECTION_ERROR); + } + } + + IOT_DEBUG("[ Protocol is %s ]\n[ Ciphersuite is %s ]\n", mbedtls_ssl_get_version(&(tlsDataParams->ssl)), + mbedtls_ssl_get_ciphersuite(&(tlsDataParams->ssl))); + if((ret = mbedtls_ssl_get_record_expansion(&(tlsDataParams->ssl))) >= 0) { + IOT_DEBUG("[Record expansion is %d]", ret); + } + else{ + IOT_DEBUG("[Record expansion is unknown (compression)]"); + } + IOT_DEBUG("Verifying peer X.509 certificate..."); + + if(pNetwork->tlsConnectParams.ServerVerificationFlag == true) { + if((tlsDataParams->flags = mbedtls_ssl_get_verify_result(&(tlsDataParams->ssl))) != 0) { + IOT_ERROR(" FAIL\n"); + mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), " ! ", tlsDataParams->flags); + IOT_ERROR("%s\n", vrfy_buf); + ret = SSL_CONNECTION_ERROR; + } else { + IOT_DEBUG("Verified OK\n"); + ret = AWS_SUCCESS; + } + } else { + IOT_DEBUG("Server Verification skipped\n"); + ret = AWS_SUCCESS; + } + +#ifdef ENABLE_IOT_DEBUG + if (mbedtls_ssl_get_peer_cert(&(tlsDataParams->ssl)) != NULL) { + unsigned char buf[MBEDTLS_DEBUG_BUFFER_SIZE]; + IOT_DEBUG("Peer certificate information ...\n"); + mbedtls_x509_crt_info((char *) buf, sizeof(buf) - 1, " ", mbedtls_ssl_get_peer_cert(&(tlsDataParams->ssl))); + IOT_DEBUG("%s", buf); + } +#endif + + mbedtls_ssl_conf_read_timeout(&(tlsDataParams->conf), IOT_SSL_READ_TIMEOUT); + + FUNC_EXIT_RC((IoT_Error_t) ret); +} + +IoT_Error_t iot_tls_write(Network *pNetwork, unsigned char *pMsg, size_t len, awsTimer *timer, size_t *written_len) +{ + size_t written_so_far; + bool isErrorFlag = false; + int frags; + int ret = 0; + TLSDataParams *tlsDataParams = &(pNetwork->tlsDataParams); + + FUNC_ENTRY; + for(written_so_far = 0, frags = 0; + written_so_far < len && !has_timer_expired(timer); written_so_far += ret, frags++) { + while(!has_timer_expired(timer) && + (ret = mbedtls_ssl_write(&(tlsDataParams->ssl), pMsg + written_so_far, len - written_so_far)) <= 0) { + if(ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + IOT_ERROR("FAILED!\n ! mbedtls_ssl_write returned -0x%x\n\n", -ret); + /* All other negative return values indicate connection needs to be reset. + * Will be caught in ping request so ignored here */ + isErrorFlag = true; + break; + } + } + if(isErrorFlag) { + break; + } + } + + *written_len = written_so_far; + + if(isErrorFlag) { + FUNC_EXIT_RC(NETWORK_SSL_WRITE_ERROR); + } else if(has_timer_expired(timer) && written_so_far != len) { + FUNC_EXIT_RC(NETWORK_SSL_WRITE_TIMEOUT_ERROR); + } + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +IoT_Error_t iot_tls_read(Network *pNetwork, unsigned char *pMsg, size_t len, awsTimer *timer, size_t *read_len) +{ + mbedtls_ssl_context *ssl = &(pNetwork->tlsDataParams.ssl); + size_t rxLen = 0; + int ret=0; + FUNC_ENTRY; + + while (len > 0) { + // This read will timeout after IOT_SSL_READ_TIMEOUT if there's no data to be read + + while( ret == 0 && !has_timer_expired(timer) ) + ret = mbedtls_ssl_read(ssl, pMsg, len); + + if (ret > 0) { + rxLen += ret; + pMsg += ret; + len -= ret; + } + else if (ret == 0 || (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret != MBEDTLS_ERR_SSL_TIMEOUT)) { + FUNC_EXIT_RC(NETWORK_SSL_READ_ERROR); + } + } + + if (len == 0) { + *read_len = rxLen; + FUNC_EXIT_RC(AWS_SUCCESS); + } + + if (rxLen == 0) { + FUNC_EXIT_RC(NETWORK_SSL_NOTHING_TO_READ); + } else { + FUNC_EXIT_RC(NETWORK_SSL_READ_TIMEOUT_ERROR); + } +} + +IoT_Error_t iot_tls_disconnect(Network *pNetwork) +{ + mbedtls_ssl_context *ssl = &(pNetwork->tlsDataParams.ssl); + int ret = 0; + FUNC_ENTRY; + do { + ret = mbedtls_ssl_close_notify(ssl); + } while(ret == MBEDTLS_ERR_SSL_WANT_WRITE); + + /* All other negative return values indicate connection needs to be reset. + * No further action required since this is disconnect call */ + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +IoT_Error_t iot_tls_destroy(Network *pNetwork) +{ + TLSDataParams *tlsDataParams = &(pNetwork->tlsDataParams); + FUNC_ENTRY; + + mbedtls_aws_free(&(tlsDataParams->server_fd)); + + mbedtls_x509_crt_free(&(tlsDataParams->clicert)); + mbedtls_x509_crt_free(&(tlsDataParams->cacert)); + mbedtls_pk_free(&(tlsDataParams->pkey)); + mbedtls_ssl_free(&(tlsDataParams->ssl)); + mbedtls_ssl_config_free(&(tlsDataParams->conf)); + mbedtls_ctr_drbg_free(&(tlsDataParams->ctr_drbg)); + mbedtls_entropy_free(&(tlsDataParams->entropy)); + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +