error_t dhcpv6DumpAuthOption(const Dhcpv6Option *option, uint_t level) { uint_t n; Dhcpv6AuthOption *authOption; //Check the length of the option if(ntohs(option->length) < sizeof(Dhcpv6AuthOption)) return ERROR_INVALID_OPTION; //Point to the option contents authOption = (Dhcpv6AuthOption *) option->value; //Get the length of the authentication information n = ntohs(option->length) - sizeof(Dhcpv6AuthOption); //Dump contents TRACE_DEBUG("%sAuthentication option (%u bytes)\r\n", prefix[level], ntohs(option->length)); TRACE_DEBUG("%sProtocol = %u\r\n", prefix[level + 1], authOption->protocol); TRACE_DEBUG("%sAlgorithm = %u\r\n", prefix[level + 1], authOption->algorithm); TRACE_DEBUG("%sRDM = %u\r\n", prefix[level + 1], authOption->rdm); TRACE_DEBUG("%sReplay Detection\r\n", prefix[level + 1]); TRACE_DEBUG_ARRAY(prefix[level + 2], authOption->replayDetection, 8); TRACE_DEBUG("%sAuthentication Information (%u bytes)\r\n", prefix[level + 1], n); TRACE_DEBUG_ARRAY(prefix[level + 2], authOption->authInfo, n); //No error to report return NO_ERROR; }
error_t dhcpDumpRawData(const DhcpOption *option) { //Dump option contents TRACE_DEBUG_ARRAY(" ", option->value, option->length); //No error to report return NO_ERROR; }
void sntpDumpMessage(const NtpHeader *message, size_t length) { #if (SNTP_TRACE_LEVEL >= TRACE_LEVEL_DEBUG) uint8_t *p; NtpAuthData* authData; //Point to the beginning of the message p = (uint8_t *) message; //Check message length if(length >= sizeof(NtpHeader)) { //Dump NTP message TRACE_DEBUG(" Mode = %" PRIu8 "\r\n", message->mode); TRACE_DEBUG(" Version = %" PRIu8 "\r\n", message->vn); TRACE_DEBUG(" Leap indicator = %" PRIu8 "\r\n", message->li); TRACE_DEBUG(" Stratum = %" PRIu8 "\r\n", message->stratum); TRACE_DEBUG(" Poll = %" PRIu8 "\r\n", message->poll); TRACE_DEBUG(" Precision = %" PRId8 "\r\n", message->precision); TRACE_DEBUG(" Root Delay = %" PRIu32 "\r\n", ntohl(message->rootDelay)); TRACE_DEBUG(" Root Dispersion = %" PRIu32 "\r\n", ntohl(message->rootDispersion)); TRACE_DEBUG(" Reference Identifier = %" PRIu32 "\r\n", ntohl(message->referenceIdentifier)); //Dump reference timestamp TRACE_DEBUG(" ReferenceTimestamp\r\n"); sntpDumpTimestamp(&message->referenceTimestamp); //Dump originate timestamp TRACE_DEBUG(" Originate Timestamp\r\n"); sntpDumpTimestamp(&message->originateTimestamp); //Dump receive timestamp TRACE_DEBUG(" Receive Timestamp\r\n"); sntpDumpTimestamp(&message->receiveTimestamp); //Dump transmit timestamp TRACE_DEBUG(" Transmit Timestamp\r\n"); sntpDumpTimestamp(&message->transmitTimestamp); //Advance data pointer p += sizeof(NtpHeader); length -= sizeof(NtpHeader); //Any authentication data? if(length >= sizeof(NtpAuthData)) { //Point to the beginning of the authentication data authData = (NtpAuthData *) p; //Dump transmit timestamp TRACE_DEBUG(" Key Identifier = %" PRIu32 "\r\n", ntohl(authData->keyIdentifier)); //Dump message digest TRACE_DEBUG(" Message Digest\r\n"); TRACE_DEBUG_ARRAY(" ", authData->messageDigest, 16); } } #endif }
error_t dhcpv6DumpGenericOption(const Dhcpv6Option *option, uint_t level) { //Dump contents TRACE_DEBUG("%sOption %u (%u bytes)\r\n", prefix[level], ntohs(option->code), ntohs(option->length)); TRACE_DEBUG_ARRAY(prefix[level + 1], option->value, ntohs(option->length)); //No error to report return NO_ERROR; }
error_t dhcpv6DumpDomainListOption(const Dhcpv6Option *option, uint_t level) { //Dump contents TRACE_DEBUG("%sDomain Search List option (%u bytes)\r\n", prefix[level], ntohs(option->length)); TRACE_DEBUG_ARRAY(prefix[level + 1], option->value, ntohs(option->length)); //No error to report return NO_ERROR; }
error_t dhcpv6DumpVendorSpecificInfoOption(const Dhcpv6Option *option, uint_t level) { //Dump contents TRACE_DEBUG("%sVendor Specific Information option (%u bytes)\r\n", prefix[level], ntohs(option->length)); TRACE_DEBUG_ARRAY(prefix[level + 1], option->value, ntohs(option->length)); //No error to report return NO_ERROR; }
error_t dhcpv6DumpServerIdOption(const Dhcpv6Option *option, uint_t level) { //Dump contents TRACE_DEBUG("%sServer Identifier option (%u bytes)\r\n", prefix[level], ntohs(option->length)); TRACE_DEBUG_ARRAY(prefix[level + 1], option->value, ntohs(option->length)); //No error to report return NO_ERROR; }
error_t tlsSendFinished(TlsContext *context) { error_t error; size_t length; TlsFinished *message; //The verify data is generated from all messages in this //handshake up to but not including the Finished message error = tlsComputeVerifyData(context, context->entity); //Unable to generate the verify data? if(error) return error; //Point to the ChangeCipherSpec message message = (TlsFinished *) (context->txBuffer + sizeof(TlsRecord)); //Set message type message->msgType = TLS_TYPE_FINISHED; //Set message length STORE24BE(context->verifyDataLength, message->length); //Copy the resulting verify data memcpy(message->verifyData, context->verifyData, context->verifyDataLength); //Length of the complete handshake message length = context->verifyDataLength + sizeof(TlsHandshake); //Debug message TRACE_INFO("Sending Finished message (%" PRIuSIZE " bytes)...\r\n", length); TRACE_DEBUG_ARRAY(" ", message, length); //Send handshake message error = tlsWriteProtocolData(context, length, TLS_TYPE_HANDSHAKE); //Failed to send TLS record? if(error) return error; //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; } //Successful processing return NO_ERROR; }
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; } //Allocate a memory buffer to hold the decryption context context->readCipherContext = osAllocMem(context->cipherAlgo->contextSize); //Failed to allocate memory? if(!context->readCipherContext) return ERROR_OUT_OF_MEMORY; //Configure the decryption engine with the read key error = context->cipherAlgo->init(context->readCipherContext, context->readEncKey, context->encKeyLength); //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... context->state = (context->entity == TLS_CONNECTION_END_CLIENT) ? TLS_STATE_SERVER_FINISHED : TLS_STATE_CLIENT_FINISHED; //Successful processing return NO_ERROR; }
error_t dhComputeSharedSecret(DhContext *context, uint8_t *output, size_t outputSize, size_t *outputLength) { error_t error; size_t k; Mpi z; //Debug message TRACE_DEBUG("Computing Diffie-Hellman shared secret...\r\n"); //Get the length in octets of the prime modulus k = mpiGetByteLength(&context->params.p); //Make sure that the output buffer is large enough if(outputSize < k) return ERROR_INVALID_LENGTH; //The multiple precision integer must be initialized before it can be used mpiInit(&z); //Start of exception handling block do { //Calculate the shared secret key (k = yb ^ xa mod p) error = mpiExpMod(&z, &context->yb, &context->xa, &context->params.p); //Any error to report? if(error) return error; //Convert the resulting integer to an octet string error = mpiWriteRaw(&z, output, k); //Conversion failed? if(error) return error; //Length of the resulting shared secret *outputLength = k; //Debug message TRACE_DEBUG(" Shared secret (%" PRIuSIZE " bytes):\r\n", *outputLength); TRACE_DEBUG_ARRAY(" ", output, *outputLength); //End of exception handling block } while(0); //Release previously allocated resources mpiFree(&z); //Return status code return error; }
error_t tlsSendChangeCipherSpec(TlsContext *context) { error_t error; size_t length; TlsChangeCipherSpec *message; //Point to the ChangeCipherSpec message message = (TlsChangeCipherSpec *) (context->txBuffer + sizeof(TlsRecord)); //Write the type field message->type = 1; //The message consists of a single byte length = sizeof(TlsChangeCipherSpec); //Debug message TRACE_INFO("Sending ChangeCipherSpec message (%" PRIuSIZE " bytes)...\r\n", length); TRACE_DEBUG_ARRAY(" ", message, length); //Send ChangeCipherSpec message error = tlsWriteProtocolData(context, length, TLS_TYPE_CHANGE_CIPHER_SPEC); //Failed to send TLS record? if(error) return error; //Allocate a memory buffer to hold the encryption context context->writeCipherContext = osAllocMem(context->cipherAlgo->contextSize); //Failed to allocate memory? if(!context->writeCipherContext) return ERROR_OUT_OF_MEMORY; //Configure the encryption engine with the write key error = context->cipherAlgo->init(context->writeCipherContext, context->writeEncKey, context->encKeyLength); //Initialization failed? if(error) return 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... context->state = (context->entity == TLS_CONNECTION_END_CLIENT) ? TLS_STATE_CLIENT_FINISHED : TLS_STATE_SERVER_FINISHED; //Successful processing return NO_ERROR; }
error_t ipv6cpDumpOptions(const PppOption *option, size_t length) { //Parse options while(length > 0) { //Malformed IPV6CP packet? if(length < sizeof(PppOption)) return ERROR_INVALID_LENGTH; //Check option length if(option->length < sizeof(PppOption)) return ERROR_INVALID_LENGTH; if(option->length > length) return ERROR_INVALID_LENGTH; //Display the name of the current option if(option->type < arraysize(ipv6cpOptionLabel)) TRACE_DEBUG(" %s option (%" PRIu8 " bytes)\r\n", ipv6cpOptionLabel[option->type], option->length); else TRACE_DEBUG(" Option %" PRIu8 " (%" PRIu8 " bytes)\r\n", option->type, option->length); //Check option code switch(option->type) { //Raw data? default: //Dump option contents TRACE_DEBUG_ARRAY(" ", option->data, option->length - sizeof(PppOption)); break; } //Remaining bytes to process length -= option->length; //Jump to the next option option = (PppOption *) ((uint8_t *) option + option->length); } //No error to report return NO_ERROR; }
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 = osAllocMem(sizeof(X509CertificateInfo)); //Failed to allocate memory? if(!certInfo) break; //Allocate a memory buffer to store the parent certificate issuerCertInfo = osAllocMem(sizeof(X509CertificateInfo)); //Failed to allocate memory? if(!issuerCertInfo) 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) { //Point to the last character of the common name int_t i = certInfo->subject.commonNameLen - 1; //Point to the last character of the hostname int_t 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 //The certificate contains a valid RSA public key? if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen, RSA_ENCRYPTION_OID, sizeof(RSA_ENCRYPTION_OID))) { //Retrieve the RSA public key error = x509ReadRsaPublicKey(certInfo, &context->peerRsaPublicKey); //Any error to report if(error) break; //Save the certificate type context->peerCertType = TLS_CERT_RSA_SIGN; } //The certificate contains a valid DSA public key? else if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen, DSA_OID, sizeof(DSA_OID))) { //Retrieve the DSA public key error = x509ReadDsaPublicKey(certInfo, &context->peerDsaPublicKey); //Any error to report if(error) break; //Save the certificate type context->peerCertType = TLS_CERT_DSS_SIGN; } //The certificate contains a valid EC public key? else 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; } //The certificate does not contain any valid public key? else { //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; //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->trustedCaListLength; //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 osFreeMem(derCert); //End of exception handling block } while(0); //Free previously allocated memory osFreeMem(certInfo); osFreeMem(issuerCertInfo); //Clean up side effects if(error) { //Release previously allocated memory rsaFreePublicKey(&context->peerRsaPublicKey); dsaFreePublicKey(&context->peerDsaPublicKey); ecFreeDomainParameters(&context->peerEcParams); ecFree(&context->peerEcPublicKey); } //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_SERVER_KEY_EXCHANGE; else context->state = TLS_STATE_CERTIFICATE_REQUEST; } else { //Prepare to receive ClientKeyExchange message... context->state = TLS_STATE_CLIENT_KEY_EXCHANGE; } //Return status code return error; }
error_t lcpDumpOptions(const PppOption *option, size_t length) { uint32_t value; //Parse options while(length > 0) { //Malformed LCP packet? if(length < sizeof(PppOption)) return ERROR_INVALID_LENGTH; //Check option length if(option->length < sizeof(PppOption)) return ERROR_INVALID_LENGTH; if(option->length > length) return ERROR_INVALID_LENGTH; //Display the name of the current option if(option->type < arraysize(lcpOptionLabel)) TRACE_DEBUG(" %s option (%" PRIu8 " bytes)\r\n", lcpOptionLabel[option->type], option->length); else TRACE_DEBUG(" Option %" PRIu8 " (%" PRIu8 " bytes)\r\n", option->type, option->length); //Check option code switch(option->type) { //16-bit unsigned value? case LCP_OPTION_MRU: //Check length field if(option->length != (sizeof(PppOption) + sizeof(uint16_t))) return ERROR_INVALID_OPTION; //Retrieve 16-bit value value = LOAD16BE(option->data); //Dump option contents TRACE_DEBUG(" %" PRIu32 "\r\n", value); break; //32-bit unsigned value? case LCP_OPTION_ACCM: case LCP_OPTION_MAGIC_NUMBER: //Check length field if(option->length != (sizeof(PppOption) + sizeof(uint32_t))) return ERROR_INVALID_OPTION; //Retrieve 32-bit value value = LOAD32BE(option->data); //Dump option contents TRACE_DEBUG(" 0x%08" PRIX32 "\r\n", value); break; //Raw data? default: //Dump option contents TRACE_DEBUG_ARRAY(" ", option->data, option->length - sizeof(PppOption)); break; } //Remaining bytes to process length -= option->length; //Jump to the next option option = (PppOption *) ((uint8_t *) option + option->length); } //No error to report return NO_ERROR; }
error_t ipcpDumpOptions(const PppOption *option, size_t length) { #if (IPV4_SUPPORT == ENABLED) Ipv4Addr ipAddr; #endif //Parse options while(length > 0) { //Malformed IPCP packet? if(length < sizeof(PppOption)) return ERROR_INVALID_LENGTH; //Check option length if(option->length < sizeof(PppOption)) return ERROR_INVALID_LENGTH; if(option->length > length) return ERROR_INVALID_LENGTH; //Display the name of the current option if(option->type < arraysize(ipcpOptionLabel)) TRACE_DEBUG(" %s option (%" PRIu8 " bytes)\r\n", ipcpOptionLabel[option->type], option->length); else if(option->type >= 128 && option->type < (128 + arraysize(ipcpOptionLabel2))) TRACE_DEBUG(" %s option (%" PRIu8 " bytes)\r\n", ipcpOptionLabel2[option->type - 128], option->length); else TRACE_DEBUG(" Option %" PRIu8 " (%" PRIu8 " bytes)\r\n", option->type, option->length); //Check option code switch(option->type) { #if (IPV4_SUPPORT == ENABLED) //IP address? case IPCP_OPTION_IP_ADDRESS: case IPCP_OPTION_PRIMARY_DNS: case IPCP_OPTION_PRIMARY_NBNS: case IPCP_OPTION_SECONDARY_DNS: case IPCP_OPTION_SECONDARY_NBNS: //Check length field if(option->length != (sizeof(PppOption) + sizeof(Ipv4Addr))) return ERROR_INVALID_OPTION; //Retrieve IPv4 address ipv4CopyAddr(&ipAddr, option->data); //Dump option contents TRACE_DEBUG(" %s\r\n", ipv4AddrToString(ipAddr, NULL)); break; #endif //Raw data? default: //Dump option contents TRACE_DEBUG_ARRAY(" ", option->data, option->length - sizeof(PppOption)); break; } //Remaining bytes to process length -= option->length; //Jump to the next option option = (PppOption *) ((uint8_t *) option + option->length); } //No error to report return NO_ERROR; }
error_t papDumpPacket(const PppPacket *packet, size_t length) { const char_t *label; //Make sure the PAP packet is valid if(length < sizeof(PppPacket)) return ERROR_INVALID_LENGTH; //Check the length field if(ntohs(packet->length) > length) return ERROR_INVALID_LENGTH; if(ntohs(packet->length) < sizeof(PppPacket)) return ERROR_INVALID_LENGTH; //Save the length of the PAP packet length = ntohs(packet->length); //Retrieve the name of the PAP packet if(packet->code < arraysize(papCodeLabel)) label = papCodeLabel[packet->code]; else label = "Unknown"; //Dump PAP packet header TRACE_DEBUG(" Code = %" PRIu8 " (%s)\r\n", packet->code, label); TRACE_DEBUG(" Identifier = %" PRIu8 "\r\n", packet->identifier); TRACE_DEBUG(" Length = %" PRIu16 "\r\n", ntohs(packet->length)); //Authenticate-Request packet? if(packet->code == PAP_CODE_AUTH_REQ) { uint8_t *q; PapAuthReqPacket *p; //Cast PAP packet p = (PapAuthReqPacket *) packet; //Valid packet length? if(length < sizeof(PapAuthReqPacket)) return ERROR_INVALID_LENGTH; //Peer-ID-Length field TRACE_DEBUG(" Peer-ID-Length = %" PRIu8 "\r\n", p->peerIdLength); //Check the length of the Peer-ID field if(length < (sizeof(PapAuthAckPacket) + 1 + p->peerIdLength)) return ERROR_INVALID_LENGTH; //Peer-ID field TRACE_DEBUG(" Peer-ID\r\n"); TRACE_DEBUG_ARRAY(" ", p->peerId, p->peerIdLength); //Point to the Passwd-Length field q = p->peerId + p->peerIdLength; //Passwd-Length field TRACE_DEBUG(" Passwd-Length = %" PRIu8 "\r\n", q[0]); //Check the length of the Password field if(length < (sizeof(PapAuthAckPacket) + 1 + p->peerIdLength + q[0])) return ERROR_INVALID_LENGTH; //Password field TRACE_DEBUG(" Password\r\n"); TRACE_DEBUG_ARRAY(" ", q + 1, q[0]); } //Authenticate-Ack or Authenticate-Nak packet? else if(packet->code == PAP_CODE_AUTH_ACK || packet->code == PAP_CODE_AUTH_NAK) { PapAuthAckPacket *p; //Cast PAP packet p = (PapAuthAckPacket *) packet; //Valid packet length? if(length < sizeof(PapAuthAckPacket)) return ERROR_INVALID_LENGTH; //Msg-Length field TRACE_DEBUG(" Msg-Length = %" PRIu8 "\r\n", p->msgLength); //Check the length of the Message field if(length < (sizeof(PapAuthAckPacket) + p->msgLength)) return ERROR_INVALID_LENGTH; if(length > 0) { //Message field TRACE_DEBUG(" Message\r\n"); TRACE_DEBUG_ARRAY(" ", p->message, p->msgLength); } } //Unknown packet? else { //Retrieve the length of data length -= sizeof(PppPacket); //Any data? if(length > 0) { //Dump data TRACE_DEBUG(" Data (%" PRIuSIZE " bytes)\r\n", length); TRACE_DEBUG_ARRAY(" ", packet->data, length); } } //No error to report return NO_ERROR; }
error_t chapDumpPacket(const PppPacket *packet, size_t length) { const char_t *label; //Make sure the CHAP packet is valid if(length < sizeof(PppPacket)) return ERROR_INVALID_LENGTH; //Check the length field if(ntohs(packet->length) > length) return ERROR_INVALID_LENGTH; if(ntohs(packet->length) < sizeof(PppPacket)) return ERROR_INVALID_LENGTH; //Save the length of the CHAP packet length = ntohs(packet->length); //Retrieve the name of the CHAP packet if(packet->code < arraysize(chapCodeLabel)) label = chapCodeLabel[packet->code]; else label = "Unknown"; //Dump CHAP packet header TRACE_DEBUG(" Code = %" PRIu8 " (%s)\r\n", packet->code, label); TRACE_DEBUG(" Identifier = %" PRIu8 "\r\n", packet->identifier); TRACE_DEBUG(" Length = %" PRIu16 "\r\n", ntohs(packet->length)); //Challenge or Response packet? if(packet->code == CHAP_CODE_CHALLENGE || packet->code == CHAP_CODE_RESPONSE) { uint8_t *q; ChapChallengePacket *p; //Cast CHAP packet p = (ChapChallengePacket *) packet; //Valid packet length? if(length < sizeof(ChapChallengePacket)) return ERROR_INVALID_LENGTH; //Value-Size field TRACE_DEBUG(" Value-Size = %" PRIu8 "\r\n", p->valueSize); //Check the length of the Value field if(length < (sizeof(ChapChallengePacket) + p->valueSize)) return ERROR_INVALID_LENGTH; //Value field TRACE_DEBUG(" Value\r\n"); TRACE_DEBUG_ARRAY(" ", p->value, p->valueSize); //Point to the Name field q = p->value + p->valueSize; //Retrieve the length of the Name field length -= sizeof(ChapChallengePacket) + p->valueSize; //Name field TRACE_DEBUG(" Name (%" PRIuSIZE " bytes)\r\n", length); TRACE_DEBUG_ARRAY(" ", q, length); } //Success or Failure packet? else if(packet->code == CHAP_CODE_SUCCESS || packet->code == CHAP_CODE_FAILURE) { ChapSuccessPacket *p; //Cast CHAP packet p = (ChapSuccessPacket *) packet; //Valid packet length? if(length < sizeof(ChapSuccessPacket)) return ERROR_INVALID_LENGTH; //Retrieve the length of Message field length -= sizeof(ChapSuccessPacket); //Message field TRACE_DEBUG(" Message (%" PRIuSIZE " bytes)\r\n", length); TRACE_DEBUG_ARRAY(" ", p->message, length); } //Unknown packet? else { //Retrieve the length of data length -= sizeof(PppPacket); //Any data? if(length > 0) { //Dump data TRACE_DEBUG(" Data (%" PRIuSIZE " bytes)\r\n", length); TRACE_DEBUG_ARRAY(" ", packet->data, length); } } //No error to report return NO_ERROR; }
error_t lcpDumpPacket(const PppPacket *packet, size_t length) { error_t error; const char_t *label; //Make sure the LCP packet is valid if(length < sizeof(PppPacket)) return ERROR_INVALID_LENGTH; //Check the length field if(ntohs(packet->length) > length) return ERROR_INVALID_LENGTH; if(ntohs(packet->length) < sizeof(PppPacket)) return ERROR_INVALID_LENGTH; //Save the length of the LCP packet length = ntohs(packet->length); //Retrieve the name of the LCP packet if(packet->code < arraysize(lcpCodeLabel)) label = lcpCodeLabel[packet->code]; else label = "Unknown"; //Dump LCP packet header TRACE_DEBUG(" Code = %" PRIu8 " (%s)\r\n", packet->code, label); TRACE_DEBUG(" Identifier = %" PRIu8 "\r\n", packet->identifier); TRACE_DEBUG(" Length = %" PRIu16 "\r\n", ntohs(packet->length)); //Configure-Request, Configure-Ack, Configure-Nak or Configure-Reject packet? if(packet->code == PPP_CODE_CONFIGURE_REQ || packet->code == PPP_CODE_CONFIGURE_ACK || packet->code == PPP_CODE_CONFIGURE_NAK || packet->code == PPP_CODE_CONFIGURE_REJ) { //Cast LCP packet PppConfigurePacket *p = (PppConfigurePacket *) packet; //Valid packet length? if(length < sizeof(PppConfigurePacket)) return ERROR_INVALID_LENGTH; //Retrieve the length of the option list length -= sizeof(PppConfigurePacket); //Dump options error = lcpDumpOptions((PppOption *) p->options, length); //Any error to report? if(error) return error; } //Terminate-Request or Terminate-Ack packet? else if(packet->code == PPP_CODE_TERMINATE_REQ || packet->code == PPP_CODE_TERMINATE_ACK) { //Cast LCP packet PppTerminatePacket *p = (PppTerminatePacket *) packet; //Valid packet length? if(length < sizeof(PppTerminatePacket)) return ERROR_INVALID_LENGTH; //Retrieve the length of data length -= sizeof(PppTerminatePacket); //Any data? if(length > 0) { //Dump data TRACE_DEBUG(" Data (%" PRIuSIZE " bytes)\r\n", length); TRACE_DEBUG_ARRAY(" ", p->data, length); } } //Code-Reject packet? else if(packet->code == PPP_CODE_CODE_REJ) { //Cast LCP packet PppCodeRejPacket *p = (PppCodeRejPacket *) packet; //Valid packet length? if(length < sizeof(PppCodeRejPacket)) return ERROR_INVALID_LENGTH; //Retrieve the length of Rejected-Packet field length -= sizeof(PppCodeRejPacket); //Rejected-Packet TRACE_DEBUG(" Rejected-Packet (%" PRIuSIZE " bytes)\r\n", length); TRACE_DEBUG_ARRAY(" ", p->rejectedPacket, length); } //Protocol-Reject packet? else if(packet->code == PPP_CODE_PROTOCOL_REJ) { //Cast LCP packet PppProtocolRejPacket *p = (PppProtocolRejPacket *) packet; //Valid packet length? if(length < sizeof(PppProtocolRejPacket)) return ERROR_INVALID_LENGTH; //Retrieve the length of Rejected-Information field length -= sizeof(PppProtocolRejPacket); //Rejected-Protocol TRACE_DEBUG(" Rejected-Protocol = %" PRIu16 "\r\n", htons(p->rejectedProtocol)); //Rejected-Information TRACE_DEBUG(" Rejected-Information (%" PRIuSIZE " bytes)\r\n", length); TRACE_DEBUG_ARRAY(" ", p->rejectedInfo, length); } //Echo-Request, Echo-Reply or Discard-Request packet? else if(packet->code == PPP_CODE_ECHO_REQ || packet->code == PPP_CODE_ECHO_REP || packet->code == PPP_CODE_DISCARD_REQ) { //Cast LCP packet PppEchoPacket *p = (PppEchoPacket *) packet; //Valid packet length? if(length < sizeof(PppEchoPacket)) return ERROR_INVALID_LENGTH; //Retrieve the length of data length -= sizeof(PppEchoPacket); //Magic-Number TRACE_DEBUG(" Magic-Number = %" PRIu32 "\r\n", htonl(p->magicNumber)); //Data TRACE_DEBUG(" Data (%" PRIuSIZE " bytes)\r\n", length); TRACE_DEBUG_ARRAY(" ", p->data, length); } //Unknown packet? else { //Retrieve the length of data length -= sizeof(PppPacket); //Any data? if(length > 0) { //Dump data TRACE_DEBUG(" Data (%" PRIuSIZE " bytes)\r\n", length); TRACE_DEBUG_ARRAY(" ", packet->data, length); } } //No error to report return NO_ERROR; }
error_t ncpDumpPacket(const PppPacket *packet, size_t length, PppProtocol protocol) { error_t error; const char_t *label; //Make sure the NDP packet is valid if(length < sizeof(PppPacket)) return ERROR_INVALID_LENGTH; //Check the length field if(ntohs(packet->length) > length) return ERROR_INVALID_LENGTH; if(ntohs(packet->length) < sizeof(PppPacket)) return ERROR_INVALID_LENGTH; //Save the length of the NDP packet length = ntohs(packet->length); //Retrieve the name of the NDP packet if(packet->code < arraysize(ncpCodeLabel)) label = ncpCodeLabel[packet->code]; else label = "Unknown"; //Dump NDP packet header TRACE_DEBUG(" Code = %" PRIu8 " (%s)\r\n", packet->code, label); TRACE_DEBUG(" Identifier = %" PRIu8 "\r\n", packet->identifier); TRACE_DEBUG(" Length = %" PRIu16 "\r\n", ntohs(packet->length)); //Configure-Request, Configure-Ack, Configure-Nak or Configure-Reject packet? if(packet->code == PPP_CODE_CONFIGURE_REQ || packet->code == PPP_CODE_CONFIGURE_ACK || packet->code == PPP_CODE_CONFIGURE_NAK || packet->code == PPP_CODE_CONFIGURE_REJ) { //Cast NDP packet PppConfigurePacket *p = (PppConfigurePacket *) packet; //Valid packet length? if(length < sizeof(PppConfigurePacket)) return ERROR_INVALID_LENGTH; //Retrieve the length of the option list length -= sizeof(PppConfigurePacket); //IPCP protocol? if(protocol == PPP_PROTOCOL_IPCP) { //Dump options error = ipcpDumpOptions((PppOption *) p->options, length); //Any error to report? if(error) return error; } //IPV6CP protocol? else if(protocol == PPP_PROTOCOL_IPV6CP) { //Dump options error = ipv6cpDumpOptions((PppOption *) p->options, length); //Any error to report? if(error) return error; } } //Terminate-Request or Terminate-Ack packet? else if(packet->code == PPP_CODE_TERMINATE_REQ || packet->code == PPP_CODE_TERMINATE_ACK) { //Cast NDP packet PppTerminatePacket *p = (PppTerminatePacket *) packet; //Valid packet length? if(length < sizeof(PppTerminatePacket)) return ERROR_INVALID_LENGTH; //Retrieve the length of data length -= sizeof(PppTerminatePacket); //Any data? if(length > 0) { //Dump data TRACE_DEBUG(" Data (%" PRIuSIZE " bytes)\r\n", length); TRACE_DEBUG_ARRAY(" ", p->data, length); } } //Code-Reject packet? else if(packet->code == PPP_CODE_CODE_REJ) { //Cast NDP packet PppCodeRejPacket *p = (PppCodeRejPacket *) packet; //Valid packet length? if(length < sizeof(PppCodeRejPacket)) return ERROR_INVALID_LENGTH; //Retrieve the length of Rejected-Packet field length -= sizeof(PppCodeRejPacket); //Rejected-Packet TRACE_DEBUG(" Rejected-Packet (%" PRIuSIZE " bytes)\r\n", length); TRACE_DEBUG_ARRAY(" ", p->rejectedPacket, length); } //Unknown packet? else { //Retrieve the length of data length -= sizeof(PppPacket); //Any data? if(length > 0) { //Dump data TRACE_DEBUG(" Data (%" PRIuSIZE " bytes)\r\n", length); TRACE_DEBUG_ARRAY(" ", packet->data, length); } } //No error to report return NO_ERROR; }
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->verifyDataLength) return ERROR_DECODING_FAILED; //Check the resulting verify data if(memcmp(message->verifyData, context->verifyData, context->verifyDataLength)) 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; }
error_t snmpParseMessage(SnmpAgentContext *context, const uint8_t *p, size_t length) { error_t error; int32_t version; Asn1Tag tag; //Debug message TRACE_INFO("SNMP message received from %s port %" PRIu16 " (%" PRIuSIZE " bytes)...\r\n", ipAddrToString(&context->remoteIpAddr, NULL), context->remotePort, length); //Display the contents of the SNMP message TRACE_DEBUG_ARRAY(" ", p, length); //Dump ASN.1 structure error = asn1DumpObject(p, length, 0); //Any error to report? if(error) return error; //The SNMP message is encapsulated within a sequence error = asn1ReadTag(p, length, &tag); //Failed to decode ASN.1 tag? if(error) return error; //Enforce encoding, class and type error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE); //The tag does not match the criteria? if(error) return error; //Point to the first field of the sequence p = tag.value; length = tag.length; //Read version identifier error = asn1ReadInt32(p, length, &tag, &version); //Failed to decode ASN.1 tag? if(error) return error; //The SNMP agent verifies the version number. If there is a mismatch, //it discards the datagram and performs no further actions if(version != context->version) return ERROR_INVALID_VERSION; //Point to the next field p += tag.totalLength; length -= tag.totalLength; //Read SNMP community name error = asn1ReadTag(p, length, &tag); //Failed to decode ASN.1 tag? if(error) return error; //Enforce encoding, class and type error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OCTET_STRING); //The tag does not match the criteria? if(error) return error; //Save community name context->request.community = tag.value; context->request.communityLen = tag.length; //Point to the next field p += tag.totalLength; length -= tag.totalLength; //Read PDU error = asn1ReadTag(p, length, &tag); //Failed to decode ASN.1 tag? if(error) return error; //Check encoding if(tag.constructed != TRUE) return ERROR_WRONG_ENCODING; //Enforce class if(tag.class != ASN1_CLASS_CONTEXT_SPECIFIC) return ERROR_INVALID_CLASS; //Save PDU type context->request.pduType = (SnmpPduType) tag.type; //Process the protocol data unit switch(context->request.pduType) { case SNMP_PDU_GET_REQUEST: case SNMP_PDU_GET_NEXT_REQUEST: //Parse GetRequest-PDU or GetNextRequest-PDU error = snmpParseGetRequestPdu(context, tag.value, tag.length); break; case SNMP_PDU_GET_BULK_REQUEST: //Parse GetBulkRequest-PDU error = snmpParseGetBulkRequestPdu(context, tag.value, tag.length); break; case SNMP_PDU_SET_REQUEST: //Parse SetRequest-PDU error = snmpParseSetRequestPdu(context, tag.value, tag.length); break; default: //Invalid PDU type error = ERROR_INVALID_TYPE; break; } //Failed to parse PDU? if(error) return error; //Format response PDU header error = snmpWritePduHeader(context); //Any error to report? if(error) return error; //Debug message TRACE_INFO("SNMP message sent (%" PRIuSIZE " bytes)...\r\n", context->response.messageLen); //Display the contents of the SNMP message TRACE_DEBUG_ARRAY(" ", context->response.message, context->response.messageLen); //Display ASN.1 structure error = asn1DumpObject(context->response.message, context->response.messageLen, 0); //Any error to report? if(error) return error; //Send SNMP response message error = socketSendTo(context->socket, &context->remoteIpAddr, context->remotePort, context->response.message, context->response.messageLen, NULL, 0); //Return status code return error; }
error_t tlsWriteRecord(TlsContext *context, size_t length, TlsContentType contentType) { error_t error; uint_t i; size_t paddingLength; //Point to the TLS record TlsRecord *record = (TlsRecord *) context->txBuffer; //Format TLS record record->type = contentType; record->version = htons(context->version); record->length = htons(length); //Debug message TRACE_DEBUG("Sending TLS record...\r\n"); TRACE_DEBUG_ARRAY(" ", record, length + sizeof(TlsRecord)); //Protect record payload? if(context->changeCipherSpecSent) { //Message authentication is required? if(context->hashAlgo) { #if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_MIN_VERSION <= SSL_VERSION_3_0) //Check whether SSL 3.0 is currently used if(context->version == SSL_VERSION_3_0) { //SSL 3.0 uses an older obsolete version of the HMAC construction error = sslComputeMac(context, context->writeMacKey, context->writeSeqNum, record, record->data, length, record->data + length); //Any error to report? if(error) return error; } else #endif #if (TLS_MAX_VERSION >= TLS_VERSION_1_0 && TLS_MIN_VERSION <= TLS_VERSION_1_2) //Check whether TLS 1.0, TLS 1.1 or TLS 1.2 is currently used if(context->version >= TLS_VERSION_1_0) { //TLS uses a HMAC construction hmacInit(&context->hmacContext, context->hashAlgo, context->writeMacKey, context->macKeyLength); //Compute MAC over the sequence number and the record contents hmacUpdate(&context->hmacContext, context->writeSeqNum, sizeof(TlsSequenceNumber)); hmacUpdate(&context->hmacContext, record, length + sizeof(TlsRecord)); //Append the resulting MAC to the message hmacFinal(&context->hmacContext, record->data + length); } else #endif //The negotiated TLS version is not valid... { //Report an error return ERROR_INVALID_VERSION; } //Debug message TRACE_DEBUG("Write sequence number:\r\n"); TRACE_DEBUG_ARRAY(" ", context->writeSeqNum, sizeof(TlsSequenceNumber)); TRACE_DEBUG("Computed MAC:\r\n"); TRACE_DEBUG_ARRAY(" ", record->data + length, context->hashAlgo->digestSize); //Adjust the length of the message length += context->hashAlgo->digestSize; //Fix length field record->length = htons(length); //Increment sequence number tlsIncSequenceNumber(context->writeSeqNum); } //Encryption is required? if(context->cipherAlgo) { #if (TLS_STREAM_CIPHER_SUPPORT == ENABLED) //Stream cipher? if(context->cipherMode == CIPHER_MODE_STREAM) { //Debug message TRACE_DEBUG("Record before encryption:\r\n"); TRACE_DEBUG_ARRAY(" ", record, length + sizeof(TlsRecord)); //Encrypt record contents context->cipherAlgo->encryptStream(context->writeCipherContext, record->data, record->data, length); } else #endif #if (TLS_CBC_CIPHER_SUPPORT == ENABLED) //CBC block cipher? if(context->cipherMode == CIPHER_MODE_CBC) { #if (TLS_MAX_VERSION >= TLS_VERSION_1_1 && TLS_MIN_VERSION <= TLS_VERSION_1_2) //TLS 1.1 and 1.2 use an explicit IV if(context->version >= TLS_VERSION_1_1) { //Make room for the IV at the beginning of the data memmove(record->data + context->recordIvLength, record->data, length); //The initialization vector should be chosen at random error = context->prngAlgo->read(context->prngContext, record->data, context->recordIvLength); //Any error to report? if(error) return error; //Adjust the length of the message length += context->recordIvLength; } #endif //Get the actual amount of bytes in the last block paddingLength = (length + 1) % context->cipherAlgo->blockSize; //Padding is added to force the length of the plaintext to be //an integral multiple of the cipher's block length if(paddingLength > 0) paddingLength = context->cipherAlgo->blockSize - paddingLength; //Write padding bytes for(i = 0; i <= paddingLength; i++) record->data[length + i] = paddingLength; //Compute the length of the resulting message length += paddingLength + 1; //Fix length field record->length = htons(length); //Debug message TRACE_DEBUG("Record before encryption:\r\n"); TRACE_DEBUG_ARRAY(" ", record, length + sizeof(TlsRecord)); //CBC encryption error = cbcEncrypt(context->cipherAlgo, context->writeCipherContext, context->writeIv, record->data, record->data, length); //Any error to report? if(error) return error; } else #endif #if (TLS_CCM_CIPHER_SUPPORT == ENABLED || TLS_GCM_CIPHER_SUPPORT == ENABLED) //AEAD cipher? if(context->cipherMode == CIPHER_MODE_CCM || context->cipherMode == CIPHER_MODE_GCM) { uint8_t *data; uint8_t *tag; size_t nonceLength; uint8_t nonce[12]; uint8_t a[13]; //Determine the total length of the nonce nonceLength = context->fixedIvLength + context->recordIvLength; //The salt is the implicit part of the nonce and is not sent in the packet memcpy(nonce, context->writeIv, context->fixedIvLength); //The explicit part of the nonce is chosen by the sender error = context->prngAlgo->read(context->prngContext, nonce + context->fixedIvLength, context->recordIvLength); //Any error to report? if(error) return error; //Make room for the explicit nonce at the beginning of the record memmove(record->data + context->recordIvLength, record->data, length); //The explicit part of the nonce is carried in each TLS record memcpy(record->data, nonce + context->fixedIvLength, context->recordIvLength); //Additional data to be authenticated memcpy(a, context->writeSeqNum, sizeof(TlsSequenceNumber)); memcpy(a + sizeof(TlsSequenceNumber), record, sizeof(TlsRecord)); //Plaintext data data = record->data + context->recordIvLength; //Buffer where to store the authentication tag tag = data + length; #if (TLS_CCM_CIPHER_SUPPORT == ENABLED) //CCM cipher mode? if(context->cipherMode == CIPHER_MODE_CCM) { //Authenticated encryption using CCM error = ccmEncrypt(context->cipherAlgo, context->writeCipherContext, nonce, nonceLength, a, 13, data, data, length, tag, context->authTagLength); } else #endif #if (TLS_GCM_CIPHER_SUPPORT == ENABLED) //GCM cipher mode? if(context->cipherMode == CIPHER_MODE_GCM) { //Authenticated encryption using GCM error = gcmEncrypt(context->cipherAlgo, context->writeCipherContext, nonce, nonceLength, a, 13, data, data, length, tag, context->authTagLength); } else #endif //Invalid cipher mode? { //The specified cipher mode is not supported error = ERROR_UNSUPPORTED_CIPHER_MODE; } //Failed to encrypt data? if(error) return error; //Compute the length of the resulting message length += context->recordIvLength + context->authTagLength; //Fix length field record->length = htons(length); //Increment sequence number tlsIncSequenceNumber(context->writeSeqNum); } else #endif //Invalid cipher mode? { //The specified cipher mode is not supported return ERROR_UNSUPPORTED_CIPHER_MODE; } //Debug message TRACE_DEBUG("Encrypted record:\r\n"); TRACE_DEBUG_ARRAY(" ", record, length + sizeof(TlsRecord)); } } //Compute the length of the complete TLS record length += sizeof(TlsRecord); //Send TLS record return tlsIoWrite(context, record, length); }
error_t tlsReadRecord(TlsContext *context, uint8_t *data, size_t size, size_t *length, TlsContentType *contentType) { error_t error; size_t i; size_t n; size_t paddingLength; TlsRecord record; //Read TLS record header error = tlsIoRead(context, &record, sizeof(TlsRecord)); //Any error to report? if(error) return error; //Debug message TRACE_DEBUG("Record header:\r\n"); TRACE_DEBUG_ARRAY(" ", &record, sizeof(record)); //Check current state if(context->state > TLS_STATE_SERVER_HELLO) { //Once the server has sent the ServerHello message, enforce //incoming record versions if(ntohs(record.version) != context->version) return ERROR_VERSION_NOT_SUPPORTED; } //Convert the length field to host byte order n = ntohs(record.length); //Make sure that the buffer is large enough to hold the entire record if(n > size) return ERROR_RECORD_OVERFLOW; //Read record contents error = tlsIoRead(context, data, n); //Any error to report? if(error) return error; //Record payload is protected? if(context->changeCipherSpecReceived) { //Decrypt record if necessary if(context->cipherAlgo) { //Debug message TRACE_DEBUG("Encrypted record (%" PRIuSIZE " bytes):\r\n", n); TRACE_DEBUG_ARRAY(" ", data, n); #if (TLS_STREAM_CIPHER_SUPPORT == ENABLED) //Stream cipher? if(context->cipherMode == CIPHER_MODE_STREAM) { //Decrypt record contents context->cipherAlgo->decryptStream(context->readCipherContext, data, data, n); //Debug message TRACE_DEBUG("Decrypted record (%" PRIuSIZE " bytes):\r\n", n); TRACE_DEBUG_ARRAY(" ", data, n); } else #endif #if (TLS_CBC_CIPHER_SUPPORT == ENABLED) //CBC block cipher? if(context->cipherMode == CIPHER_MODE_CBC) { //The length of the data must be a multiple of the block size if((n % context->cipherAlgo->blockSize) != 0) return ERROR_DECODING_FAILED; //CBC decryption error = cbcDecrypt(context->cipherAlgo, context->readCipherContext, context->readIv, data, data, n); //Any error to report? if(error) return error; //Debug message TRACE_DEBUG("Decrypted record (%" PRIuSIZE " bytes):\r\n", n); TRACE_DEBUG_ARRAY(" ", data, n); #if (TLS_MAX_VERSION >= TLS_VERSION_1_1 && TLS_MIN_VERSION <= TLS_VERSION_1_2) //TLS 1.1 and 1.2 use an explicit IV if(context->version >= TLS_VERSION_1_1) { //Make sure the message length is acceptable if(n < context->recordIvLength) return ERROR_DECODING_FAILED; //Adjust the length of the message n -= context->recordIvLength; //Discard the first cipher block (corresponding to the explicit IV) memmove(data, data + context->recordIvLength, n); } #endif //Make sure the message length is acceptable if(n < context->cipherAlgo->blockSize) return ERROR_DECODING_FAILED; //Compute the length of the padding string paddingLength = data[n - 1]; //Erroneous padding length? if(paddingLength >= n) return ERROR_BAD_RECORD_MAC; //The receiver must check the padding for(i = 0; i <= paddingLength; i++) { //Each byte in the padding data must be filled //with the padding length value if(data[n - 1 - i] != paddingLength) return ERROR_BAD_RECORD_MAC; } //Remove padding bytes n -= paddingLength + 1; //Debug message TRACE_DEBUG("Padding removed (%" PRIuSIZE " bytes):\r\n", n); TRACE_DEBUG_ARRAY(" ", data, n); } else #endif #if (TLS_CCM_CIPHER_SUPPORT == ENABLED || TLS_GCM_CIPHER_SUPPORT == ENABLED) //AEAD cipher? if(context->cipherMode == CIPHER_MODE_CCM || context->cipherMode == CIPHER_MODE_GCM) { uint8_t *ciphertext; uint8_t *tag; size_t nonceLength; uint8_t nonce[12]; uint8_t a[13]; //Make sure the message length is acceptable if(n < (context->recordIvLength + context->authTagLength)) return ERROR_DECODING_FAILED; //Determine the total length of the nonce nonceLength = context->fixedIvLength + context->recordIvLength; //The salt is the implicit part of the nonce and is not sent in the packet memcpy(nonce, context->readIv, context->fixedIvLength); //The explicit part of the nonce is chosen by the sender memcpy(nonce + context->fixedIvLength, data, context->recordIvLength); //Calculate the length of the ciphertext n -= context->recordIvLength + context->authTagLength; //Fix the length field of the TLS record record.length = htons(n); //Additional data to be authenticated memcpy(a, context->readSeqNum, sizeof(TlsSequenceNumber)); memcpy(a + sizeof(TlsSequenceNumber), &record, sizeof(TlsRecord)); //Ciphertext data ciphertext = data + context->recordIvLength; //Authentication tag tag = ciphertext + n; #if (TLS_CCM_CIPHER_SUPPORT == ENABLED) //CCM cipher mode? if(context->cipherMode == CIPHER_MODE_CCM) { //Decryption and verification (using CCM) error = ccmDecrypt(context->cipherAlgo, context->readCipherContext, nonce, nonceLength, a, 13, ciphertext, ciphertext, n, tag, context->authTagLength); } else #endif #if (TLS_GCM_CIPHER_SUPPORT == ENABLED) //GCM cipher mode? if(context->cipherMode == CIPHER_MODE_GCM) { //Decryption and verification (using GCM) error = gcmDecrypt(context->cipherAlgo, context->readCipherContext, nonce, nonceLength, a, 13, ciphertext, ciphertext, n, tag, context->authTagLength); } else #endif //Invalid cipher mode? { //The specified cipher mode is not supported error = ERROR_UNSUPPORTED_CIPHER_MODE; } //Wrong authentication tag? if(error) return ERROR_BAD_RECORD_MAC; //Discard the explicit part of the nonce memmove(data, data + context->recordIvLength, n); //Debug message TRACE_DEBUG("Decrypted record (%" PRIuSIZE " bytes):\r\n", n); TRACE_DEBUG_ARRAY(" ", data, n); //Increment sequence number tlsIncSequenceNumber(context->readSeqNum); } else #endif //Invalid cipher mode? { //The specified cipher mode is not supported return ERROR_UNSUPPORTED_CIPHER_MODE; } } //Check message authentication code if necessary if(context->hashAlgo) { //Make sure the message length is acceptable if(n < context->hashAlgo->digestSize) return ERROR_DECODING_FAILED; //Adjust the length of the message n -= context->hashAlgo->digestSize; //Fix the length field of the TLS record record.length = htons(n); #if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_MIN_VERSION <= SSL_VERSION_3_0) //Check whether SSL 3.0 is currently used if(context->version == SSL_VERSION_3_0) { //SSL 3.0 uses an older obsolete version of the HMAC construction error = sslComputeMac(context, context->readMacKey, context->readSeqNum, &record, data, n, context->hmacContext.digest); //Any error to report? if(error) return error; } else #endif #if (TLS_MAX_VERSION >= TLS_VERSION_1_0 && TLS_MIN_VERSION <= TLS_VERSION_1_2) //Check whether TLS 1.0, TLS 1.1 or TLS 1.2 is currently used if(context->version >= TLS_VERSION_1_0) { //TLS uses a HMAC construction hmacInit(&context->hmacContext, context->hashAlgo, context->readMacKey, context->macKeyLength); //Compute MAC over the sequence number and the record contents hmacUpdate(&context->hmacContext, context->readSeqNum, sizeof(TlsSequenceNumber)); hmacUpdate(&context->hmacContext, &record, sizeof(TlsRecord)); hmacUpdate(&context->hmacContext, data, n); hmacFinal(&context->hmacContext, NULL); } else #endif //The negotiated TLS version is not valid... { //Report an error return ERROR_INVALID_VERSION; } //Debug message TRACE_DEBUG("Read sequence number:\r\n"); TRACE_DEBUG_ARRAY(" ", context->readSeqNum, sizeof(TlsSequenceNumber)); TRACE_DEBUG("Computed MAC:\r\n"); TRACE_DEBUG_ARRAY(" ", context->hmacContext.digest, context->hashAlgo->digestSize); //Check the message authentication code if(memcmp(data + n, context->hmacContext.digest, context->hashAlgo->digestSize)) return ERROR_BAD_RECORD_MAC; //Increment sequence number tlsIncSequenceNumber(context->readSeqNum); } } //Actual length of the record data *length = n; //Record type *contentType = (TlsContentType) record.type; //The TLS record has been successfully read return NO_ERROR; }
error_t rsassaPkcs1v15Sign(const RsaPrivateKey *key, const HashAlgo *hash, const uint8_t *digest, uint8_t *signature, size_t *signatureLength) { error_t error; uint_t k; uint8_t *em; Mpi m; Mpi s; //Check parameters if(key == NULL || hash == NULL || digest == NULL) return ERROR_INVALID_PARAMETER; if(signature == NULL || signatureLength == NULL) return ERROR_INVALID_PARAMETER; //Debug message TRACE_DEBUG("RSA PKCS #1 v1.5 signature generation...\r\n"); TRACE_DEBUG(" Modulus:\r\n"); TRACE_DEBUG_MPI(" ", &key->n); TRACE_DEBUG(" Public exponent:\r\n"); TRACE_DEBUG_MPI(" ", &key->e); TRACE_DEBUG(" Private exponent:\r\n"); TRACE_DEBUG_MPI(" ", &key->d); TRACE_DEBUG(" Prime 1:\r\n"); TRACE_DEBUG_MPI(" ", &key->p); TRACE_DEBUG(" Prime 2:\r\n"); TRACE_DEBUG_MPI(" ", &key->q); TRACE_DEBUG(" Prime exponent 1:\r\n"); TRACE_DEBUG_MPI(" ", &key->dp); TRACE_DEBUG(" Prime exponent 2:\r\n"); TRACE_DEBUG_MPI(" ", &key->dq); TRACE_DEBUG(" Coefficient:\r\n"); TRACE_DEBUG_MPI(" ", &key->qinv); TRACE_DEBUG(" Message digest:\r\n"); TRACE_DEBUG_ARRAY(" ", digest, hash->digestSize); //Initialize multiple-precision integers mpiInit(&m); mpiInit(&s); //Get the length in octets of the modulus n k = mpiGetByteLength(&key->n); //Point to the buffer where the encoded message EM will be generated em = signature; //Apply the EMSA-PKCS1-v1.5 encoding operation error = emsaPkcs1v15Encode(hash, digest, em, k); //Any error to report? if(error) return error; //Debug message TRACE_DEBUG(" Encoded message\r\n"); TRACE_DEBUG_ARRAY(" ", em, k); //Start of exception handling block do { //Convert the encoded message EM to an integer message representative m error = mpiReadRaw(&m, em, k); //Conversion failed? if(error) break; //Apply the RSASP1 signature primitive error = rsasp1(key, &m, &s); //Any error to report? if(error) break; //Convert the signature representative s to a signature of length k octets error = mpiWriteRaw(&s, signature, k); //Conversion failed? if(error) break; //Length of the resulting signature *signatureLength = k; //Debug message TRACE_DEBUG(" Signature:\r\n"); TRACE_DEBUG_ARRAY(" ", signature, *signatureLength); //End of exception handling block } while(0); //Free previously allocated memory mpiFree(&m); mpiFree(&s); //Return status code return error; }
error_t rsaesPkcs1v15Encrypt(const PrngAlgo *prngAlgo, void *prngContext, const RsaPublicKey *key, const uint8_t *message, size_t messageLength, uint8_t *ciphertext, size_t *ciphertextLength) { error_t error; uint_t i; uint_t j; uint_t k; uint_t n; uint8_t *p; Mpi m; Mpi c; //Check parameters if(key == NULL || message == NULL) return ERROR_INVALID_PARAMETER; if(ciphertext == NULL || ciphertextLength == NULL) return ERROR_INVALID_PARAMETER; //Debug message TRACE_DEBUG("RSA PKCS #1 v1.5 encryption...\r\n"); TRACE_DEBUG(" Modulus:\r\n"); TRACE_DEBUG_MPI(" ", &key->n); TRACE_DEBUG(" Public exponent:\r\n"); TRACE_DEBUG_MPI(" ", &key->e); TRACE_DEBUG(" Message:\r\n"); TRACE_DEBUG_ARRAY(" ", message, messageLength); //Initialize multiple-precision integers mpiInit(&m); mpiInit(&c); //Get the length in octets of the modulus n k = mpiGetByteLength(&key->n); //Check the length of the message if((messageLength + 11) > k) return ERROR_INVALID_LENGTH; //Point to the buffer where the encoded message EM will be formatted p = ciphertext; //The leading 0x00 octet ensures that the encoded message, //converted to an integer, is less than the modulus *(p++) = 0x00; //For a public-key operation, the block type BT shall be 0x02 *(p++) = 0x02; //Length of the padding string PS n = k - messageLength - 3; //Generate the padding string (pseudo-randomly generated non-zero octets) while(n > 0) { //Generate random data error = prngAlgo->read(prngContext, p, n); //Any error to report? if(error) return error; //Parse the resulting octet string for(i = 0, j = 0; j < n; j++) { //Strip any byte with a value of zero if(p[j] != 0) p[i++] = p[j]; } //Advance data pointer p += i; n -= i; } //Append a 0x00 octet to the padding string *(p++) = 0x00; //Copy the message to be encrypted memcpy(p, message, messageLength); //Rewind to the beginning of the encoded message p = ciphertext; //Debug message TRACE_DEBUG(" Encoded message\r\n"); TRACE_DEBUG_ARRAY(" ", p, k); //Start of exception handling block do { //Convert the encoded message EM to an integer message representative m error = mpiReadRaw(&m, p, k); //Conversion failed? if(error) break; //Apply the RSAEP encryption primitive error = rsaep(key, &m, &c); //Any error to report? if(error) break; //Convert the ciphertext representative c to a ciphertext of length k octets error = mpiWriteRaw(&c, ciphertext, k); //Conversion failed? if(error) break; //Length of the resulting ciphertext *ciphertextLength = k; //Debug message TRACE_DEBUG(" Ciphertext:\r\n"); TRACE_DEBUG_ARRAY(" ", ciphertext, *ciphertextLength); //End of exception handling block } while(0); //Free previously allocated memory mpiFree(&m); mpiFree(&c); //Return status code return error; }
error_t dhcpDumpMessage(const DhcpMessage *message, size_t length) { error_t error; uint_t i; const char_t *label; DhcpOption *option; //Ensure the length of the DHCP message is acceptable if(length < sizeof(DhcpMessage)) { //Report a warning TRACE_WARNING("DHCP message length is invalid!\r\n"); //Dump message contents for debugging purpose TRACE_DEBUG_ARRAY(" ", message, length); //Report an error return ERROR_INVALID_LENGTH; } //Retrieve the name associated with the opcode label = (message->op < arraysize(opcodeLabel)) ? opcodeLabel[message->op] : ""; //Dump DHCP message contents TRACE_DEBUG(" Op Code (op) = %" PRIu8 " (%s)\r\n", message->op, label); TRACE_DEBUG(" Hardware Type (htype) = %" PRIu8 "\r\n", message->htype); TRACE_DEBUG(" Hardware Address Length (hlen) = %" PRIu8 "\r\n", message->hlen); TRACE_DEBUG(" Hops (hops) = %" PRIu8 "\r\n", message->hops); TRACE_DEBUG(" Transaction ID (xid) = 0x%08" PRIX32 "\r\n", ntohl(message->xid)); TRACE_DEBUG(" Seconds (secs) = %" PRIu16 "s\r\n", ntohs(message->secs)); TRACE_DEBUG(" Flags (flags) = 0x%04" PRIX16 "\r\n", ntohs(message->flags)); TRACE_DEBUG(" Client IP Address (ciaddr) = %s\r\n", ipv4AddrToString(message->ciaddr, NULL)); TRACE_DEBUG(" Your IP Address (yiaddr) = %s\r\n", ipv4AddrToString(message->yiaddr, NULL)); TRACE_DEBUG(" Server IP Address (siaddr) = %s\r\n", ipv4AddrToString(message->siaddr, NULL)); TRACE_DEBUG(" Relay IP Address (giaddr) = %s\r\n", ipv4AddrToString(message->giaddr, NULL)); TRACE_DEBUG(" Client Hardware Address (chaddr) = %s\r\n", macAddrToString(&message->chaddr, NULL)); TRACE_DEBUG(" Magic Cookie = 0x%08" PRIX32 "\r\n", ntohl(message->magicCookie)); //Get the length of the options field length -= sizeof(DhcpMessage); //Parse DHCP options for(i = 0; i < length; i++) { //Point to the current option option = (DhcpOption *) (message->options + i); //Pad option detected? if(option->code == DHCP_OPT_PAD) continue; //End option detected? if(option->code == DHCP_OPT_END) break; //Check option length if((i + 1) >= length || (i + 1 + option->length) >= length) { //Report a warning TRACE_WARNING("DHCP option length is invalid!\r\n"); //Dump message contents for debugging purpose TRACE_DEBUG_ARRAY(" ", message, length); //Report an error return ERROR_INVALID_LENGTH; } //Display the name of the current option if(option->code < arraysize(optionLabel)) TRACE_DEBUG(" %s option (%" PRIu8 " bytes)\r\n", optionLabel[option->code], option->length); else TRACE_DEBUG(" Option %" PRIu8 " (%" PRIu8 " bytes)\r\n", option->code, option->length); //Check option code switch(option->code) { //Message type? case DHCP_OPT_DHCP_MESSAGE_TYPE: error = dhcpDumpMessageType(option); break; //Parameter Request List option case DHCP_OPT_PARAM_REQUEST_LIST: error = dhcpDumpParamRequestList(option); break; //Boolean value? case DHCP_OPT_IP_FORWARDING: case DHCP_OPT_NON_LOCAL_SOURCE_ROUTING: case DHCP_OPT_ALL_SUBNETS_ARE_LOCAL: case DHCP_OPT_PERFORM_MASK_DISCOVERY: case DHCP_OPT_MASK_SUPPLIER: case DHCP_OPT_PERFORM_ROUTER_DISCOVERY: case DHCP_OPT_TRAILER_ENCAPSULATION: case DHCP_OPT_ETHERNET_ENCAPSULATION: case DHCP_OPT_TCP_KEEPALIVE_GARBAGE: error = dhcpDumpBoolean(option); break; //8-bit unsigned integer? case DHCP_OPT_DEFAULT_IP_TTL: case DHCP_OPT_TCP_DEFAULT_TTL: case DHCP_OPT_NETBIOS_NODE_TYPE: case DHCP_OPT_OPTION_OVERLOAD: error = dhcpDumpInt8(option); break; //16-bit unsigned integer? case DHCP_OPT_BOOT_FILE_SIZE: case DHCP_OPT_MAX_DATAGRAM_REASSEMBLY_SIZE: case DHCP_OPT_INTERFACE_MTU: case DHCP_OPT_MAX_DHCP_MESSAGE_SIZE: error = dhcpDumpInt16(option); break; //32-bit unsigned integer? case DHCP_OPT_PATH_MTU_AGING_TIMEOUT: case DHCP_OPT_ARP_CACHE_TIMEOUT: case DHCP_OPT_TCP_KEEPALIVE_INTERVAL: case DHCP_OPT_IP_ADDRESS_LEASE_TIME: case DHCP_OPT_RENEWAL_TIME_VALUE: case DHCP_OPT_REBINDING_TIME_VALUE: error = dhcpDumpInt32(option); break; //Character strings? case DHCP_OPT_HOST_NAME: case DHCP_OPT_MERIT_DUMP_FILE: case DHCP_OPT_DOMAIN_NAME: case DHCP_OPT_ROOT_PATH: case DHCP_OPT_EXTENSIONS_PATH: case DHCP_OPT_NIS_DOMAIN: case DHCP_OPT_MESSAGE: case DHCP_OPT_NISP_DOMAIN: case DHCP_OPT_TFTP_SERVER_NAME: case DHCP_OPT_BOOTFILE_NAME: error = dhcpDumpString(option); break; //IPv4 address? case DHCP_OPT_SUBNET_MASK: case DHCP_OPT_SWAP_SERVER: case DHCP_OPT_BROADCAST_ADDRESS: case DHCP_OPT_ROUTER_SOLICITATION_ADDRESS: case DHCP_OPT_REQUESTED_IP_ADDRESS: case DHCP_OPT_SERVER_IDENTIFIER: error = dhcpDumpIpv4Addr(option); break; //List of IPv4 addresses? case DHCP_OPT_ROUTER: case DHCP_OPT_TIME_SERVER: case DHCP_OPT_NAME_SERVER: case DHCP_OPT_DNS_SERVER: case DHCP_OPT_LOG_SERVER: case DHCP_OPT_COOKIE_SERVER: case DHCP_OPT_LPR_SERVER: case DHCP_OPT_IMPRESS_SERVER: case DHCP_OPT_RESOURCE_LOCATION_SERVER: case DHCP_OPT_NIS_SERVER: case DHCP_OPT_NTP_SERVER: case DHCP_OPT_NETBIOS_NBNS_SERVER: case DHCP_OPT_NETBIOS_NBDD_SERVER: case DHCP_OPT_X11_FONT_SERVER: case DHCP_OPT_X11_DISPLAY_MANAGER: case DHCP_OPT_NISP_SERVER: case DHCP_OPT_MOBILE_IP_HOME_AGENT: case DHCP_OPT_SMTP_SERVER: case DHCP_OPT_POP3_SERVER: case DHCP_OPT_NNTP_SERVER: case DHCP_OPT_DEFAULT_WWW_SERVER: case DHCP_OPT_DEFAULT_FINGER_SERVER: case DHCP_OPT_DEFAULT_IRC_SERVER: case DHCP_OPT_STREETTALK_SERVER: case DHCP_OPT_STDA_SERVER: error = dhcpDumpIpv4AddrList(option); break; //Raw data? default: error = dhcpDumpRawData(option); break; } //Failed to parse current option? if(error) { //Report a warning TRACE_WARNING("Failed to parse DHCP options!\r\n"); //Dump message contents for debugging purpose TRACE_DEBUG_ARRAY(" ", message, length); } //Jump to the next option i += option->length + 1; } //No error to report return NO_ERROR; }
error_t rsaesPkcs1v15Decrypt(const RsaPrivateKey *key, const uint8_t *ciphertext, size_t ciphertextLength, uint8_t *message, size_t messageSize, size_t *messageLength) { error_t error; uint_t i; uint_t k; uint8_t *em; Mpi c; Mpi m; //Check parameters if(key == NULL || ciphertext == NULL) return ERROR_INVALID_PARAMETER; if(message == NULL || messageLength == NULL) return ERROR_INVALID_PARAMETER; //Debug message TRACE_DEBUG("RSA PKCS #1 v1.5 decryption...\r\n"); TRACE_DEBUG(" Modulus:\r\n"); TRACE_DEBUG_MPI(" ", &key->n); TRACE_DEBUG(" Public exponent:\r\n"); TRACE_DEBUG_MPI(" ", &key->e); TRACE_DEBUG(" Private exponent:\r\n"); TRACE_DEBUG_MPI(" ", &key->d); TRACE_DEBUG(" Prime 1:\r\n"); TRACE_DEBUG_MPI(" ", &key->p); TRACE_DEBUG(" Prime 2:\r\n"); TRACE_DEBUG_MPI(" ", &key->q); TRACE_DEBUG(" Prime exponent 1:\r\n"); TRACE_DEBUG_MPI(" ", &key->dp); TRACE_DEBUG(" Prime exponent 2:\r\n"); TRACE_DEBUG_MPI(" ", &key->dq); TRACE_DEBUG(" Coefficient:\r\n"); TRACE_DEBUG_MPI(" ", &key->qinv); TRACE_DEBUG(" Ciphertext:\r\n"); TRACE_DEBUG_ARRAY(" ", ciphertext, ciphertextLength); //Initialize multiple-precision integers mpiInit(&c); mpiInit(&m); //Get the length in octets of the modulus n k = mpiGetByteLength(&key->n); //Check the length of the ciphertext if(ciphertextLength != k || ciphertextLength < 11) return ERROR_INVALID_LENGTH; //Allocate a buffer to store the encoded message EM em = osAllocMem(k); //Failed to allocate memory? if(!em) return ERROR_OUT_OF_MEMORY; //Start of exception handling block do { //Convert the ciphertext to an integer ciphertext representative c error = mpiReadRaw(&c, ciphertext, ciphertextLength); //Conversion failed? if(error) break; //Apply the RSADP decryption primitive error = rsadp(key, &c, &m); //Any error to report? if(error) break; //Convert the message representative m to an encoded message EM of length k octets error = mpiWriteRaw(&m, em, k); //Conversion failed? if(error) break; //Debug message TRACE_DEBUG(" Encoded message\r\n"); TRACE_DEBUG_ARRAY(" ", em, k); //The first octet of EM must have a value of 0x00 //and the block type BT shall be 0x02 if(em[0] != 0x00 || em[1] != 0x02) { //Report an error error = ERROR_UNEXPECTED_VALUE; break; } //An octet with hexadecimal value 0x00 is used to separate PS from M for(i = 2; i < k && em[i] != 0x00; i++); //Check whether the padding string is valid if(i < 10 || i >= k) { //Report an error error = ERROR_INVALID_PADDING; break; } //Ensure that the output buffer is large enough if(messageSize < (k - i - 1)) { //Report an error error = ERROR_INVALID_LENGTH; break; } //Recover the length of the message *messageLength = k - i - 1; //Copy the message contents memcpy(message, em + i + 1, *messageLength); //Debug message TRACE_DEBUG(" Message:\r\n"); TRACE_DEBUG_ARRAY(" ", message, *messageLength); //End of exception handling block } while(0); //Release multiple precision integers mpiFree(&c); mpiFree(&m); //Free previously allocated memory osFreeMem(em); //Return status code return error; }
error_t tlsSendCertificate(TlsContext *context) { error_t error; bool_t sendCert; uint8_t *p; size_t length; const char_t *pemCert; size_t pemCertLength; uint8_t *derCert; size_t derCertSize; size_t derCertLength; TlsCertificate *message; //TLS operates as a client or a server? if(context->entity == TLS_CONNECTION_END_CLIENT) { //The client must send a Certificate message if the server requests it sendCert = context->clientCertRequested ? TRUE : FALSE; #if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_MIN_VERSION <= SSL_VERSION_3_0) //SSL 3.0 currently selected? if(context->version == SSL_VERSION_3_0) { //The server requests a certificate but no suitable certificate is available? if(sendCert && context->cert == NULL) { //The client should send a no_certificate alert instead error = tlsSendAlert(context, TLS_ALERT_LEVEL_WARNING, TLS_ALERT_NO_CERTIFICATE); //Any error to report? if(error) return error; //Skip Certificate message... sendCert = FALSE; } } #endif } else { //The server must send a Certificate message whenever the agreed-upon //key exchange method uses certificates for authentication sendCert = (context->cert != NULL) ? TRUE : FALSE; } //Check whether a Certificate message must be sent if(sendCert) { //Buffer where to format the message p = context->txBuffer + sizeof(TlsRecord); //Point to the Certificate message message = (TlsCertificate *) p; //Format message header 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) break; //Total length of the certificate list length += derCertLength + 3; //Prevent the buffer from overflowing if((length + sizeof(TlsCertificate)) > TLS_MAX_PROTOCOL_DATA_LENGTH) return ERROR_MESSAGE_TOO_LONG; //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 osFreeMem(derCert); //A 3-byte length field shall precede the certificate list STORE24BE(length, message->certificateListLength); //Consider the 3-byte length field length += 3; //Fix message header STORE24BE(length, message->length); //Length of the complete handshake message length += sizeof(TlsHandshake); //Debug message TRACE_INFO("Sending Certificate message (%" PRIuSIZE " bytes)...\r\n", length); TRACE_DEBUG_ARRAY(" ", message, length); //Send handshake message error = tlsWriteProtocolData(context, length, TLS_TYPE_HANDSHAKE); //Failed to send TLS record? if(error) return error; } //Update FSM state context->state = (context->entity == TLS_CONNECTION_END_CLIENT) ? TLS_STATE_CLIENT_KEY_EXCHANGE : TLS_STATE_SERVER_KEY_EXCHANGE; //Successful processing return NO_ERROR; }
error_t rsassaPkcs1v15Verify(const RsaPublicKey *key, const HashAlgo *hash, const uint8_t *digest, const uint8_t *signature, size_t signatureLength) { error_t error; uint_t k; uint8_t *em; const uint8_t *oid; size_t oidLength; const uint8_t *d; size_t dLength; Mpi s; Mpi m; //Check parameters if(key == NULL || hash == NULL || digest == NULL || signature == NULL) return ERROR_INVALID_PARAMETER; //Debug message TRACE_DEBUG("RSA PKCS #1 v1.5 signature verification...\r\n"); TRACE_DEBUG(" Modulus:\r\n"); TRACE_DEBUG_MPI(" ", &key->n); TRACE_DEBUG(" Public exponent:\r\n"); TRACE_DEBUG_MPI(" ", &key->e); TRACE_DEBUG(" Message digest:\r\n"); TRACE_DEBUG_ARRAY(" ", digest, hash->digestSize); TRACE_DEBUG(" Signature:\r\n"); TRACE_DEBUG_ARRAY(" ", signature, signatureLength); //Initialize multiple-precision integers mpiInit(&s); mpiInit(&m); //Get the length in octets of the modulus n k = mpiGetByteLength(&key->n); //Check the length of the signature if(signatureLength != k) return ERROR_INVALID_LENGTH; //Allocate a memory buffer to hold the encoded message em = osAllocMem(k); //Failed to allocate memory? if(!em) return ERROR_OUT_OF_MEMORY; //Start of exception handling block do { //Convert the signature to an integer signature representative s error = mpiReadRaw(&s, signature, signatureLength); //Conversion failed? if(error) break; //Apply the RSAVP1 verification primitive error = rsavp1(key, &s, &m); //Any error to report? if(error) break; //Convert the message representative m to an encoded message EM of length k octets error = mpiWriteRaw(&m, em, k); //Conversion failed? if(error) break; //Debug message TRACE_DEBUG(" Encoded message\r\n"); TRACE_DEBUG_ARRAY(" ", em, k); //Parse the encoded message EM error = emsaPkcs1v15Decode(em, k, &oid, &oidLength, &d, &dLength); //Any error to report? if(error) break; //Assume an error... error = ERROR_INVALID_SIGNATURE_ALGO; //Ensure the hash algorithm identifier matches the OID if(oidComp(oid, oidLength, hash->oid, hash->oidSize)) break; //Check the length of the digest if(dLength != hash->digestSize) break; //Compare the message digest error = memcmp(digest, d, dLength) ? ERROR_INVALID_SIGNATURE : NO_ERROR; //End of exception handling block } while(0); //Release multiple precision integers mpiFree(&s); mpiFree(&m); //Free previously allocated memory osFreeMem(em); //Return status code return error; }
error_t dhcpv6DumpOptions(const uint8_t *options, size_t length, uint_t level) { error_t error; uint_t i; Dhcpv6Option *option; //Check whether the maximum level of recursion is reached if(level >= 6) { //If the maximum level of recursion is reached, then dump contents TRACE_DEBUG(prefix[level], "Options (% bytes)\r\n", length); TRACE_DEBUG_ARRAY(prefix[level + 1], options, length); //Exit immediately return NO_ERROR; } //Parse DHCPv6 options for(i = 0; i < length; ) { //Point to the current option option = (Dhcpv6Option *) (options + i); //Make sure the option is valid if((i + sizeof(Dhcpv6Option)) > length) return ERROR_INVALID_OPTION; //Check the length of the option data if((i + sizeof(Dhcpv6Option) + ntohs(option->length)) > length) return ERROR_INVALID_OPTION; //Check option code switch(ntohs(option->code)) { //Client Identifier option case DHCPV6_OPTION_CLIENTID: error = dhcpv6DumpClientIdOption(option, level); break; //Server Identifier option case DHCPV6_OPTION_SERVERID: error = dhcpv6DumpServerIdOption(option, level); break; //IA_NA option case DHCPV6_OPTION_IA_NA: error = dhcpv6DumpIaNaOption(option, level); break; //IA_TA option case DHCPV6_OPTION_IA_TA: error = dhcpv6DumpIaTaOption(option, level); break; //IA Address option case DHCPV6_OPTION_IAADDR: error = dhcpv6DumpIaAddrOption(option, level); break; //Option Request option case DHCPV6_OPTION_ORO: error = dhcpv6DumpOroOption(option, level); break; //Preference option case DHCPV6_OPTION_PREFERENCE: error = dhcpv6DumpPreferenceOption(option, level); break; //Elapsed Time option case DHCPV6_OPTION_ELAPSED_TIME: error = dhcpv6DumpElapsedTimeOption(option, level); break; //Relay Message option case DHCPV6_OPTION_RELAY_MSG: error = dhcpv6DumpRelayMessageOption(option, level); break; //Authentication option case DHCPV6_OPTION_AUTH: error = dhcpv6DumpAuthOption(option, level); break; //Server Unicast option case DHCPV6_OPTION_UNICAST: error = dhcpv6DumpServerUnicastOption(option, level); break; //Status Code option case DHCPV6_OPTION_STATUS_CODE: error = dhcpv6DumpStatusCodeOption(option, level); break; //Rapid Commit option case DHCPV6_OPTION_RAPID_COMMIT: error = dhcpv6DumpRapidCommitOption(option, level); break; //User Class option case DHCPV6_OPTION_USER_CLASS: error = dhcpv6DumpUserClassOption(option, level); break; //Vendor Class option case DHCPV6_OPTION_VENDOR_CLASS: error = dhcpv6DumpVendorClassOption(option, level); break; //Vendor Specific Information option case DHCPV6_OPTION_VENDOR_OPTS: error = dhcpv6DumpVendorSpecificInfoOption(option, level); break; //Interface ID option case DHCPV6_OPTION_INTERFACE_ID: error = dhcpv6DumpInterfaceIdOption(option, level); break; //Reconfigure Message option case DHCPV6_OPTION_RECONF_MSG: error = dhcpv6DumpReconfMessageOption(option, level); break; //Reconfigure Accept option case DHCPV6_OPTION_RECONF_ACCEPT: error = dhcpv6DumpReconfAcceptOption(option, level); break; //DNS Recursive Name Server option case DHCPV6_OPTION_DNS_SERVERS: error = dhcpv6DumpDnsServersOption(option, level); break; //Domain Search List option case DHCPV6_OPTION_DOMAIN_LIST: error = dhcpv6DumpDomainListOption(option, level); break; //Unknown option... default: error = dhcpv6DumpGenericOption(option, level); break; } //Failed to parse current option? if(error) return error; //Jump to the next option i += sizeof(Dhcpv6Option) + ntohs(option->length); } //No error to report return NO_ERROR; }