error_t pemReadCertificate(const char_t **input, size_t *inputLength, uint8_t **output, size_t *outputSize, size_t *outputLength) { error_t error; size_t length; size_t i; size_t j; int_t k; //Check parameters if(input == NULL || inputLength == NULL) return ERROR_INVALID_PARAMETER; if(output == NULL || outputSize == NULL || outputLength == NULL) return ERROR_INVALID_PARAMETER; //Search for the beginning tag k = pemSearchTag(*input, *inputLength, "-----BEGIN CERTIFICATE-----", 27); //Failed to find the specified tag? if(k < 0) return ERROR_END_OF_FILE; //Advance the input pointer over the tag *input += k + 27; *inputLength -= k + 27; //Search for the end tag k = pemSearchTag(*input, *inputLength, "-----END CERTIFICATE-----", 25); //Invalid PEM file? if(k <= 0) return ERROR_INVALID_SYNTAX; //Length of the PEM structure length = k; //Increase buffer size? if(length > *outputSize) { //Release previously allocated buffer if necessary if(*output != NULL) { osMemFree(*output); *output = NULL; *outputSize = 0; } //Allocate a memory buffer to hold the decoded data *output = osMemAlloc(length); //Failed to allocate memory? if(*output == NULL) return ERROR_OUT_OF_MEMORY; //Record the size of the buffer *outputSize = length; } //Copy the contents of the PEM structure memcpy(*output, *input, length); //Advance the input pointer over the certificate *input += length + 25; *inputLength -= length + 25; //Remove carriage returns and line feeds for(i = 0, j = 0; i < length; i++) { if((*output)[i] != '\r' && (*output)[i] != '\n') (*output)[j++] = (*output)[i]; } //Start of exception handling block do { //The PEM file is Base64 encoded... error = base64Decode((char_t *) *output, j, *output, &length); //Failed to decode the file? if(error) break; //Display ASN.1 structure error = asn1DumpObject(*output, length, 0); //Any error to report? if(error) break; //End of exception handling block } while(0); //Clean up side effects if(error) { //Release previously allocated memory osMemFree(*output); *output = NULL; *outputSize = 0; } //Size of the decoded certificate *outputLength = length; //Return status code return 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 pemReadDhParameters(const char_t *input, size_t length, DhParameters *params) { error_t error; size_t i; size_t j; int_t k; char_t *buffer; const uint8_t *data; Asn1Tag tag; //Check parameters if(input == NULL && length != 0) return ERROR_INVALID_PARAMETER; if(params == NULL) return ERROR_INVALID_PARAMETER; //Search for the beginning tag k = pemSearchTag(input, length, "-----BEGIN DH PARAMETERS-----", 29); //Failed to find the specified tag? if(k < 0) return ERROR_INVALID_SYNTAX; //Advance the pointer over the tag input += k + 29; length -= k + 29; //Search for the end tag k = pemSearchTag(input, length, "-----END DH PARAMETERS-----", 27); //Invalid PEM file? if(k <= 0) return ERROR_INVALID_SYNTAX; //Length of the PEM structure length = k; //Allocate a memory buffer to hold the decoded data buffer = osMemAlloc(length); //Failed to allocate memory? if(!buffer) return ERROR_OUT_OF_MEMORY; //Copy the contents of the PEM structure memcpy(buffer, input, length); //Remove carriage returns and line feeds for(i = 0, j = 0; i < length; i++) { if(buffer[i] != '\r' && buffer[i] != '\n') buffer[j++] = buffer[i]; } //Start of exception handling block do { //The PEM file is Base64 encoded... error = base64Decode(buffer, j, buffer, &length); //Failed to decode the file? if(error) break; //Point to the resulting ASN.1 structure data = (uint8_t *) buffer; //Display ASN.1 structure error = asn1DumpObject(data, length, 0); //Any error to report? if(error) break; //The Diffie-Hellman parameters are encapsulated within a sequence error = asn1ReadTag(data, length, &tag); //Failed to decode ASN.1 tag? if(error) break; //Enforce encoding, type and class error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE); //The tag does not match the criteria? if(error) break; //Point to the first field of the sequence data = tag.value; length = tag.length; //Read the prime modulus error = asn1ReadTag(data, length, &tag); //Failed to decode ASN.1 tag? if(error) break; //Enforce encoding, type and class error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER); //The tag does not match the criteria? if(error) break; //Convert the prime modulus to a multiple precision integer error = mpiReadRaw(¶ms->p, tag.value, tag.length); //Any error to report? if(error) break; //Point to the next field data += tag.totalLength; length -= tag.totalLength; //Read the generator error = asn1ReadTag(data, length, &tag); //Failed to decode ASN.1 tag? if(error) break; //Enforce encoding, type and class error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER); //The tag does not match the criteria? if(error) break; //Convert the generator to a multiple precision integer error = mpiReadRaw(¶ms->g, tag.value, tag.length); //Any error to report? if(error) break; //Debug message TRACE_DEBUG("Diffie-Hellman parameters:\r\n"); TRACE_DEBUG(" Prime modulus:\r\n"); TRACE_DEBUG_MPI(" ", ¶ms->p); TRACE_DEBUG(" Generator:\r\n"); TRACE_DEBUG_MPI(" ", ¶ms->g); //End of exception handling block } while(0); //Release previously allocated memory osMemFree(buffer); //Clean up side effects if necessary if(error) dhFreeParameters(params); //Return status code return 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; }