Webserver+3d print
cyclone_ssl/tls_common.c
- Committer:
- Sergunb
- Date:
- 2017-02-04
- Revision:
- 0:8918a71cdbe9
File content as of revision 0:8918a71cdbe9:
/** * @file tls_common.c * @brief Handshake message processing (TLS client and server) * * @section License * * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. * * This file is part of CycloneSSL Open. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * @author Oryx Embedded SARL (www.oryx-embedded.com) * @version 1.7.6 **/ //Switch to the appropriate trace level #define TRACE_LEVEL TLS_TRACE_LEVEL //Dependencies #include <string.h> #include <ctype.h> #include "tls.h" #include "tls_cipher_suites.h" #include "tls_client.h" #include "tls_server.h" #include "tls_common.h" #include "tls_record.h" #include "tls_cache.h" #include "tls_misc.h" #include "asn1.h" #include "oid.h" #include "x509.h" #include "pem.h" #include "debug.h" //Check SSL library configuration #if (TLS_SUPPORT == ENABLED) /** * @brief Perform TLS handshake * @param[in] context Pointer to the TLS context * @return Error code **/ error_t tlsHandshake(TlsContext *context) { error_t error; #if (TLS_CLIENT_SUPPORT == ENABLED) //TLS operates as a client? if(context->entity == TLS_CONNECTION_END_CLIENT) { //Initiate TLS handshake with the remote server error = tlsClientHandshake(context); } else #endif #if (TLS_SERVER_SUPPORT == ENABLED) //TLS operates as a server? if(context->entity == TLS_CONNECTION_END_SERVER) { //Initiate TLS handshake with the remote client error = tlsServerHandshake(context); } else #endif //Unsupported mode of operation? { //Cannot establish a secure session between the server and the client error = ERROR_INVALID_PARAMETER; } //Return status code return error; } /** * @brief Send Certificate message * @param[in] context Pointer to the TLS context * @return Error code **/ error_t tlsSendCertificate(TlsContext *context) { error_t error; size_t length; void *message; //Initialize status code error = NO_ERROR; //Point to the buffer where to format the message message = (void *) context->txBuffer; #if (TLS_CLIENT_SUPPORT == ENABLED) //TLS operates as a client? if(context->entity == TLS_CONNECTION_END_CLIENT) { //The client must send a Certificate message if the server requests it if(context->clientCertRequested) { #if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_MIN_VERSION <= SSL_VERSION_3_0) //No suitable certificate available? if(context->cert == NULL && context->version == SSL_VERSION_3_0) { //The client should send a no_certificate alert instead error = tlsFormatAlert(context, TLS_ALERT_LEVEL_WARNING, TLS_ALERT_NO_CERTIFICATE, message, &length); //Check status code if(!error) { //Debug message TRACE_INFO("Sending Alert message (%" PRIuSIZE " bytes)...\r\n", length); TRACE_INFO_ARRAY(" ", message, length); //Send Alert message error = tlsWriteProtocolData(context, message, length, TLS_TYPE_ALERT); } } else #endif { //Format Certificate message error = tlsFormatCertificate(context, message, &length); //Check status code if(!error) { //Debug message TRACE_INFO("Sending Certificate message (%" PRIuSIZE " bytes)...\r\n", length); TRACE_DEBUG_ARRAY(" ", message, length); //Send handshake message error = tlsWriteProtocolData(context, message, length, TLS_TYPE_HANDSHAKE); } } } } else #endif #if (TLS_SERVER_SUPPORT == ENABLED) //TLS operates as a server? if(context->entity == TLS_CONNECTION_END_SERVER) { //The server must send a Certificate message whenever the agreed-upon //key exchange method uses certificates for authentication if(context->cert != NULL) { //Format Certificate message error = tlsFormatCertificate(context, message, &length); //Check status code if(!error) { //Debug message TRACE_INFO("Sending Certificate message (%" PRIuSIZE " bytes)...\r\n", length); TRACE_DEBUG_ARRAY(" ", message, length); //Send handshake message error = tlsWriteProtocolData(context, message, length, TLS_TYPE_HANDSHAKE); } } } else #endif //Unsupported mode of operation? { //Report an error error = ERROR_FAILURE; } //Check status code if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT) { //Update FSM state if(context->entity == TLS_CONNECTION_END_CLIENT) context->state = TLS_STATE_CLIENT_KEY_EXCHANGE; else context->state = TLS_STATE_SERVER_KEY_EXCHANGE; } //Return status code return error; } /** * @brief Send ChangeCipherSpec message * * The change cipher spec message is sent by both the client and the * server to notify the receiving party that subsequent records will be * protected under the newly negotiated CipherSpec and keys * * @param[in] context Pointer to the TLS context * @return Error code **/ error_t tlsSendChangeCipherSpec(TlsContext *context) { error_t error; size_t length; TlsChangeCipherSpec *message; //Point to the buffer where to format the message message = (TlsChangeCipherSpec *) context->txBuffer; //Format ChangeCipherSpec message error = tlsFormatChangeCipherSpec(context, message, &length); //Check status code if(!error) { //Debug message TRACE_INFO("Sending ChangeCipherSpec message (%" PRIuSIZE " bytes)...\r\n", length); TRACE_DEBUG_ARRAY(" ", message, length); //Send ChangeCipherSpec message error = tlsWriteProtocolData(context, message, length, TLS_TYPE_CHANGE_CIPHER_SPEC); } //Check status code if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT) { //Initialize encryption engine error = tlsInitEncryptionEngine(context); } //Check status code if(!error) { //Inform the record layer that subsequent records will be protected //under the newly negotiated encryption algorithm context->changeCipherSpecSent = TRUE; //Prepare to send a Finished message to the peer... if(context->entity == TLS_CONNECTION_END_CLIENT) context->state = TLS_STATE_CLIENT_FINISHED; else context->state = TLS_STATE_SERVER_FINISHED; } //Return status code return error; } /** * @brief Send Finished message * * A Finished message is always sent immediately after a change * cipher spec message to verify that the key exchange and * authentication processes were successful. It is essential that a * change cipher spec message be received between the other handshake * messages and the Finished message * * @param[in] context Pointer to the TLS context * @return Error code **/ error_t tlsSendFinished(TlsContext *context) { error_t error; size_t length; TlsFinished *message; //Point to the buffer where to format the message message = (TlsFinished *) context->txBuffer; //The verify data is generated from all messages in this handshake //up to but not including the Finished message error = tlsComputeVerifyData(context, context->entity); //Check status code if(!error) { //Format Finished message error = tlsFormatFinished(context, message, &length); } //Check status code if(!error) { //Debug message TRACE_INFO("Sending Finished message (%" PRIuSIZE " bytes)...\r\n", length); TRACE_DEBUG_ARRAY(" ", message, length); //Send handshake message error = tlsWriteProtocolData(context, message, length, TLS_TYPE_HANDSHAKE); } //Check status code if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT) { //TLS operates as a client or a server? if(context->entity == TLS_CONNECTION_END_CLIENT) { //Use abbreviated or full handshake? if(context->resume) context->state = TLS_STATE_APPLICATION_DATA; else context->state = TLS_STATE_SERVER_CHANGE_CIPHER_SPEC; } else { //Use abbreviated or full handshake? if(context->resume) context->state = TLS_STATE_CLIENT_CHANGE_CIPHER_SPEC; else context->state = TLS_STATE_APPLICATION_DATA; } } //Return status code return error; } /** * @brief Send Alert message * @param[in] context Pointer to the TLS context * @param[in] level Severity of the message (warning or fatal) * @param[in] description Description of the alert * @return Error code **/ error_t tlsSendAlert(TlsContext *context, uint8_t level, uint8_t description) { error_t error; size_t length; TlsAlert *message; //Point to the buffer where to format the message message = (TlsAlert *) context->txBuffer; //Format Alert message error = tlsFormatAlert(context, level, description, message, &length); //Check status code if(!error) { //Debug message TRACE_INFO("Sending Alert message (%" PRIuSIZE " bytes)...\r\n", length); TRACE_INFO_ARRAY(" ", message, length); //Send Alert message error = tlsWriteProtocolData(context, message, length, TLS_TYPE_ALERT); } //Alert messages convey the severity of the message if(level == TLS_ALERT_LEVEL_WARNING) { //If an alert with a level of warning is sent, generally the //connection can continue normally if(description == TLS_ALERT_CLOSE_NOTIFY) { //Either party may initiate a close by sending a close_notify alert context->closeNotifySent = TRUE; //Update FSM state context->state = TLS_STATE_CLOSING; } } else if(level == TLS_ALERT_LEVEL_FATAL) { //Alert messages with a level of fatal result in the immediate //termination of the connection context->fatalAlertSent = TRUE; //Any connection terminated with a fatal alert must not be resumed if(context->entity == TLS_CONNECTION_END_SERVER) tlsRemoveFromCache(context); //Servers and clients must forget any session identifiers memset(context->sessionId, 0, 32); context->sessionIdLen = 0; //Update FSM state context->state = TLS_STATE_CLOSING; } //Return status code return error; } /** * @brief Format Certificate message * @param[in] context Pointer to the TLS context * @param[out] message Buffer where to format the Certificate message * @param[out] length Length of the resulting Certificate message * @return Error code **/ error_t tlsFormatCertificate(TlsContext *context, TlsCertificate *message, size_t *length) { error_t error; uint8_t *p; const char_t *pemCert; size_t pemCertLength; uint8_t *derCert; size_t derCertSize; size_t derCertLength; //Initialize status code error = NO_ERROR; //Handshake message type message->msgType = TLS_TYPE_CERTIFICATE; //Point to the first certificate of the list p = message->certificateList; //Length of the certificate list in bytes *length = 0; //Check whether a certificate is available if(context->cert != NULL) { //Point to the certificate chain pemCert = context->cert->certChain; //Get the total length, in bytes, of the certificate chain pemCertLength = context->cert->certChainLength; } else { //If no suitable certificate is available, the message //contains an empty certificate list pemCert = NULL; pemCertLength = 0; } //DER encoded certificate derCert = NULL; derCertSize = 0; derCertLength = 0; //Parse the certificate chain while(pemCertLength > 0) { //Decode PEM certificate error = pemReadCertificate(&pemCert, &pemCertLength, &derCert, &derCertSize, &derCertLength); //Any error to report? if(error) { //End of file detected error = NO_ERROR; break; } //Total length of the certificate list *length += derCertLength + 3; //Prevent the buffer from overflowing if((*length + sizeof(TlsCertificate)) > context->txRecordMaxLen) { //Report an error error = ERROR_MESSAGE_TOO_LONG; break; } //Each certificate is preceded by a 3-byte length field STORE24BE(derCertLength, p); //Copy the current certificate memcpy(p + 3, derCert, derCertLength); //Advance data pointer p += derCertLength + 3; } //Free previously allocated memory tlsFreeMem(derCert); //A 3-byte length field shall precede the certificate list STORE24BE(*length, message->certificateListLength); //Consider the 3-byte length field *length += 3; //Fix the length field STORE24BE(*length, message->length); //Length of the complete handshake message *length += sizeof(TlsHandshake); //Return status code return error; } /** * @brief Format ChangeCipherSpec message * @param[in] context Pointer to the TLS context * @param[out] message Buffer where to format the ChangeCipherSpec message * @param[out] length Length of the resulting ChangeCipherSpec message * @return Error code **/ error_t tlsFormatChangeCipherSpec(TlsContext *context, TlsChangeCipherSpec *message, size_t *length) { //The ChangeCipherSpec message consists of a single byte of value 1 message->type = 1; //Length of the ChangeCipherSpec message *length = sizeof(TlsChangeCipherSpec); //Successful processing return NO_ERROR; } /** * @brief Format Finished message * @param[in] context Pointer to the TLS context * @param[out] message Buffer where to format the Finished message * @param[out] length Length of the resulting Finished message * @return Error code **/ error_t tlsFormatFinished(TlsContext *context, TlsFinished *message, size_t *length) { //Handshake message type message->msgType = TLS_TYPE_FINISHED; //The length of the verify data depends on the cipher suite STORE24BE(context->verifyDataLen, message->length); //Copy the verify data memcpy(message->verifyData, context->verifyData, context->verifyDataLen); //Length of the complete handshake message *length = context->verifyDataLen + sizeof(TlsHandshake); //Successful processing return NO_ERROR; } /** * @brief Format Alert message * @param[in] context Pointer to the TLS context * @param[in] level Severity of the message (warning or fatal) * @param[in] description Description of the alert * @param[out] message Buffer where to format the Alert message * @param[out] length Length of the resulting Alert message * @return Error code **/ error_t tlsFormatAlert(TlsContext *context, uint8_t level, uint8_t description, TlsAlert *message, size_t *length) { //Severity of the message message->level = level; //Description of the alert message->description = description; //Length of the Alert message *length = sizeof(TlsAlert); //Successful processing return NO_ERROR; } /** * @brief Parse Certificate message * @param[in] context Pointer to the TLS context * @param[in] message Incoming Certificate message to parse * @param[in] length Message length * @return Error code **/ error_t tlsParseCertificate(TlsContext *context, const TlsCertificate *message, size_t length) { error_t error; const uint8_t *p; size_t n; const char_t *pemCert; size_t pemCertLength; uint8_t *derCert; size_t derCertSize; size_t derCertLength; //X.509 certificates X509CertificateInfo *certInfo = NULL; X509CertificateInfo *issuerCertInfo = NULL; //Debug message TRACE_INFO("Certificate message received (%" PRIuSIZE " bytes)...\r\n", length); TRACE_DEBUG_ARRAY(" ", message, length); //Check the length of the Certificate message if(length < sizeof(TlsCertificate)) return ERROR_DECODING_FAILED; //TLS operates as a client or a server? if(context->entity == TLS_CONNECTION_END_CLIENT) { //Check current state if(context->state != TLS_STATE_SERVER_CERTIFICATE) return ERROR_UNEXPECTED_MESSAGE; } else { //Check current state if(context->state != TLS_STATE_CLIENT_CERTIFICATE) return ERROR_UNEXPECTED_MESSAGE; } //Update the hash value with the incoming handshake message tlsUpdateHandshakeHash(context, message, length); //Get the size occupied by the certificate list n = LOAD24BE(message->certificateListLength); //Remaining bytes to process length -= sizeof(TlsCertificate); //Ensure that the chain of certificates is valid if(n > length) return ERROR_DECODING_FAILED; //Compute the length of the certificate list length = n; //The sender's certificate must come first in the list p = message->certificateList; //Start of exception handling block do { //Assume an error... error = ERROR_OUT_OF_MEMORY; //Allocate a memory buffer to store X.509 certificate info certInfo = tlsAllocMem(sizeof(X509CertificateInfo)); //Failed to allocate memory? if(certInfo == NULL) break; //Allocate a memory buffer to store the parent certificate issuerCertInfo = tlsAllocMem(sizeof(X509CertificateInfo)); //Failed to allocate memory? if(issuerCertInfo == NULL) break; //TLS operates as a server? if(context->entity == TLS_CONNECTION_END_SERVER) { //Empty certificate list? if(!length) { //Check whether mutual authentication is required if(context->clientAuthMode == TLS_CLIENT_AUTH_REQUIRED) { //If client authentication is required by the server for the handshake //to continue, it may respond with a fatal handshake failure alert error = ERROR_HANDSHAKE_FAILED; break; } else { //Client authentication is optional context->peerCertType = TLS_CERT_NONE; //Exit immediately error = NO_ERROR; break; } } } //Each certificate is preceded by a 3-byte length field if(length < 3) { //Report an error error = ERROR_DECODING_FAILED; break; } //Get the size occupied by the certificate n = LOAD24BE(p); //Jump to the beginning of the DER encoded certificate p += 3; length -= 3; //Make sure that the certificate is valid if(n > length) { //Report an error error = ERROR_DECODING_FAILED; break; } //Display ASN.1 structure error = asn1DumpObject(p, n, 0); //Any error to report? if(error) break; //Parse X.509 certificate error = x509ParseCertificate(p, n, certInfo); //Failed to parse the X.509 certificate? if(error) break; #if (TLS_CLIENT_SUPPORT == ENABLED) //TLS operates as a client? if(context->entity == TLS_CONNECTION_END_CLIENT) { //Check if the hostname must be verified if(context->serverName != NULL) { int_t i; int_t j; //Point to the last character of the common name i = certInfo->subject.commonNameLen - 1; //Point to the last character of the hostname j = strlen(context->serverName) - 1; //Check the common name in the server certificate against //the actual hostname that is being requested while(i >= 0 && j >= 0) { //Wildcard certificate found? if(certInfo->subject.commonName[i] == '*' && i == 0) { //The CN is acceptable j = 0; } //Perform case insensitive character comparison else if(tolower((uint8_t) certInfo->subject.commonName[i]) != context->serverName[j]) { break; } //Compare previous characters i--; j--; } //If the host names do not match, reject the certificate if(i >= 0 || j >= 0) { //Debug message TRACE_WARNING("Server name mismatch!\r\n"); //Report an error error = ERROR_BAD_CERTIFICATE; break; } } } #endif #if (TLS_RSA_SIGN_SUPPORT == ENABLED || TLS_RSA_SUPPORT == ENABLED || \ TLS_DHE_RSA_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED) //The certificate contains a valid RSA public key? if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen, RSA_ENCRYPTION_OID, sizeof(RSA_ENCRYPTION_OID))) { uint_t k; //Retrieve the RSA public key error = x509ReadRsaPublicKey(certInfo, &context->peerRsaPublicKey); //Any error to report if(error) break; //Get the length of the modulus, in bits k = mpiGetBitLength(&context->peerRsaPublicKey.n); //Make sure the modulus is acceptable if(k < TLS_MIN_RSA_MODULUS_SIZE || k > TLS_MAX_RSA_MODULUS_SIZE) { //Report an error error = ERROR_BAD_CERTIFICATE; break; } //Save the certificate type context->peerCertType = TLS_CERT_RSA_SIGN; } else #endif #if (TLS_DSA_SIGN_SUPPORT == ENABLED || TLS_DHE_DSS_SUPPORT == ENABLED) //The certificate contains a valid DSA public key? if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen, DSA_OID, sizeof(DSA_OID))) { uint_t k; //Retrieve the DSA public key error = x509ReadDsaPublicKey(certInfo, &context->peerDsaPublicKey); //Any error to report if(error) break; //Get the length of the prime modulus, in bits k = mpiGetBitLength(&context->peerDsaPublicKey.p); //Make sure the prime modulus is acceptable if(k < TLS_MIN_DSA_MODULUS_SIZE || k > TLS_MAX_DSA_MODULUS_SIZE) { //Report an error error = ERROR_BAD_CERTIFICATE; break; } //Save the certificate type context->peerCertType = TLS_CERT_DSS_SIGN; } else #endif #if (TLS_ECDSA_SIGN_SUPPORT == ENABLED || TLS_ECDHE_ECDSA_SUPPORT == ENABLED) //The certificate contains a valid EC public key? if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen, EC_PUBLIC_KEY_OID, sizeof(EC_PUBLIC_KEY_OID))) { const EcCurveInfo *curveInfo; //Retrieve EC domain parameters curveInfo = ecGetCurveInfo(certInfo->subjectPublicKeyInfo.ecParams.namedCurve, certInfo->subjectPublicKeyInfo.ecParams.namedCurveLen); //Make sure the specified elliptic curve is supported if(curveInfo == NULL) { //Report an error error = ERROR_BAD_CERTIFICATE; //Exit immediately break; } //Load EC domain parameters error = ecLoadDomainParameters(&context->peerEcParams, curveInfo); //Any error to report? if(error) break; //Retrieve the EC public key error = ecImport(&context->peerEcParams, &context->peerEcPublicKey, certInfo->subjectPublicKeyInfo.ecPublicKey.q, certInfo->subjectPublicKeyInfo.ecPublicKey.qLen); //Any error to report if(error) break; //Save the certificate type context->peerCertType = TLS_CERT_ECDSA_SIGN; } else #endif //The certificate does not contain any valid public key? { //Report an error error = ERROR_BAD_CERTIFICATE; break; } //Next certificate p += n; length -= n; //PKIX path validation while(length > 0) { //Each certificate is preceded by a 3-byte length field if(length < 3) { //Report an error error = ERROR_DECODING_FAILED; break; } //Get the size occupied by the certificate n = LOAD24BE(p); //Jump to the beginning of the DER encoded certificate p += 3; length -= 3; //Ensure that the certificate is valid if(n > length) { //Report an error error = ERROR_DECODING_FAILED; break; } //Display ASN.1 structure error = asn1DumpObject(p, n, 0); //Any error to report? if(error) break; //Parse X.509 certificate error = x509ParseCertificate(p, n, issuerCertInfo); //Failed to parse the X.509 certificate? if(error) break; //Valid trusted CA list? if(context->trustedCaListLen > 0) { //Validate current certificate error = x509ValidateCertificate(certInfo, issuerCertInfo); //Certificate validation failed? if(error) break; } //Keep track of the issuer certificate memcpy(certInfo, issuerCertInfo, sizeof(X509CertificateInfo)); //Next certificate p += n; length -= n; } //Propagate exception if necessary... if(error) break; //Point to the first trusted CA certificate pemCert = context->trustedCaList; //Get the total length, in bytes, of the trusted CA list pemCertLength = context->trustedCaListLen; //DER encoded certificate derCert = NULL; derCertSize = 0; derCertLength = 0; //Loop through the list while(pemCertLength > 0) { //Decode PEM certificate error = pemReadCertificate(&pemCert, &pemCertLength, &derCert, &derCertSize, &derCertLength); //Any error to report? if(error) break; //Parse X.509 certificate error = x509ParseCertificate(derCert, derCertLength, issuerCertInfo); //Failed to parse the X.509 certificate? if(error) break; //Validate the certificate with the current trusted CA error = x509ValidateCertificate(certInfo, issuerCertInfo); //Certificate validation succeeded? if(!error) break; } //The certificate could not be matched with a known, trusted CA? if(error == ERROR_END_OF_FILE) error = ERROR_UNKNOWN_CA; //Free previously allocated memory tlsFreeMem(derCert); //End of exception handling block } while(0); //Free previously allocated memory tlsFreeMem(certInfo); tlsFreeMem(issuerCertInfo); //Clean up side effects if(error) { #if (TLS_RSA_SIGN_SUPPORT == ENABLED || TLS_RSA_SUPPORT == ENABLED || \ TLS_DHE_RSA_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED) //Release peer's RSA public key rsaFreePublicKey(&context->peerRsaPublicKey); #endif #if (TLS_DSA_SIGN_SUPPORT == ENABLED || TLS_DHE_DSS_SUPPORT == ENABLED) //Release peer's DSA public key dsaFreePublicKey(&context->peerDsaPublicKey); #endif #if (TLS_ECDSA_SIGN_SUPPORT == ENABLED || TLS_ECDHE_ECDSA_SUPPORT == ENABLED) //Release peer's EC domain parameters ecFreeDomainParameters(&context->peerEcParams); //Release peer's EC public key ecFree(&context->peerEcPublicKey); #endif } //TLS operates as a client or a server? if(context->entity == TLS_CONNECTION_END_CLIENT) { //Update FSM state if(context->keyExchMethod == TLS_KEY_EXCH_RSA) context->state = TLS_STATE_CERTIFICATE_REQUEST; else context->state = TLS_STATE_SERVER_KEY_EXCHANGE; } else { //Prepare to receive ClientKeyExchange message... context->state = TLS_STATE_CLIENT_KEY_EXCHANGE; } //Return status code return error; } /** * @brief Parse ChangeCipherSpec message * @param[in] context Pointer to the TLS context * @param[in] message Incoming ChangeCipherSpec message to parse * @param[in] length Message length * @return Error code **/ error_t tlsParseChangeCipherSpec(TlsContext *context, const TlsChangeCipherSpec *message, size_t length) { error_t error; //Debug message TRACE_INFO("ChangeCipherSpec message received (%" PRIuSIZE " bytes)...\r\n", length); TRACE_DEBUG_ARRAY(" ", message, length); //Check the length of the ChangeCipherSpec message if(length < sizeof(TlsChangeCipherSpec)) return ERROR_DECODING_FAILED; //TLS operates as a client or a server? if(context->entity == TLS_CONNECTION_END_CLIENT) { //Check current state if(context->state != TLS_STATE_SERVER_CHANGE_CIPHER_SPEC) return ERROR_UNEXPECTED_MESSAGE; } else { //Check current state if(context->state != TLS_STATE_CLIENT_CHANGE_CIPHER_SPEC) return ERROR_UNEXPECTED_MESSAGE; } //Initialize decryption engine error = tlsInitDecryptionEngine(context); //Any error to report? if(error) return error; //Inform the record layer that subsequent records will be protected //under the newly negotiated encryption algorithm context->changeCipherSpecReceived = TRUE; //Prepare to receive a Finished message from the peer... if(context->entity == TLS_CONNECTION_END_CLIENT) context->state = TLS_STATE_SERVER_FINISHED; else context->state = TLS_STATE_CLIENT_FINISHED; //Successful processing return NO_ERROR; } /** * @brief Parse Finished message * @param[in] context Pointer to the TLS context * @param[in] message Incoming Finished message to parse * @param[in] length Message length * @return Error code **/ error_t tlsParseFinished(TlsContext *context, const TlsFinished *message, size_t length) { error_t error; //Debug message TRACE_INFO("Finished message received (%" PRIuSIZE " bytes)...\r\n", length); TRACE_DEBUG_ARRAY(" ", message, length); //Check the length of the Finished message if(length < sizeof(TlsFinished)) return ERROR_DECODING_FAILED; //TLS operates as a client or a server? if(context->entity == TLS_CONNECTION_END_CLIENT) { //Check current state if(context->state != TLS_STATE_SERVER_FINISHED) return ERROR_UNEXPECTED_MESSAGE; //The verify data is generated from all messages in this //handshake up to but not including the Finished message error = tlsComputeVerifyData(context, TLS_CONNECTION_END_SERVER); } else { //Check current state if(context->state != TLS_STATE_CLIENT_FINISHED) return ERROR_UNEXPECTED_MESSAGE; //The verify data is generated from all messages in this //handshake up to but not including the Finished message error = tlsComputeVerifyData(context, TLS_CONNECTION_END_CLIENT); } //Unable to generate the verify data? if(error) return error; //Check message length if(LOAD24BE(message->length) != context->verifyDataLen) return ERROR_DECODING_FAILED; //Check the resulting verify data if(memcmp(message->verifyData, context->verifyData, context->verifyDataLen)) return ERROR_INVALID_SIGNATURE; //Update the hash value with the incoming handshake message tlsUpdateHandshakeHash(context, message, length); //TLS operates as a client or a server? if(context->entity == TLS_CONNECTION_END_CLIENT) { //Use abbreviated or full handshake? if(context->resume) context->state = TLS_STATE_CLIENT_CHANGE_CIPHER_SPEC; else context->state = TLS_STATE_APPLICATION_DATA; } else { //Use abbreviated or full handshake? if(context->resume) context->state = TLS_STATE_APPLICATION_DATA; else context->state = TLS_STATE_SERVER_CHANGE_CIPHER_SPEC; } //Successful processing return NO_ERROR; } /** * @brief Parse Alert message * @param[in] context Pointer to the TLS context * @param[in] message Incoming Alert message to parse * @param[in] length Message length * @return Error code **/ error_t tlsParseAlert(TlsContext *context, const TlsAlert *message, size_t length) { //Debug message TRACE_INFO("Alert message received (%" PRIuSIZE " bytes)...\r\n", length); TRACE_INFO_ARRAY(" ", message, length); //Check message length if(length != sizeof(TlsAlert)) return ERROR_INVALID_LENGTH; //Debug message TRACE_DEBUG(" Level = %" PRIu8 "\r\n", message->level); TRACE_DEBUG(" Description = %" PRIu8 "\r\n", message->description); //Alert messages convey the severity of the message if(message->level == TLS_ALERT_LEVEL_WARNING) { //Closure alert received? if(message->description == TLS_ALERT_CLOSE_NOTIFY) { //A closure alert has been received context->closeNotifyReceived = TRUE; //Close down the connection immediately if(context->state == TLS_STATE_APPLICATION_DATA) context->state = TLS_STATE_CLOSING; } } else if(message->level == TLS_ALERT_LEVEL_FATAL) { //A fatal alert message has been received context->fatalAlertReceived = TRUE; //Any connection terminated with a fatal alert must not be resumed if(context->entity == TLS_CONNECTION_END_SERVER) tlsRemoveFromCache(context); //Servers and clients must forget any session identifiers memset(context->sessionId, 0, 32); context->sessionIdLen = 0; //Alert messages with a level of fatal result in the immediate //termination of the connection context->state = TLS_STATE_CLOSED; } else { //Report an error return ERROR_ILLEGAL_PARAMETER; } //Successful processing return NO_ERROR; } #endif