Пример #1
0
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;
}
Пример #2
0
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;
}
Пример #3
0
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(&params->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(&params->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("    ", &params->p);
      TRACE_DEBUG("  Generator:\r\n");
      TRACE_DEBUG_MPI("    ", &params->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;
}
Пример #4
0
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;
}