/* * Copyright (C) 2011-2018 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "X509Cert.h" #include "sgx_lfence.h" #include #include #define X509_FOR_PSE_PR 1 #ifdef X509_FOR_PSE_PR #include "pse_pr_support.h" // used to eliminate `unused variable' warning #define UNUSED(val) (void)(val) #endif #ifndef X509_FOR_PSE_PR #include "MeTypes.h" #include "SessMgrCommonDefs.h" #include "le2be_macros.h" #include "CryptoDefs.h" #include "romapi/romapi_rsa.h" #include "TimeSrv.h" #endif // #ifndef X509_FOR_PSE_PR #ifdef X509_FOR_PSE_PR STATUS CreateSha1Hash ( /*in */ SessMgrDataBuffer *pSrcBuffer, /*out*/ SessMgrDataBuffer *pDigest ) { PrepareHashSHA1 hash; hash.Update(pSrcBuffer->buffer, pSrcBuffer->length); if (!hash.Finalize((SHA1_HASH*)pDigest->buffer)) return X509_GENERAL_ERROR; return STATUS_SUCCESS; }; crypto_status_t EcDsa_VerifySignature ( /*in */ const UINT8* pMsg, /*in */ uint32_t nMsg, /*in */ const EcDsaPubKey* pPublicKey, /*in */ const EcDsaSig* pSignature, /*out*/ bool* fValid ) { crypto_status_t status = CRYPTO_STATUS_INTERNAL_ERROR; sgx_ecc_state_handle_t ecc_handle = NULL; *fValid = false; do { if (SGX_SUCCESS != sgx_ecc256_open_context(&ecc_handle)) break; uint8_t result; if ((SGX_SUCCESS == sgx_ecdsa_verify(pMsg, nMsg, (sgx_ec256_public_t *)pPublicKey, (sgx_ec256_signature_t *)pSignature, &result, ecc_handle)) && (result == SGX_EC_VALID )) *fValid = true; status = CRYPTO_STATUS_SUCCESS; } while (0); if (ecc_handle != NULL) sgx_ecc256_close_context(ecc_handle); return status; } #endif //******************************************************************************************************************************************* //******************************************************************************************************************************************* //******************************************************************************************************************************************* static STATUS VerifyBasicCertificateAttributes(const Uint8* certificateDerEncoded, const Uint8* workBuffer, const SessMgrCertificateFields* certificateFields, const ISSUER_INFO *IssuerInfo, CertificateType CertType, CertificateLevel CertLevel , BOOL UseFacsimileEpid); #ifndef X509_FOR_PSE_PR static STATUS VerifyOcspRevocationStatus(SessMgrCertificateFields* certificateFields, UINT8 NumberofSingleResponses, OCSP_CERT_STATUS_TABLE * OcspCertStatusTable, ISSUER_INFO *IssuerInfo); #endif static STATUS VerifySignature(const ISSUER_INFO *IssuerInfo, const SessMgrDataBuffer *MsgBuffer, const SessMgrDataBuffer *SignBuffer, BOOL UseFacsimileEpid); #ifndef X509_FOR_PSE_PR static void GetPublicKeyDataBuf(SessMgrDataBuffer *PubKeyBuffer, ISSUER_INFO *IssuerInfo); static STATUS VerifyOcspCachedResponseValidity(SessMgrOcspResponseFields *OcspResponseFields); static STATUS VerifyValidity(SessMgrDateTime notValidBeforeTime, SessMgrDateTime NotValidAfter); static STATUS ConvertTimeToNtp(SessMgrDateTime Time, NTP_TIMESTAMP *NtpTime); static STATUS StoreTrustedTime(SessMgrDateTime TrustedTime); #endif #ifndef X509_FOR_PSE_PR static STATUS VerifyOcspResponseAttributes(Uint8* OcspRespBuffer, SessMgrOcspResponseFields *ocspResponseFields, ISSUER_INFO* OcspCertRootPublicKey, SessMgrDataBuffer Nonce, OCSP_REQ_TYPE OcspReqType, BOOL UseFacsimileEpid); static BOOL VerifySha1Hash(SessMgrDataBuffer *HashData, UINT8 *Expectedhash, UINT32 ExpectedHashLength); #endif static STATUS sessMgrParseDerCert(IN X509_PROTOCOL* X509Protocol, IN Uint8* certificateDerEncoded, IN Uint8* pCertEnd, IN Uint8* workBuffer, IN UINT32 workBufferSize, OUT SessMgrCertificateFields* certificateFields, IN ISSUER_INFO *IssuerInfo, IN BOOL UseFacsimileEpid); #ifndef X509_FOR_PSE_PR static STATUS sessMgrParseOcspResponse(IN X509_PROTOCOL* X509Protocol, IN Uint8* OcspResponse, IN Uint8* OcspResponseEnd, IN Uint8* workBuffer, IN UINT32 workBufferSize, OUT SessMgrOcspResponseFields* OcspResponseFields); static STATUS ParseBoolean(UINT8 **ppCurrent, UINT8 *pEnd, BOOL* Value, BOOL optional); #endif static STATUS ParseInteger(UINT8 **ppCurrent, UINT8 *pEnd, SessMgrDataBuffer* DataBuf, BOOL isOptional, BOOL MustBePositive, UINT32 *PaddingLen); #ifndef X509_FOR_PSE_PR static STATUS ParseOcspExtensions(UINT8 **ppCurrent, UINT8 *pEnd, SessMgrOcspResponseFields* OcspResponseFields); static STATUS ParseCertExtensions(UINT8 **ppCurrent, UINT8 *pEnd, SessMgrCertificateFields* certificateFields); static STATUS ParseCertificatePolicy(UINT8 **ppCurrent, UINT8 *pEnd, SessMgrDataBuffer *CertificatePolicy); #endif static STATUS ParseSubjectPublicKeyInfo(UINT8 **ppCurrent, UINT8 *pEnd, UINT8 **pworkbuffer, SessMgrCertificateFields* certificateFields); static STATUS ParseRsaPublicKey(UINT8 **ppCurrent, UINT8 *pEnd, SessMgrRsaKey * RsaKey); static STATUS ParseEpidPublicKey(UINT8 **ppCurrent, UINT8 *pEnd, SessMgrEpidGroupPublicKey * EpidKey); static STATUS ParseEcdsaPublicKey(UINT8 **ppCurrent, UINT8 *pEnd, SessMgrEcdsaPublicKey * EcDsaKey, SessMgrEllipticCurveParameter params); static STATUS ParseOID(UINT8 **ppCurrent, UINT8 *pEnd, UINT32 *EnumVal, const UINT8 *OidList, UINT32 Max_Entries, UINT32 EntrySize ); static STATUS ParseSignatureValue(UINT8 **ppCurrent, UINT8 *pEnd, UINT8 **pworkbuffer, UINT32 WorkBufferSize, SessMgrDataBuffer *SignatureValueBuf, UINT8 SignatureAlgoId); static STATUS ParseAlgoIdentifier(UINT8 **ppCurrent, UINT8 *pEnd, UINT32* algoId, AlgorithmTypes Type, SessMgrEllipticCurveParameter *params); static STATUS ParseAlgoParameters(UINT8 **ppCurrent, UINT8 *pEnd, UINT32* param); static STATUS ParseName(UINT8 **ppCurrent, UINT8 *pEnd, SessMgrX509Name* Name); static STATUS ParseTime(UINT8 **ppCurrent, UINT8 *pEnd, SessMgrDateTime* DateTime); static STATUS DecodeLength(UINT8* Buffer, UINT8* BufferEnd, UINT32* Length, UINT8* EncodingBytes); static void SwapEndian(UINT8* ptr, int length); static STATUS swapendian_memcpy(UINT8 *DestPtr, UINT32 DestLen, UINT8 *SrcPtr, UINT32 SrcLen); static STATUS ParseIdAndLength(UINT8 **ppCurrent, UINT8 *pEnd, UINT8 ExpectedId, UINT32* Length, UINT8* EncodingBytes, BOOL Optional); #ifndef X509_FOR_PSE_PR static int Pow(int num, int exp); #endif /* This list should always be synced up with the SessMgrAlgorithmOid enum */ const UINT8 HardCodedSignatureAlgorithmOid[][9] = { {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x02}, {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x03}, {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04}, {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05}, {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x07}, {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x08}, {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x09}, {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0a}, {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0b}, {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0c}, {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0d}, {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0e}, {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x01}, {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02}, }; const UINT8 HardCodedPublicKeyAlgorithmOid[][10] = { {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01}, {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01}, {0x2A, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x09, 0x04, 0x01}, {0x2A, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x09, 0x04, 0x02}, {0x2A, 0x86, 0x48, 0x86, 0xf8, 0x4d, 0x01, 0x09, 0x04, 0x03}, }; const UINT8 HashAlgorithmOid[][9] = { {0x2B, 0x0E, 0x03, 0x02, 0x1A}, {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01}, }; /* This list should always be synced up with the NameStruct enum */ const UINT8 HardCodedNameOid[][10] = { {0x55, 0x04, 0x03}, {0x55, 0x04, 0x0a}, {0x55, 0x04, 0x06}, {0x55, 0x04, 0x07}, {0x55, 0x04, 0x08}, {0x55, 0x04, 0x0b}, {0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x01}, }; #ifndef X509_FOR_PSE_PR const UINT8 CertExtensionOid[][9] = { {0x55, 0x1d, 0x23}, {0x55, 0x1d, 0x0E}, {0x55, 0x1d, 0x0F}, {0x55, 0x1d, 0x13}, {0x55, 0x1d, 0x20}, {0x55, 0x1d, 0x25}, {0x2A, 0x86, 0x48, 0x86, 0xF8, 0x4D, 0x01, 0x09, 0x02}, }; const UINT8 OcspExtensionOid[][9] = { {0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x02}, // 1.3.6.1.5.5.7.48.1.2 }; #endif const UINT8 EllipticCurveOid[][8] = { {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07} }; #ifndef X509_FOR_PSE_PR const UINT8 CertificatePolicyOid[][9] = { {0x2A, 0x86, 0x48, 0x86, 0xF8, 0x4d, 0x01, 0x09, 0x01} }; const UINT8 CertificatePolicyQualifierIdOid[][8] = { {0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01} }; const UINT8 OcspResponseTypeOid[][9] = { {0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01} }; const UINT8 ExtendedKeyUsageOcspSignOid[][8] = { 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09 }; #endif #ifndef X509_FOR_PSE_PR /* ParseOcspResponseChain - Decodes a DER encoded X.509 OCSP response and makes a list of the serial numbers and hashes from the OCSP response. @param OcspRespBuffer - If not NULL, contains the OCSP response. OCSP response contains a list of certificates with their current status (good/revoked) @param OcspRespBufferLength - Total Length of the OCSP response @param OcspCertRootPublicKey - Public key used to sign the first certificate in the chain. This is the root of trust. If NULL, Intel public key is used. @param OcspCertStatusTable - Table containing interesting fields in the OCSP response which will be used to compare against the verifier certificate. @param NumberOfSingleResponses - Number of single responses that the OCSP response has returned to us. @param Nonce - Contains the Nonce that was sent. If nonce Exists, the OCSP response should have a nonce extension that contains the same values. @retval X509_STATUS_SUCCESS - The operation completed successfully. @retval STATUS_INVALID_VERSION @retval STATUS_UNSUPPORTED_ALGORITHM @retval STATUS_ENCODING_ERROR @retval STATUS_INVALID_ARGS @retval STATUS_UNSUPPORTED_CRITICAL_EXTENSION @retval STATUS_UNSUPPORTED_TYPE */ STATUS ParseOcspResponseChain( UINT8* OcspRespBuffer, UINT32 OcspRespBufferLength, UINT8* workBuffer, UINT32 workBufferSize, ISSUER_INFO* OcspCertRootPublicKey, OCSP_CERT_STATUS_TABLE *OcspCertStatusTable, UINT8* NumberOfSingleResponses, SessMgrDataBuffer Nonce, OCSP_REQ_TYPE OcspReqType, BOOL UseFacsimileEpid) { STATUS Status; UINT32 Length = 0; UINT32 OcspResponseLength = 0; UINT8 TableIndex = 0; SessMgrOcspSingleResponse *SingleResponse; int i; SessMgrOcspResponseFields OcspResponseFields; UINT8 EncodingBytes; // number of bytes used for encoding into ASN DER format UINT8 *current_ptr = OcspRespBuffer; UINT8 *end_of_ocsp_response_chain = OcspRespBuffer + OcspRespBufferLength; // Workaround for Windows OCSP responder shortcoming. Loop through OCSP response // We will loop through each of the OCSP responses, verify the OCSP responder certificate and while(current_ptr < end_of_ocsp_response_chain){ Status = DecodeLength(current_ptr + 1, end_of_ocsp_response_chain, &Length, &EncodingBytes); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } OcspResponseLength = Length + EncodingBytes + 1; memset(workBuffer, 0, workBufferSize); memset(&OcspResponseFields, 0, sizeof(OcspResponseFields)); Status = sessMgrParseOcspResponse( NULL, current_ptr, current_ptr + OcspResponseLength, workBuffer, workBufferSize, &OcspResponseFields); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } Status = VerifyOcspResponseAttributes(NULL, &OcspResponseFields, NULL, Nonce, OcspReqType, UseFacsimileEpid); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); return X509_STATUS_OCSP_VERIFICATION_FAILED; } // copy the interesting data for(i=0;ithisUpdate, SingleResponse->nextUpdate); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); return X509_STATUS_OCSP_VERIFICATION_FAILED; } if(SingleResponse->ocspCertificateStatus == good){ if(TableIndex == MAX_CERT_CHAIN_LENGTH){ DBG_ASSERT(0); return X509_STATUS_OCSP_VERIFICATION_FAILED; } SESSMGR_MEMCPY_S(OcspCertStatusTable[TableIndex].serialNumber, sizeof(OcspCertStatusTable[TableIndex].serialNumber), SingleResponse->serialNumber.buffer, SingleResponse->serialNumber.length); DBG_ASSERT(SingleResponse->serialNumber.length <= 255); OcspCertStatusTable[TableIndex].SerialNumberSize = (UINT8)SingleResponse->serialNumber.length; SESSMGR_MEMCPY_S(OcspCertStatusTable[TableIndex].issuerKeyHash, sizeof(OcspCertStatusTable[TableIndex].issuerKeyHash), SingleResponse->issuerKeyHash.buffer, SingleResponse->issuerKeyHash.length); DBG_ASSERT(SingleResponse->issuerKeyHash.length <= 255); OcspCertStatusTable[TableIndex].issuerKeyHashSize = (UINT8)SingleResponse->issuerKeyHash.length; SESSMGR_MEMCPY_S(OcspCertStatusTable[TableIndex].issuerNameHash, sizeof(OcspCertStatusTable[TableIndex].issuerNameHash), SingleResponse->issuerNameHash.buffer, SingleResponse->issuerNameHash.length); DBG_ASSERT(SingleResponse->issuerNameHash.length <= 255); OcspCertStatusTable[TableIndex].issuerNameHashSize = (UINT8)SingleResponse->issuerNameHash.length; OcspCertStatusTable[TableIndex].HashAlgo = SingleResponse->issuerIdentifierHashType; TableIndex++; } } current_ptr += OcspResponseLength; } if (current_ptr != end_of_ocsp_response_chain){ DBG_ASSERT(0); return X509_STATUS_INVALID_ARGS; } *NumberOfSingleResponses = TableIndex; return STATUS_SUCCESS; } #endif /* ParseCertificateChain - This function can - parse a certificate chain and return the CertificateFields of all the last certificate (usually the certificate of interest) - optionally take in root public key that was used to sign first certificate in the chain. If NULL, Intel public Key is used. - optionally take in ocspRespBuffer. If ocspRespBuffer is not NULL, this function will parse the ocsp response cert, make a list of certificates authenticated by OCSP responder, and use this list to verify if each certificate in the chain has been authenticated. - optionally takes in the root public key used to sign the first certifcate in the OCSP responders certificate. @param pCertChain - Pointer to the certificate chain. The first certificate in the chain is assumed to be Intel signed if root public key (arg 4) is NULL @param CertChainLength - Length of the certificate chain @param certificateFields - Data structure containing parsed output of the last certificate in the chain (usually the certificate of interest)) @param RootPublicKey - Public key used to sign the first certificate in the chain. This is the root of trust. If NULL, Intel public key is used. @param NumberOfSingleResponses - Number of single responses that the OCSP response has returned to us. @param OcspCertStatusTable - Table containing interesting fields in the OCSP response which will be used to compare against the verifier certificate. @param VerifierCert - Contains pointer and length of the VerifierCertificate. If non NULL, the final certificate in the chain is populated with this Data. @param CertType - Indicates what type of certificate we are processing.Some checks are specific to certain types of certs. @retval X509_STATUS_SUCCESS - The operation completed successfully. */ /* Input: pointer to buffer containing a chain of certificates Total Length Assumes the first certificate in the chain is signed by Intel */ STATUS ParseCertificateChain(UINT8 *pCertChain, UINT32 CertChainLength, SessMgrCertificateFields *certificateFields, UINT8 *CertWorkBuffer, UINT32 CertWorkBufferLength, ISSUER_INFO *RootPublicKey, UINT8 NumberOfSingleResponses, OCSP_CERT_STATUS_TABLE *OcspCertStatusTable, CertificateType CertType, BOOL UseFacsimileEpid ) { STATUS Status; UINT8 CertCount = 0; SessMgrEcdsaPublicKey ecdsa_pub_key; // This is the temp buffer used to store the issuer signing key to verify the next certificate in the chain. // This size of this buffer should be equal to the Max possible key size UINT8 TempSignKeyBuffer[200]; SessMgrDataBuffer TempDataBuffer; UINT8 *pCert; UINT8 *pCertChainEnd; UINT32 CertLength = 0; UINT8 EncodingBytes; // number of bytes used for encoding into ASN DER format int MaxChainLengthAllowed = 0xFF; // This is to enforce the PathLen Basic Constraints. UINT8 HashOut[SHA1_HASH_LEN] = {0}; UINT8 *KeyBufPtr; CertificateLevel CertLevel; ISSUER_INFO IssuerInfo; #ifdef X509_FOR_PSE_PR UNUSED(NumberOfSingleResponses); #endif if (pCertChain == NULL || pCertChain + CertChainLength <= pCertChain || CertWorkBuffer == NULL || CertWorkBuffer + CertWorkBufferLength <= CertWorkBuffer) { return X509_STATUS_INVALID_ARGS; } memset(&IssuerInfo, 0 , sizeof(ISSUER_INFO)); pCert = pCertChain; pCertChainEnd = pCertChain + CertChainLength; CertLevel = root; if(!RootPublicKey){ // always use debug keys except for EPID group certs #ifdef X509_FOR_PSE_PR ecdsa_pub_key.px = SerializedPublicKey; ecdsa_pub_key.py = SerializedPublicKey + 32; #else if(gSessmgrCtx.FuseGidZero == FALSE){ ecdsa_pub_key.px = INTEL_ECDSA_PUBKEY_PROD_BE; ecdsa_pub_key.py = INTEL_ECDSA_PUBKEY_PROD_BE + 32; }else{ ecdsa_pub_key.px = INTEL_ECDSA_PUBKEY_DBG_BE; ecdsa_pub_key.py = INTEL_ECDSA_PUBKEY_DBG_BE + 32; } #endif ecdsa_pub_key.eccParameter = curvePrime256v1; IssuerInfo.buffer = (UINT8 *)&ecdsa_pub_key; IssuerInfo.length = sizeof(ecdsa_pub_key); IssuerInfo.AlgoType = X509_ecdsa_with_SHA256; }else{ // Not allowing user to pass their own root key. DBG_ASSERT(0); return X509_STATUS_INVALID_ARGS; // memcpy(&IssuerInfo, RootPublicKey, sizeof(IssuerInfo)); } // Set this up for the hash. IssuerInfo.EncodedPublicKeyHashBuffer.buffer = HashOut; IssuerInfo.EncodedPublicKeyHashBuffer.length = SHA1_HASH_LEN; // For root key, populate the ISSUER_INFO data structure. Refer to data strucutre definition for more details. // use the temp sign key buffer for this purpose. memset(TempSignKeyBuffer, 0 , sizeof(TempSignKeyBuffer)); TempDataBuffer.buffer = TempSignKeyBuffer; KeyBufPtr = TempSignKeyBuffer; *KeyBufPtr = 0x04; KeyBufPtr++; SESSMGR_MEMCPY_S(KeyBufPtr, sizeof(TempSignKeyBuffer) - 1, ecdsa_pub_key.px, ECDSA_KEY_ELEMENT_SIZE); KeyBufPtr += ECDSA_KEY_ELEMENT_SIZE; SESSMGR_MEMCPY_S(KeyBufPtr, sizeof(TempSignKeyBuffer) - 1 - ECDSA_KEY_ELEMENT_SIZE, ecdsa_pub_key.py, ECDSA_KEY_ELEMENT_SIZE); TempDataBuffer.length = ECDSA_KEY_SIZE + 1; // +1 to reflect the addition of 0x04 in the beginning of the buffer. Status = CryptoCreateHash(CRYPTO_HASH_TYPE_SHA1, &TempDataBuffer, &IssuerInfo.EncodedPublicKeyHashBuffer, NULL, NULL, SINGLE_BLOCK); if(Status != STATUS_SUCCESS){ DBG_ASSERT(0); return X509_STATUS_INTERNAL_ERROR; } while(pCert < pCertChainEnd) { // // very, very doubtful we get here speculatively // with NULL pCert, but between that and possibility // of looping too many times, add lfence // sgx_lfence(); /* certificate always starts with a sequence followed by length at offset 1. */ CHECK_ID(*pCert, DER_ENCODING_SEQUENCE_ID); if ((pCert + 1) == pCertChainEnd) { break; } Status = DecodeLength(pCert + 1, pCertChainEnd, &CertLength, &EncodingBytes); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); return Status; } CertLength = CertLength + EncodingBytes + 1; if( (pCert + CertLength) >= pCertChainEnd){ // if this is the last certificate in the chain, it is the leaf CertLevel = leaf; } // Check Basic Constraints Compliance if(MaxChainLengthAllowed <= 0 && CertLevel != leaf){ // We have one more CA which is violating the basic constraints set by somebody. return error DBG_ASSERT(0); return X509_STATUS_BASIC_CONSTRAINTS_VIOLATION; } memset(certificateFields, 0, sizeof(SessMgrCertificateFields)); memset(CertWorkBuffer, 0, CertWorkBufferLength); certificateFields->productType = invalidProductType; Status = sessMgrParseDerCert(NULL, pCert, pCert + CertLength, CertWorkBuffer, CertWorkBufferLength, certificateFields, &IssuerInfo, UseFacsimileEpid); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); return Status; } // First Certificate is always intel signed Status = VerifyBasicCertificateAttributes(pCert, CertWorkBuffer, certificateFields, &IssuerInfo, CertType, CertLevel, UseFacsimileEpid); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); return Status; } // Verifiation is required if OCSP table exists (even if empty), make sure the certificate has not been revoked if(OcspCertStatusTable){ BOOL IntelSelfSignedRoot = false; #ifdef X509_FOR_PSE_PR if(CertLevel == root && memcmp(certificateFields->EncodedSubjectPublicKey.buffer+1, SerializedPublicKey, sizeof(SerializedPublicKey)) == 0) IntelSelfSignedRoot = true; #else if(CertLevel == root && memcmp(certificateFields->EncodedSubjectPublicKey.buffer+1, INTEL_ECDSA_PUBKEY_PROD_BE, sizeof(INTEL_ECDSA_PUBKEY_PROD_BE)) == 0) IntelSelfSignedRoot = true; else if(gSessmgrCtx.FuseGidZero && CertLevel == root && memcmp(certificateFields->EncodedSubjectPublicKey.buffer+1, INTEL_ECDSA_PUBKEY_DBG_BE, sizeof(INTEL_ECDSA_PUBKEY_DBG_BE)) == 0) IntelSelfSignedRoot = true; #endif // Skip revocation status check for Intel self-signed root certificate if(!IntelSelfSignedRoot) { #ifndef X509_FOR_PSE_PR Status = VerifyOcspRevocationStatus(certificateFields, NumberOfSingleResponses, OcspCertStatusTable, &IssuerInfo); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); return X509_STATUS_OCSP_FAILURE; } #endif } } // Certificate has been verified. Everything is good. // if this is not the leaf, store the public key and algorithm type to use in next certificate signature verification if(CertLevel != leaf){ memset(TempSignKeyBuffer, 0, sizeof(TempSignKeyBuffer)); SESSMGR_MEMCPY_S(TempSignKeyBuffer, sizeof(TempSignKeyBuffer), certificateFields->subjectPublicKey.buffer, certificateFields->subjectPublicKey.length); // Clear the issuer info structure. memset(&IssuerInfo, 0 , sizeof(ISSUER_INFO)); // This key is the issuer key for the next certificate. Populate the IssuerInfo IssuerInfo.buffer = TempSignKeyBuffer; IssuerInfo.length = certificateFields->subjectPublicKey.length; IssuerInfo.AlgoType = certificateFields->algorithmIdentifierForSignature; // Set this up for the hash. IssuerInfo.EncodedPublicKeyHashBuffer.buffer = HashOut; IssuerInfo.EncodedPublicKeyHashBuffer.length = SHA1_HASH_LEN; Status = CryptoCreateHash(CRYPTO_HASH_TYPE_SHA1, &certificateFields->EncodedSubjectPublicKey, &IssuerInfo.EncodedPublicKeyHashBuffer, NULL, NULL, SINGLE_BLOCK); if(Status != STATUS_SUCCESS){ DBG_ASSERT(0); return X509_STATUS_INTERNAL_ERROR; } // We might have zero or more intermediate certificates CertLevel = intermediate; // Record and Verify Basic Path Len Constraints. Refer to RFC for details on Basic constrains path len extensions. // If PathLen constraint set by this CA is more constrained than the one enforced by the previous CA, update MaxChainLength if(certificateFields->basicConstraint.isBasicConstraintPresent && certificateFields->basicConstraint.pathLenConstraint < (UINT32)MaxChainLengthAllowed) MaxChainLengthAllowed = certificateFields->basicConstraint.pathLenConstraint; else MaxChainLengthAllowed--; IssuerInfo.CommonNameBuf.buffer = (UINT8 *)certificateFields->subject.commonName; IssuerInfo.CommonNameBuf.length = certificateFields->subject.commonNameSize; IssuerInfo.productType = certificateFields->productType; } pCert += CertLength; CertCount++; } if (pCert != pCertChainEnd) { DBG_ASSERT(0); return X509_STATUS_INVALID_ARGS; } return X509_STATUS_SUCCESS; } #ifndef X509_FOR_PSE_PR /* This function compares the serial number of the certificate with the list of certificates that the OCSP responder sent us. If found, Make sure the status of the certificate is not revoked. */ STATUS VerifyOcspRevocationStatus(SessMgrCertificateFields* certificateFields, UINT8 NumberofSingleResponses, OCSP_CERT_STATUS_TABLE* OcspCertStatusTable, ISSUER_INFO *IssuerInfo) { UINT32 i; STATUS Status; STATUS VerificationStatus = X509_STATUS_OCSP_VERIFICATION_FAILED; SessMgrDataBuffer HashBuf; SessMgrDataBuffer IssuerNameBuf; UINT8 HashOut[SHA1_HASH_LEN] = {0}; HashBuf.buffer = HashOut; HashBuf.length = SHA1_HASH_LEN; IssuerNameBuf.buffer = (UINT8 *)certificateFields->issuer.DistinguishedName; IssuerNameBuf.length = certificateFields->issuer.DistinguishedNameSize; for (i=0;iserialNumber.length != OcspCertStatusTable[i].SerialNumberSize || memcmp(certificateFields->serialNumber.buffer, OcspCertStatusTable[i].serialNumber, OcspCertStatusTable[i].SerialNumberSize) != 0) { continue; } // Check hash key if(IssuerInfo->EncodedPublicKeyHashBuffer.length != OcspCertStatusTable[i].issuerKeyHashSize || memcmp(IssuerInfo->EncodedPublicKeyHashBuffer.buffer, OcspCertStatusTable[i].issuerKeyHash, OcspCertStatusTable[i].issuerKeyHashSize) != 0){ continue; } memset(HashBuf.buffer, 0, SHA1_HASH_LEN); Status = CryptoCreateHash(CRYPTO_HASH_TYPE_SHA1, &IssuerNameBuf, &HashBuf, NULL, NULL, SINGLE_BLOCK); if(Status != STATUS_SUCCESS){ DBG_ASSERT(0); return X509_STATUS_INTERNAL_ERROR; } // Check issuer name if(SHA1_HASH_LEN != OcspCertStatusTable[i].issuerNameHashSize || memcmp(HashBuf.buffer, OcspCertStatusTable[i].issuerNameHash, OcspCertStatusTable[i].issuerNameHashSize) != 0){ continue; } // The certificate has been found in the OCSP response, break and return success VerificationStatus = X509_STATUS_SUCCESS; break; } DBG_ASSERT(VerificationStatus == X509_STATUS_SUCCESS); return VerificationStatus; } #endif #ifndef X509_FOR_PSE_PR /* This function will accept the algorithm and the keys as a void pointer and will verify the keys accordingly. */ #define SESSMGR_RSA_WORK_BUFFER_SIZE (ROM_RSA_WIN_EXP_1_BUFFER_SIZE + 2*(RSA_KEY_SIZE_2048_BYTES)) UINT8 RsaWorkBuffer[SESSMGR_RSA_WORK_BUFFER_SIZE]; #endif // #ifndef X509_FOR_PSE_PR STATUS VerifySignature(const ISSUER_INFO *IssuerInfo, const SessMgrDataBuffer *MsgBuffer, const SessMgrDataBuffer *SignBuffer, BOOL UseFacsimileEpid) { #ifdef X509_FOR_PSE_PR BOOL VerifRes = FALSE; #else BOOL VerifRes = FALSE; SessMgrDataBuffer LocalSignBuffer; SessMgrDataBuffer LocalMsgBuffer; ROM_RSA_DATA_BUFFER workBuffer; ROM_RSA_VERIFY_PARAMS RsaVerifyParams; UINT8 RsaEBuffer[RSA_E_SIZE] = {0, 0 ,0 ,0}; UINT8 RsaNBuffer[RSA_KEY_SIZE_2048_BYTES] = {0}; #endif SessMgrEcdsaPublicKey *PublicKeyFromCert; PseEcdsaPublicKey EcdsaKey; G3Point* g3point; #ifndef X509_FOR_PSE_PR UINT32 hashSize = 0; BOOL IsSignatureValid = FALSE; SessMgrRsaKey *RsaKeyFromCert; #endif STATUS Status = X509_INVALID_SIGNATURE; #ifdef X509_FOR_PSE_PR UNUSED(UseFacsimileEpid); #endif do{ switch(IssuerInfo->AlgoType){ case X509_ecdsa_with_SHA1: Status = X509_STATUS_UNSUPPORTED_ALGORITHM; break; case X509_ecdsa_with_SHA256: PublicKeyFromCert = (SessMgrEcdsaPublicKey *)(IssuerInfo->buffer); SESSMGR_MEMCPY_S(EcdsaKey.px, sizeof(EcdsaKey.px), PublicKeyFromCert->px, 32); SESSMGR_MEMCPY_S(EcdsaKey.py, sizeof(EcdsaKey.py), PublicKeyFromCert->py, 32); // Allocate DWORD aligned local buffers for Signature and Msg. // Swap Key and signature Convert Intel signature of the parameters certificate prior verifying it g3point = reinterpret_cast(EcdsaKey.px); SwapEndian_32B(g3point->x); SwapEndian_32B(g3point->y); SwapEndian_32B(reinterpret_cast(SignBuffer->buffer)->x); SwapEndian_32B(reinterpret_cast(SignBuffer->buffer)->y); #ifndef X509_FOR_PSE_PR void* pCtx = NULL; pCtx = UseFacsimileEpid ? gSessmgrCtx.KeysCtxFacsimile : gSessmgrCtx.KeysCtx; #endif Status = SafeIdSigmaEcDsaVerifyPriv( pCtx, MsgBuffer->buffer, MsgBuffer->length, (unsigned char *)&EcdsaKey, (unsigned char *)SignBuffer->buffer, CRYPTO_HASH_TYPE_SHA256, 0, 32, &VerifRes); if(Status != STATUS_SUCCESS){ DBG_ASSERT(0); Status = SESSMGR_STATUS_INTERNAL_ERROR; break; } #ifndef X509_FOR_PSE_PR // Testing workaround: always allow production signed on SSKU part if(VerifRes == FALSE) { if(gSessmgrCtx.FuseGidZero) { Status = SafeIdSigmaEcDsaVerifyPriv(UseFacsimileEpid ? gSessmgrCtx.KeysCtxFacsimile : gSessmgrCtx.KeysCtx, MsgBuffer->buffer, MsgBuffer->length, INTEL_ECDSA_PUBKEY_PROD_LE, (unsigned char *)SignBuffer->buffer, CRYPTO_HASH_TYPE_SHA256, 0, 32, &VerifRes); DBG_ASSERT(Status == STATUS_SUCCESS); } if(VerifRes == FALSE) { DBG_ASSERT(0); return X509_INVALID_SIGNATURE; } } #else if(VerifRes == FALSE) { return X509_INVALID_SIGNATURE; } #endif // convert back g3point = reinterpret_cast(EcdsaKey.px); SwapEndian_32B(g3point->x); SwapEndian_32B(g3point->y); SwapEndian_32B((reinterpret_cast(SignBuffer->buffer))->x); SwapEndian_32B((reinterpret_cast(SignBuffer->buffer))->y); break; case X509_sha1withRSAEncryption: case X509_sha256WithRSAEncryption: #ifdef X509_FOR_PSE_PR Status = X509_STATUS_UNSUPPORTED_ALGORITHM; #else RsaKeyFromCert = (SessMgrRsaKey *)(IssuerInfo->buffer); DBG_ASSERT(RsaKeyFromCert->n.length == RSA_KEY_SIZE_2048_BYTES); SESSMGR_MEMCPY_S(RsaEBuffer + sizeof(RsaEBuffer) - RsaKeyFromCert->e.length, RsaKeyFromCert->e.length, RsaKeyFromCert->e.buffer, RsaKeyFromCert->e.length); RsaKeyFromCert->e.buffer = RsaEBuffer; RsaKeyFromCert->e.length = RSA_E_SIZE; SESSMGR_MEMCPY_S(RsaNBuffer + RSA_KEY_SIZE_2048_BYTES - RsaKeyFromCert->n.length, RsaKeyFromCert->n.length, RsaKeyFromCert->n.buffer, RsaKeyFromCert->n.length); RsaKeyFromCert->n.buffer = RsaNBuffer; RsaKeyFromCert->n.length = RSA_KEY_SIZE_2048_BYTES; workBuffer.length = SESSMGR_RSA_WORK_BUFFER_SIZE; workBuffer.buffer = RsaWorkBuffer; SESSMGR_MEM_ALLOC_BUFFER(LocalSignBuffer.buffer, TRUSTED_MEM, sizeof(UINT32), SignBuffer->length, TX_WAIT_FOREVER); SESSMGR_MEM_ALLOC_BUFFER(LocalMsgBuffer.buffer, TRUSTED_MEM, sizeof(UINT32), MsgBuffer->length, TX_WAIT_FOREVER); SESSMGR_MEMCPY_S(LocalSignBuffer.buffer, SignBuffer->length, SignBuffer->buffer, SignBuffer->length); LocalSignBuffer.length = SignBuffer->length; SESSMGR_MEMCPY_S(LocalMsgBuffer.buffer, MsgBuffer->length, MsgBuffer->buffer, MsgBuffer->length); LocalMsgBuffer.length = MsgBuffer->length; // Swap the Key and signature buffers. These are local buffers so no swapping back is necessary. SwapEndian(RsaKeyFromCert->e.buffer, RsaKeyFromCert->e.length); SwapEndian(RsaKeyFromCert->n.buffer, RsaKeyFromCert->n.length); SwapEndian(LocalSignBuffer.buffer, LocalSignBuffer.length); RsaVerifyParams.pMsgBuffer = (ROM_RSA_DATA_BUFFER*)&LocalMsgBuffer; RsaVerifyParams.pSignatureBuffer = (ROM_RSA_DATA_BUFFER*)&LocalSignBuffer; RsaVerifyParams.pbIsValid = &IsSignatureValid; RsaVerifyParams.CallbackAbortNow = NULL; RsaVerifyParams.pWorkBuffer = &workBuffer; RsaVerifyParams.HashFunc = (X509_sha1withRSAEncryption == IssuerInfo->AlgoType ? ROM_RSA_SCHEME_HASH_SHA1 : ROM_RSA_SCHEME_HASH_SHA256); Status = CryptoRsaPkcsVerify((RSA_IPP_KEY*)RsaKeyFromCert, FALSE, (RSA_VERIFY_PARAMS*)&RsaVerifyParams); if(LocalSignBuffer.buffer){ SESSMGR_MEM_FREE(LocalSignBuffer.buffer) LocalSignBuffer.length = 0; } if(LocalMsgBuffer.buffer){ SESSMGR_MEM_FREE(LocalMsgBuffer.buffer) LocalMsgBuffer.length = 0; } if ((Status != STATUS_SUCCESS) || (IsSignatureValid != TRUE)){ DBG_ASSERT(0); return X509_INVALID_SIGNATURE; } #endif // #ifdef X509_FOR_PSE_PR break; default: assert(0); } }while(0); return Status; } #define CRYPTO_SIZE_SHA256 32 #ifndef X509_FOR_PSE_PR STATUS VerifyOcspResponseAttributes(Uint8* OcspRespBuffer, SessMgrOcspResponseFields *ocspResponseFields, ISSUER_INFO* OcspCertRootPublicKey, SessMgrDataBuffer Nonce, OCSP_REQ_TYPE OcspReqType, BOOL UseFacsimileEpid) { SessMgrCertificateFields certificateFields; STATUS Status; UINT32 workBufferSize = 1000; UINT8 *workBuffer = NULL; ISSUER_INFO IssuerInfo; SessMgrDataBuffer PubKeyHashBuf; UINT8 HashOut[SHA1_HASH_LEN] = {0}; PubKeyHashBuf.buffer = HashOut; PubKeyHashBuf.length = SHA1_HASH_LEN; if(ocspResponseFields->ocspResponseStatus != successful){ return X509_STATUS_OCSP_FAILURE; } // parse the ocsp certificate SESSMGR_MEM_ALLOC_BUFFER(workBuffer, MM_DATA_HEAP_SHARED_RW, sizeof(UINT32), workBufferSize, TX_WAIT_FOREVER); do { Status = ParseCertificateChain(ocspResponseFields->responderCertificate.buffer, ocspResponseFields->responderCertificate.length, &certificateFields, workBuffer, workBufferSize, NULL, 0, NULL, OcspResponderCertificate, UseFacsimileEpid); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); break; } // verify OCSP response signature. IssuerInfo.AlgoType = ocspResponseFields->algorithmIdentifierForSignature; IssuerInfo.buffer = certificateFields.subjectPublicKey.buffer; IssuerInfo.length = certificateFields.subjectPublicKey.length; Status = VerifySignature(&IssuerInfo, &ocspResponseFields->tbsResponseData, &ocspResponseFields->signature, UseFacsimileEpid); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); Status = X509_INVALID_SIGNATURE; break; } // Verify Nonce only on Signed FW. #ifndef _WIN32_DEVPLATFORM #ifndef X509_FOR_PSE_PR if(!(gManifestDataPtr->ManifestHeader.manifestFlags.r.debugManifest)){ #endif // Verify Nonce only for Non-cached Response. if(OcspReqType == NON_CACHED){ // Make sure we have a nonce in the OCSP response if(!ocspResponseFields->nonce.buffer || Nonce.length <= 0){ DBG_ASSERT(0); Status = X509_STATUS_OCSP_FAILURE; break; } if( memcmp(ocspResponseFields->nonce.buffer, Nonce.buffer, Nonce.length) != 0 || (ocspResponseFields->nonce.length != Nonce.length) ) { DBG_ASSERT(0); Status = X509_STATUS_OCSP_FAILURE; break; } } #ifndef X509_FOR_PSE_PR } #endif #endif // If ResponderId is a KeyHash, then verify its a Hash of the responders public key. if(ocspResponseFields->ocspResponderIdKeyHash.buffer){ Status = CryptoCreateHash(CRYPTO_HASH_TYPE_SHA1, &certificateFields.EncodedSubjectPublicKey, &PubKeyHashBuf, NULL, NULL, SINGLE_BLOCK); if(Status != STATUS_SUCCESS){ DBG_ASSERT(0); Status = X509_STATUS_INTERNAL_ERROR; break; } if(SHA1_HASH_LEN != ocspResponseFields->ocspResponderIdKeyHash.length || memcmp(HashOut,ocspResponseFields->ocspResponderIdKeyHash.buffer, ocspResponseFields->ocspResponderIdKeyHash.length) != 0){ DBG_ASSERT(0); Status = X509_STATUS_OCSP_FAILURE; break; } }else{ // If there is no Hash, There has to be a name. In the name structure, Common Name has to exist. if(!ocspResponseFields->ocspResponderIdName.commonName){ DBG_ASSERT(0); Status = X509_STATUS_OCSP_FAILURE; break; } // if Responder Id is a Name, make sure the value matches the value in the certificate. if(ocspResponseFields->ocspResponderIdName.commonNameSize != certificateFields.subject.commonNameSize || memcmp(ocspResponseFields->ocspResponderIdName.commonName, certificateFields.subject.commonName, certificateFields.subject.commonNameSize) != 0){ DBG_ASSERT(0); Status = X509_STATUS_OCSP_FAILURE; break; } } // We have verified the OCSP response. See if we have trusted time. Else provision it. if(OcspReqType == NON_CACHED){ Status = StoreTrustedTime(ocspResponseFields->producedAt); if(Status == STATUS_SUCCESS) gSessmgrCtx.TrustedTimeProvisioned = TRUE; } // If we have cached response, Make sure produced at was not more than a day if(OcspReqType == CACHED){ Status = VerifyOcspCachedResponseValidity(ocspResponseFields); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); Status = X509_STATUS_OCSP_FAILURE; break; } } #ifdef PRINT printf("\nOcsp response signature verified "); SetConsoleTextAttribute(hConsole, 2); printf("\n \n *************** VerifyOcspResponseAttributes complete ***************** \n \n"); SetConsoleTextAttribute(hConsole, 8); #endif Status = X509_STATUS_SUCCESS; } while(0); SESSMGR_MEM_FREE(workBuffer); return Status; } #endif // #ifndef X509_FOR_PSE_PR STATUS VerifyBasicCertificateAttributes(const Uint8* certificateDerEncoded, const Uint8* workBuffer, const SessMgrCertificateFields* certificateFields, const ISSUER_INFO *IssuerInfo, const CertificateType CertType, CertificateLevel CertLevel, BOOL UseFacsimileEpid) { SessMgrEcdsaPublicKey *EcdsaKey; #ifdef X509_FOR_PSE_PR UNUSED(certificateDerEncoded); UNUSED(workBuffer); UNUSED(EcdsaKey); UNUSED(CertType); UNUSED(CertLevel); UNUSED(UseFacsimileEpid); #endif // Make sure the signature Algorithms for issuer is the same as the one used in the TbsCertificate if(certificateFields->TbsCertSignAlgoId != certificateFields->algorithmIdentifierForSignature){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } #ifndef X509_FOR_PSE_PR //PSE Usage doesn't need to check Cert expiration STATUS Status = X509_GENERAL_ERROR; // Verify if the certificate time is valid. At this point we expect trusted time to be set. /* Chicken and Egg problem: we get trusted time from OCSP. How do we check validity of OCSP responder certificate? Solution : Intel signs the OCSP responder cert and EPID group certs. its valid for a really long time. So for these certs, dont check validity.*/ if((CertType != EpidGroupCertificate) && (CertType != OcspResponderCertificate)){ Status = VerifyValidity(certificateFields->notValidBeforeTime, certificateFields->notValidAfterTime); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); return X509_STATUS_EXPIRED_CERTIFICATE; } } Status = VerifySignature(IssuerInfo, &certificateFields->messageBuffer, &certificateFields->signatureBuffer, UseFacsimileEpid); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); return X509_INVALID_SIGNATURE; } #endif // Common name and OrgName should be present. if(!certificateFields->issuer.commonName || !certificateFields->subject.commonName || !certificateFields->subject.organization){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } #if 0 // The issuer of the root certificate has to be intel irrespective of the certificate type. if(CertLevel == root){ if(certificateFields->issuer.commonNameSize != strlen("www.intel.com") || memcmp(certificateFields->issuer.commonName, "www.intel.com", strlen("www.intel.com")) !=0){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } } #endif // Make sure the subject of the prev certificate is the issuer of the current certificate if(IssuerInfo->CommonNameBuf.buffer && IssuerInfo->CommonNameBuf.length > 0){ if(certificateFields->issuer.commonNameSize != IssuerInfo->CommonNameBuf.length || memcmp(certificateFields->issuer.commonName, IssuerInfo->CommonNameBuf.buffer, IssuerInfo->CommonNameBuf.length) != 0){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } } #ifndef X509_FOR_PSE_PR switch(CertType){ case OcspResponderCertificate: if(certificateFields->keyUsage.value == 0){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } // Make sure only the non-repudiation and digitalSignature are set. if(certificateFields->keyUsage.value != (X509_BIT0 | X509_BIT1)){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } // ExtendedKeyUsage must be specified. if(certificateFields->ExtendedKeyUsage.value == 0){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } // Basic constraints extension should be present. if(!certificateFields->basicConstraint.isBasicConstraintPresent){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } // isCa should be deasserted. We will not delegate OCSP signing authority to anybody else. if(certificateFields->basicConstraint.isCa == DER_ENCODING_TRUE){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } break; case VerifierCertificate: // Basic Constraint should be present. if(!certificateFields->basicConstraint.isBasicConstraintPresent){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } // Make sure leaf certs dont have isCA set and intermediate Certs have isCa deasserted. if( (CertLevel == leaf && certificateFields->basicConstraint.isCa == DER_ENCODING_TRUE) || (CertLevel != leaf && certificateFields->basicConstraint.isCa == DER_ENCODING_FALSE)){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } if(certificateFields->algorithmIdentifierForSubjectPublicKey == X509_ecdsaPublicKey){ // For ECDSA public key, we expect curvve to be prime256v1 EcdsaKey = (SessMgrEcdsaPublicKey *)certificateFields->subjectPublicKey.buffer; if(EcdsaKey->eccParameter != curvePrime256v1){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } } // For root and intermediate certificates, BIT5 (KeyCertSign) must be asserted if(CertLevel != leaf){ if(certificateFields->keyUsage.value != X509_BIT5){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } }else{ if(certificateFields->keyUsage.value != (X509_BIT0 | X509_BIT1)){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } } // Check for Subject Key Identifier. Per spec, all certificate except leaf should have this and should be equal to SHA1 of the public key. if(CertLevel != leaf){ if(!certificateFields->SubjectKeyId.buffer || certificateFields->SubjectKeyId.length != SHA1_HASH_LEN ){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } if(VerifySha1Hash(&certificateFields->EncodedSubjectPublicKey, certificateFields->SubjectKeyId.buffer , certificateFields->SubjectKeyId.length) == FALSE){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } } // Every verifier cert should have an authority key id if(!certificateFields->AuthorityKeyId.buffer || (certificateFields->AuthorityKeyId.length != IssuerInfo->EncodedPublicKeyHashBuffer.length) ){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } // Verify Authority Key Id. Spec says Authority Key ID of current cert should be equal to the SubjectKeyId of the upper cert. SubjectKeyId is nothing but the hash of the upper certs public key. // we have that available in this function. So compare Authority Key with that. if(certificateFields->AuthorityKeyId.length != IssuerInfo->EncodedPublicKeyHashBuffer.length || memcmp(certificateFields->AuthorityKeyId.buffer, IssuerInfo->EncodedPublicKeyHashBuffer.buffer, IssuerInfo->EncodedPublicKeyHashBuffer.length) != 0){ // if the first cert is signed by the prod Intel IVK & GID is 0, try again with hash of the prod Intel IVK #ifndef X509_FOR_PSE_PR if (gSessmgrCtx.FuseGidZero){ #endif UINT8 TempSignKeyBuffer[ECDSA_KEY_SIZE + 1] = {0}; SessMgrDataBuffer TempDataBuffer = {sizeof(TempSignKeyBuffer), TempSignKeyBuffer}; TempSignKeyBuffer[0] = 0x04; SESSMGR_MEMCPY_S(TempSignKeyBuffer + 1, ECDSA_KEY_SIZE, INTEL_ECDSA_PUBKEY_PROD_BE, sizeof(INTEL_ECDSA_PUBKEY_PROD_BE)); Status = CryptoCreateHash(CRYPTO_HASH_TYPE_SHA1, &TempDataBuffer, &IssuerInfo->EncodedPublicKeyHashBuffer, NULL, NULL, SINGLE_BLOCK); if (Status != STATUS_SUCCESS) { DBG_ASSERT(0); return X509_STATUS_INTERNAL_ERROR; } if (certificateFields->AuthorityKeyId.length != IssuerInfo->EncodedPublicKeyHashBuffer.length || memcmp(certificateFields->AuthorityKeyId.buffer, IssuerInfo->EncodedPublicKeyHashBuffer.buffer, IssuerInfo->EncodedPublicKeyHashBuffer.length) != 0){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } #ifndef X509_FOR_PSE_PR } else{ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } #endif // #ifdef X509_FOR_PSE_PR } // Product type must be present if(certificateFields->productType == invalidProductType || certificateFields->productType >= Max_ProductType){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } // If issuer certificate product type exists, ensure it matches current cert if ((IssuerInfo->productType != invalidProductType) && (IssuerInfo->productType != certificateFields->productType)) { DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } break; } #endif #ifdef PRINT SetConsoleTextAttribute(hConsole, 4); printf("\n \n *************** VerifyBasicCertificateAttributes complete ***************** \n \n"); SetConsoleTextAttribute(hConsole, 8); #endif return X509_STATUS_SUCCESS; } #ifndef X509_FOR_PSE_PR static BOOL VerifySha1Hash(SessMgrDataBuffer *HashData, UINT8 *Expectedhash, UINT32 ExpectedHashLength) { UINT8 HashOut[SHA1_HASH_LEN] = {0}; SessMgrDataBuffer HashBuf; STATUS Status; if(!HashData->buffer || HashData->length == 0) return FALSE; HashBuf.buffer = HashOut; HashBuf.length = SHA1_HASH_LEN; memset(HashBuf.buffer, 0 , HashBuf.length); Status = CryptoCreateHash(CRYPTO_HASH_TYPE_SHA1, HashData, &HashBuf, NULL, NULL, SINGLE_BLOCK); if(Status != STATUS_SUCCESS){ DBG_ASSERT(0); return FALSE; } if(HashBuf.length != ExpectedHashLength || memcmp(HashBuf.buffer, Expectedhash, ExpectedHashLength) != 0){ return FALSE; } return TRUE; } #endif #ifndef X509_FOR_PSE_PR //NRG: not validating time in enclave STATUS VerifyValidity(SessMgrDateTime notValidBeforeTime, SessMgrDateTime NotValidAfterTime) { STATUS Status; NTP_TIMESTAMP CurrentTime, NotValidBefore, NotValidAfter; // At this point, we expect trusted time to be provisioned. Else Error if(!gSessmgrCtx.TrustedTimeProvisioned){ DBG_ASSERT(0); return X509_STATUS_INTERNAL_ERROR; } Status = PrtcGetTime(&CurrentTime, gSessmgrCtx.TrustedTime.RtcOffset); if(Status != STATUS_SUCCESS){ DBG_ASSERT(0); return X509_STATUS_INTERNAL_ERROR; } // extract time from the certs and convert it into NTP. Get NotValidBefore and NotValidAfter. // There is no day 0. We use that to find if the field has been populated. If not, ignore this check. if(notValidBeforeTime.date.yearMonthDay.day != 0){ Status = ConvertTimeToNtp(notValidBeforeTime, &NotValidBefore); if(Status != STATUS_SUCCESS){ DBG_ASSERT(0); return X509_STATUS_INTERNAL_ERROR; } if( CurrentTime.Seconds < NotValidBefore.Seconds - 120){ DBG_ASSERT(0); return X509_STATUS_EXPIRED_CERTIFICATE; } } if(NotValidAfterTime.date.yearMonthDay.day != 0){ Status = ConvertTimeToNtp(NotValidAfterTime, &NotValidAfter); if(Status != STATUS_SUCCESS){ DBG_ASSERT(0); return X509_STATUS_INTERNAL_ERROR; } if(CurrentTime.Seconds > NotValidAfter.Seconds){ DBG_ASSERT(0); return X509_STATUS_EXPIRED_CERTIFICATE; } } return X509_STATUS_SUCCESS; } STATUS VerifyOcspCachedResponseValidity(SessMgrOcspResponseFields *OcspResponseFields) { STATUS Status; NTP_TIMESTAMP CurrentTime = {0, 0}; NTP_TIMESTAMP ProducedAt = {0, 0}; // At this point, we expect trusted time to be provisioned. Else Error if(!gSessmgrCtx.TrustedTimeProvisioned){ DBG_ASSERT(0); return X509_STATUS_INTERNAL_ERROR; } Status = PrtcGetTime(&CurrentTime, gSessmgrCtx.TrustedTime.RtcOffset); if(Status != STATUS_SUCCESS){ DBG_ASSERT(0); return X509_STATUS_INTERNAL_ERROR; } // extract time from the certs and convert it into NTP. Get NotValidBefore and NotValidAfter Status = ConvertTimeToNtp(OcspResponseFields->producedAt, &ProducedAt); if(Status != STATUS_SUCCESS){ DBG_ASSERT(0); return X509_STATUS_INTERNAL_ERROR; } #ifndef _WIN32_DEVPLATFORM // don't validate certificate time on DevPlatform // assuming no rollover if ((ProducedAt.Seconds > CurrentTime.Seconds) && ((ProducedAt.Seconds - CurrentTime.Seconds) > OCSP_DELAY_TOLERANCE_SECONDS )){ DBG_ASSERT(0); return X509_STATUS_INTERNAL_ERROR; } if ((CurrentTime.Seconds > ProducedAt.Seconds) && ((CurrentTime.Seconds - ProducedAt.Seconds) > SECONDS_IN_DAY )){ DBG_ASSERT(0); return X509_STATUS_EXPIRED_CERTIFICATE; } #endif // _WIN32_DEVPLATFORM return X509_STATUS_SUCCESS; } #endif // #ifndef X509_FOR_PSE_PR /* This function will parse the certificate, will extract out interesting data (defined in the "Fields" structure) and return them to the caller. */ static STATUS sessMgrParseDerCert ( IN X509_PROTOCOL* X509Protocol, IN Uint8* certificateDerEncoded, IN Uint8* pCertEnd, IN Uint8* workBuffer, IN UINT32 workBufferSize, OUT SessMgrCertificateFields* certificateFields, IN ISSUER_INFO *IssuerInfo, IN BOOL UseFacsimileEpid ) { UINT32 Status; /* Refer to certificate structure for definition */ Uint8* workBufferStart = workBuffer; UINT8* current_ptr = certificateDerEncoded; UINT8* pTemp; UINT32 length; UINT32 i = 0; SessMgrEllipticCurveParameter params = unknownParameter; UINT8 EncodingBytes; UINT32 padding; #ifdef PRINT printf("\n **************** Parsing a Certificate ******************** "); for(i=0;iserialNumber.length;i++) printf("%hhx ",*(certificateFields->serialNumber.buffer + i)); #endif #ifdef X509_FOR_PSE_PR UNUSED(X509Protocol); UNUSED(i); #endif do{ Status = ParseIdAndLength(¤t_ptr, pCertEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; certificateFields->messageBuffer.buffer = current_ptr; /* Start of TBS Certificate : Starts with a sequence */ Status = ParseIdAndLength(¤t_ptr, pCertEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; // Total signed buffer length = certificate Length + Encoding Bytes ( +1 is for the Type identifier) certificateFields->messageBuffer.length = length + EncodingBytes + 1; /*** Early Signature Verification ****/ // Try to verify signature first. That way we resolve all corruption problems. pTemp = certificateFields->messageBuffer.buffer + certificateFields->messageBuffer.length; // Current pointer is at SignatureAlgorithm which is a algorithm identifier Status = ParseAlgoIdentifier(&pTemp, pCertEnd, (UINT32*)&certificateFields->TbsCertSignAlgoId, signature_algo, ¶ms); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); break; } // Next field : SignatureValue Status = ParseSignatureValue(&pTemp, pCertEnd, &workBuffer, workBufferSize - (int)(workBuffer-workBufferStart), &certificateFields->signatureBuffer, certificateFields->TbsCertSignAlgoId); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); break; } Status = VerifySignature(IssuerInfo, &certificateFields->messageBuffer, &certificateFields->signatureBuffer, UseFacsimileEpid); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); return X509_INVALID_SIGNATURE; } // // if signature verifies, then length values in cert // are ok wrt Spectre // sgx_lfence(); /**** End Early Signature Verification *****/ // Next Field : Version (Optional field with explicit tagging) Status = ParseIdAndLength(¤t_ptr, pCertEnd, EXPLICIT_TAG_0_ID_VALUE, &length, &EncodingBytes, TRUE); if( (Status != X509_STATUS_SUCCESS) && Status != X509_STATUS_NOT_FOUND) break; if(Status != X509_STATUS_NOT_FOUND){ // We have a version number. Note: Not using ParseInteger function because certificateVersion is defined as a UINT32 and not a DataBuffer Status = ParseIdAndLength(¤t_ptr, pCertEnd, DER_ENCODING_INTEGER_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS || length > MAX_VERSION_LENGTH_SIZE_BYTES){ Status = X509_STATUS_ENCODING_ERROR; break; } SESSMGR_MEMCPY_S((UINT8*)&certificateFields->certificateVersion + sizeof(UINT32) - length, length, current_ptr, length); // The certificates are big endian. SwapEndian((UINT8 *)&certificateFields->certificateVersion, sizeof(UINT32)); if(certificateFields->certificateVersion != v3){ Status = X509_STATUS_INVALID_VERSION; break; } current_ptr+= length; }else{ /* Default is v1 according to spec . But we dont support onyl v3. Return error. */ Status = X509_STATUS_INVALID_VERSION; break; } // Next Field : Certificate Serial Number Format : Integer Identifier + Length + Value (Max 20 bytes) Status = ParseInteger(¤t_ptr, pCertEnd, &certificateFields->serialNumber, FALSE, TRUE, &padding); if ((Status != X509_STATUS_SUCCESS) || (certificateFields->serialNumber.length + padding > MAX_HASH_LEN)) { DBG_ASSERT(0); Status = X509_STATUS_ENCODING_ERROR; break; } #ifdef PRINT printf("\n Serial Number "); for(i=0;iserialNumber.length;i++) printf("%hhx ",*(certificateFields->serialNumber.buffer + i)); #endif // Next Field : Algorithm Identifier (signature) The OID is expected to be one of the HardCodedSignatureAlgorithmOid array values. Status = ParseAlgoIdentifier(¤t_ptr, pCertEnd, (UINT32*)&certificateFields->algorithmIdentifierForSignature,signature_algo, ¶ms); if(Status != X509_STATUS_SUCCESS){ Status = X509_STATUS_ENCODING_ERROR; break; } // Next Field : issuer Status = ParseName(¤t_ptr, pCertEnd, &certificateFields->issuer); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); break; } // Next Field : Validity Format : sequence notBefore notAfter Status = ParseIdAndLength(¤t_ptr, pCertEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; // Parse notBefore, notAfter, Subject, SubjectPublic key info Status = ParseTime(¤t_ptr, pCertEnd, &certificateFields->notValidBeforeTime); if(Status != X509_STATUS_SUCCESS) break; Status = ParseTime(¤t_ptr, pCertEnd, &certificateFields->notValidAfterTime); if(Status != X509_STATUS_SUCCESS) break; Status = ParseName(¤t_ptr, pCertEnd, &certificateFields->subject); if(Status != X509_STATUS_SUCCESS) break; Status = ParseSubjectPublicKeyInfo(¤t_ptr, pCertEnd, &workBuffer, certificateFields); if(Status != X509_STATUS_SUCCESS) break; // Next Field: IssuerUniqueId [optional] Implicit TAG Number is 1 Type : UniqueIdentifier (BIT STRING) Status = ParseIdAndLength(¤t_ptr, pCertEnd, IMPLICIT_TAG_ID + TAG_NUMBER_ISSUER_UNIQUE_ID, &length, &EncodingBytes, TRUE); if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_NOT_FOUND) break; if(Status != X509_STATUS_NOT_FOUND){ certificateFields->IssuerUniqueId.buffer = current_ptr; certificateFields->IssuerUniqueId.length = length; current_ptr += length; } // Next Field: SubjectUniqueId [optional] Implicit TAG Number is 2 Type : UniqueIdentifier (BIT STRING) Status = ParseIdAndLength(¤t_ptr, pCertEnd, IMPLICIT_TAG_ID + TAG_NUMBER_SUBJECT_UNIQUE_ID, &length, &EncodingBytes, TRUE); if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_NOT_FOUND) break; if(Status != X509_STATUS_NOT_FOUND){ certificateFields->SubjectUniqueId.buffer = current_ptr; certificateFields->SubjectUniqueId.length = length; current_ptr += length; } // Next Field: Extensions [optional] Status = ParseIdAndLength(¤t_ptr, pCertEnd, EXPLICIT_TAG_ID + TAG_NUMBER_EXTENSIONS, &length, &EncodingBytes, TRUE); if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_NOT_FOUND) break; if(Status != X509_STATUS_NOT_FOUND){ #ifndef X509_FOR_PSE_PR Status = ParseCertExtensions(¤t_ptr, current_ptr + length, certificateFields); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); break; } #else current_ptr += length; #endif } /* End of TbsCErtificate */ // Current pointer is at SignatureAlgorithm which is a algorithm identifier Status = ParseAlgoIdentifier(¤t_ptr, pCertEnd, (UINT32*)&certificateFields->TbsCertSignAlgoId, signature_algo, ¶ms); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); break; } // Next field : SignatureValue Status = ParseSignatureValue(¤t_ptr, pCertEnd, &workBuffer, workBufferSize - (int)(workBuffer - workBufferStart), &certificateFields->signatureBuffer, certificateFields->algorithmIdentifierForSignature); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); break; } }while(0); DBG_ASSERT((int)(workBuffer - workBufferStart) < workBufferSize) DBG_ASSERT(Status == X509_STATUS_SUCCESS); if (current_ptr != pCertEnd) { DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } return Status; } #ifndef X509_FOR_PSE_PR STATUS sessMgrParseOcspResponse ( IN X509_PROTOCOL* X509Protocol, IN Uint8* OcspResponse, IN Uint8* OcspResponseEnd, IN Uint8* workBuffer, IN UINT32 workBufferSize, OUT SessMgrOcspResponseFields* OcspResponseFields ) { STATUS Status; Uint8* workBufferStart = workBuffer; UINT8* current_ptr = OcspResponse; UINT32 length; UINT32 temp; SessMgrOcspSingleResponse *SingleResponse; UINT8 *end_of_single_responses; SessMgrEllipticCurveParameter params; UINT8 EncodingBytes; UINT32 padding; /* Ocsp response is a SEQ { responseStatus, response bytes }*/ do{ Status = ParseIdAndLength(¤t_ptr, OcspResponseEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; // Next Field: response status response status is a enumerated type Status = ParseIdAndLength(¤t_ptr, OcspResponseEnd, DER_ENCODING_ENUMERATED_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS || length != 1){ // Note: currently we support only 7 bits. So error out if length is more than 1 byte Status = X509_STATUS_ENCODING_ERROR; break; } OcspResponseFields->ocspResponseStatus = (ResponseStatus)*current_ptr; current_ptr += length; // Next Field: response bytes Reponsne Bytes is a SEQ { response type , response }. response bytes is optional Status = ParseIdAndLength(¤t_ptr, OcspResponseEnd, EXPLICIT_TAG_ID + TAG_NUMBER_RESPONSE_BYTES, &length, &EncodingBytes, TRUE); if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_NOT_FOUND) break; if(Status == X509_STATUS_NOT_FOUND){ // No response bytes. Nothing more to parse. Status = X509_STATUS_SUCCESS; break; } /* WE HAVE A RESPONSE !!!! */ Status = ParseIdAndLength(¤t_ptr, OcspResponseEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; // Next Field: responseType Type : Object ID Status = ParseOID(¤t_ptr, OcspResponseEnd, &temp, &OcspResponseTypeOid[0][0], sizeof(OcspResponseTypeOid)/sizeof(OcspResponseTypeOid[0]), sizeof(OcspResponseTypeOid[0])); if(Status != X509_STATUS_SUCCESS || temp != 0){ DBG_ASSERT(0); Status = X509_STATUS_ENCODING_ERROR; break; } // Next Field: response Type : OCTET STRING Status = ParseIdAndLength(¤t_ptr, OcspResponseEnd, DER_ENCODING_OCTET_STRING_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; // The only response type we support is basicOcspResponse. BasicOcspResponse is SEQ {tbsResponseData, SignatureAlgo, Signature, Certs [optional] } Status = ParseIdAndLength(¤t_ptr, OcspResponseEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; // Next Field : tbsResponseData Format : Seq { version [optional], ResponderId, ProducesdAt, responses, responseExtensions [optional] OcspResponseFields->tbsResponseData.buffer = current_ptr; Status = ParseIdAndLength(¤t_ptr, OcspResponseEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; OcspResponseFields->tbsResponseData.length = length + EncodingBytes + 1; // We dont store the version anywhere. Just parse if present and move pointer to next type Status = ParseIdAndLength(¤t_ptr, OcspResponseEnd, EXPLICIT_TAG_ID + TAG_NUMBER_VERSION, &length, &EncodingBytes, TRUE); if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_NOT_FOUND) break; if(Status != X509_STATUS_NOT_FOUND){ // we have version. Just parse over it current_ptr += length; } // Next Field : ResponderId ResponderId can be a choice of either Name or KeyHash. We distinguish them using the tags Status = ParseIdAndLength(¤t_ptr, OcspResponseEnd, EXPLICIT_TAG_ID + TAG_NUMBER_RESPONDER_NAME, &length, &EncodingBytes, TRUE); if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_NOT_FOUND) break; if(Status != X509_STATUS_NOT_FOUND){ Status = ParseName(¤t_ptr, OcspResponseEnd, &OcspResponseFields->ocspResponderIdName); if(Status != X509_STATUS_SUCCESS) break; }else{ Status = ParseIdAndLength(¤t_ptr, OcspResponseEnd, EXPLICIT_TAG_ID + TAG_NUMBER_RESPONDER_KEYHASH, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS){ /* We either need to have a Id or a Keyhash */ Status = X509_STATUS_ENCODING_ERROR; break; } // KeyHash is a octet String Status = ParseIdAndLength(¤t_ptr, OcspResponseEnd, DER_ENCODING_OCTET_STRING_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; OcspResponseFields->ocspResponderIdKeyHash.buffer = current_ptr; OcspResponseFields->ocspResponderIdKeyHash.length = length; current_ptr +=length; } /* Next Field : ProducedAt */ Status = ParseTime(¤t_ptr, OcspResponseEnd, &OcspResponseFields->producedAt); if(Status != X509_STATUS_SUCCESS) break; #ifdef TIME_PRINT printf("\n Produced At "); PrintValidity(&OcspResponseFields->producedAt); #endif // Next Field : response Format : responses is a SEQ { single Responses } Status = ParseIdAndLength(¤t_ptr, OcspResponseEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; end_of_single_responses = current_ptr + length; // Each single response is parsed and put into the SessMgrOcspSingleResponse structure. This structure is declared out of the work buffer SingleResponse = (SessMgrOcspSingleResponse *)workBuffer; OcspResponseFields->allResponses = (SessMgrOcspSingleResponse*) workBuffer; OcspResponseFields->numberOfSingleReponses = 0; while(current_ptr < end_of_single_responses){ // update workbuffer pointer for future use workBuffer += sizeof(SessMgrOcspSingleResponse); // Each single response is a SEQ { CertID, CertStatus, Thisupdate, nextUpdate [optional], SingleExtensions [optional] */ Status = ParseIdAndLength(¤t_ptr, end_of_single_responses, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; // current pointer is over CertId CertId is a Seq {hash algorithm, issuername hash, issuerKeyHash, serialNumber} Status = ParseIdAndLength(¤t_ptr, end_of_single_responses, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; // current pointer over Hash algo Hash algorithm is a Algorithm identifier Status = ParseAlgoIdentifier(¤t_ptr, end_of_single_responses, (UINT32*)&SingleResponse->issuerIdentifierHashType,Hash_algo, ¶ms); if(Status != X509_STATUS_SUCCESS) break; // Next Field: IssueNameHash Type : octet string */ Status = ParseIdAndLength(¤t_ptr, end_of_single_responses, DER_ENCODING_OCTET_STRING_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; SingleResponse->issuerNameHash.buffer = current_ptr; SingleResponse->issuerNameHash.length = length; current_ptr+=length; // Next Field: IssueKeyHash Type : octet string Status = ParseIdAndLength(¤t_ptr, end_of_single_responses, DER_ENCODING_OCTET_STRING_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; SingleResponse->issuerKeyHash.buffer = current_ptr; SingleResponse->issuerKeyHash.length = length; current_ptr+=length; // Next Field: SerialNumber Type : Integer */ Status = ParseInteger(¤t_ptr, end_of_single_responses, &SingleResponse->serialNumber, FALSE, TRUE, &padding); if(Status != X509_STATUS_SUCCESS || (SingleResponse->serialNumber.length + padding > MAX_HASH_LEN)){ DBG_ASSERT(0); Status = X509_STATUS_ENCODING_ERROR; break; } // Next field: CertStatus certStatus can be a choice of { good [type : NULL], revoked [type : revoked info], unknown [type : NULL] switch(*current_ptr) { case IMPLICIT_TAG_ID + TAG_NUMBER_GOOD: SingleResponse->ocspCertificateStatus = good; current_ptr++; /* NULL id is always followed by length 0x00 */ if(*current_ptr != 0) { DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } current_ptr++; break; case IMPLICIT_TAG_ID + TAG_NUMBER_UNKNOWN: SingleResponse->ocspCertificateStatus = unknown; current_ptr++; /* NULL id is always followed by length 0x00 */ if(*current_ptr != 0) { DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } current_ptr++; break; case IMPLICIT_TAG_STRUCTURED_TYPE_ID + TAG_NUMBER_REVOKED: SingleResponse->ocspCertificateStatus = revoked; /* This will be followed by a revoked time and reason. We parse but ignore those fields */ current_ptr++; /* Get length */ Status = DecodeLength(current_ptr, end_of_single_responses, &length, &EncodingBytes); if(Status != X509_STATUS_SUCCESS) break; current_ptr += EncodingBytes; /* Move current_ptr beyond the revoked data */ current_ptr +=length; break; default: DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } if(Status != X509_STATUS_SUCCESS) break; // Next Field : ThisUpdate type: GeneralizedTime Status = ParseTime(¤t_ptr, end_of_single_responses, &SingleResponse->thisUpdate); if(Status != X509_STATUS_SUCCESS) break; /* Next Field : NextUpdate [optional] */ Status = ParseIdAndLength(¤t_ptr, end_of_single_responses, EXPLICIT_TAG_ID + TAG_NUMBER_NEXT_UPDATE, &length, &EncodingBytes, TRUE); if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_NOT_FOUND) break; if(Status != X509_STATUS_NOT_FOUND){ Status = ParseTime(¤t_ptr, end_of_single_responses, &SingleResponse->nextUpdate); if(Status != X509_STATUS_SUCCESS) break; } /* INTERESTING CASE: Both singleExtensions and responseExtensions use the tag [1]. So at this point, there is no way of finding out if "A1" means Single or response Extentions. We check the end of sequence pointer to resolve this. */ if(current_ptr < end_of_single_responses){ // Next Field : Single Extensions [optional] we parse this but ignore the contents Status = ParseIdAndLength(¤t_ptr, end_of_single_responses, EXPLICIT_TAG_ID + TAG_NUMBER_SINGLE_EXTENSIONS, &length, &EncodingBytes, TRUE); if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_NOT_FOUND) break; if(Status != X509_STATUS_NOT_FOUND){ /* Move pointer past the extensions */ current_ptr +=length; } } SingleResponse = (SessMgrOcspSingleResponse *)((UINT8 *)SingleResponse + sizeof(SessMgrOcspSingleResponse)); OcspResponseFields->numberOfSingleReponses++; Status = X509_STATUS_SUCCESS; } if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); Status = X509_STATUS_ENCODING_ERROR; break; } #ifdef PRINT int i; printf("\n Number of single responses %d", OcspResponseFields->numberOfSingleReponses); for (i=0;inumberOfSingleReponses;i++){ printf("\n\n Response Number %d", i); SingleResponse = (SessMgrOcspSingleResponse *)(OcspResponseFields->allResponses) + i; printf("\n serial number "); PrintDataBuffer(&SingleResponse->serialNumber); #ifdef TIME_PRINT printf("\n This Update "); PrintValidity(&SingleResponse->thisUpdate); #endif } #endif // Next Field : Response Extensions [optional] Status = ParseIdAndLength(¤t_ptr, OcspResponseEnd, EXPLICIT_TAG_ID + TAG_NUMBER_RESPONSE_EXTENSIONS, &length, &EncodingBytes, TRUE); if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_NOT_FOUND) break; if(Status != X509_STATUS_NOT_FOUND){ // Move pointer past the extensions Status = ParseOcspExtensions(¤t_ptr, current_ptr + length, OcspResponseFields); if(Status != X509_STATUS_SUCCESS){ break; } } // Next field : Signature Algorithm Id*/ Status = ParseAlgoIdentifier(¤t_ptr, OcspResponseEnd, (UINT32 *)&OcspResponseFields->algorithmIdentifierForSignature ,signature_algo, ¶ms); if(Status != X509_STATUS_SUCCESS) break; Status = ParseSignatureValue(¤t_ptr, OcspResponseEnd, &workBuffer, workBufferSize - (int)(workBuffer - workBufferStart), &OcspResponseFields->signature, OcspResponseFields->algorithmIdentifierForSignature); if(Status != X509_STATUS_SUCCESS) break; // Next field : Certificate of the OCSP responder. This can be a chain */ // ??? why is this optional Status = ParseIdAndLength(¤t_ptr, OcspResponseEnd, EXPLICIT_TAG_ID + TAG_NUMBER_CERTS, &length, &EncodingBytes, TRUE); if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_NOT_FOUND) break; if(Status != X509_STATUS_NOT_FOUND){ /* This is a sequence of certificates. */ Status = ParseIdAndLength(¤t_ptr, OcspResponseEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; OcspResponseFields->responderCertificate.buffer = current_ptr; OcspResponseFields->responderCertificate.length = length; current_ptr += length; } }while(0); DBG_ASSERT(Status == X509_STATUS_SUCCESS); if (current_ptr != OcspResponseEnd) { DBG_ASSERT(0) return X509_STATUS_ENCODING_ERROR; } return Status; } #endif /* Common functions */ #ifndef X509_FOR_PSE_PR static STATUS ParseBoolean(UINT8 **ppCurrent, UINT8 *pEnd, BOOL* Value, BOOL optional) { STATUS Status; UINT8 *current_ptr = *ppCurrent; UINT8 EncodingBytes; UINT32 length; Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_BOOLEAN_ID, &length, &EncodingBytes, optional); if(Status == X509_STATUS_NOT_FOUND && optional == TRUE) return X509_STATUS_NOT_FOUND; if(Status != X509_STATUS_SUCCESS || length != 1){ Status = X509_STATUS_ENCODING_ERROR; return Status; } *Value = *current_ptr; current_ptr++; /* we expect Status to be 0xFF or 0x00 */ if( (*Value != DER_ENCODING_TRUE) && (*Value != DER_ENCODING_FALSE)) return X509_STATUS_ENCODING_ERROR; *ppCurrent = current_ptr; return X509_STATUS_SUCCESS; } #endif /* ParseInteger will strip out the integer encoding part. If parsing is successful, ParseInteger will update the current pointer and length Returns X509_ENCODING_ERROR on failure */ static STATUS ParseInteger(UINT8 **ppCurrent, UINT8 *pEnd, SessMgrDataBuffer* DataBuf, BOOL isOptional, BOOL MustBePositive, UINT32 *PaddingLen) { STATUS Status; UINT8 *current_ptr = *ppCurrent; UINT32 Integer_Length; UINT8 EncodingBytes; UINT32 PaddingCounter = 0; Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_INTEGER_ID, &Integer_Length, &EncodingBytes, isOptional); if(isOptional == TRUE && Status == X509_STATUS_NOT_FOUND) return X509_STATUS_NOT_FOUND; if(Status != X509_STATUS_SUCCESS) return X509_STATUS_ENCODING_ERROR; // MSB must be zero for positive integer. Error if MSB is not zero and we expect positive // // current_ptr could be out of bounds (speculatively) // sgx_lfence(); if(MustBePositive && (*current_ptr & 0x80)) return X509_STATUS_ENCODING_ERROR; // Note : DER Integer Encoding adds 0x00 if MSB of the actual integer is one. Ignore the first byte if it is 0x00 if ((*current_ptr == 0) && (Integer_Length > 1)){ current_ptr++; // skip padding Integer_Length--; // remove padding from length PaddingCounter++; } DataBuf->buffer = current_ptr; DataBuf->length = Integer_Length; *ppCurrent = current_ptr + Integer_Length; if (PaddingLen) *PaddingLen = PaddingCounter; return X509_STATUS_SUCCESS; } #ifndef X509_FOR_PSE_PR STATUS ParseOcspExtensions(UINT8 **ppCurrent, UINT8 *pEnd, SessMgrOcspResponseFields* OcspResponseFields) { UINT8 *current_ptr = *ppCurrent; UINT32 ExtensionType; BOOL critical; UINT32 length; STATUS Status; UINT8 EncodingBytes; do{ // Extensions = SEQ { extension } Each extension is associated with a Object ID. We support the extensions listed in ExtensionOID array. Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; while(current_ptr < pEnd){ // Each extension is a SEQ { extid (OID) , critical (BOOLEAN), extnValue (OCTET STRING) } Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; /* Next Field : Extension OID */ Status = ParseOID(¤t_ptr, pEnd, &ExtensionType, &OcspExtensionOid[0][0], sizeof(OcspExtensionOid)/sizeof(OcspExtensionOid[0]), sizeof(OcspExtensionOid[0])); // Note : We might have unsupported extensions. if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_UNKNOWN_OID){ DBG_ASSERT(0); break; } // Next field is "Critical". This indicates whether it is mandatory for the parser to understand the extension. This is an optinal field. default is false Status = ParseBoolean(¤t_ptr, pEnd, &critical, TRUE); if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_NOT_FOUND) break; if(Status == X509_STATUS_NOT_FOUND) critical = DER_ENCODING_FALSE; // Next Field: Extn Value This is a OCTET STRING Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_OCTET_STRING_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; switch(ExtensionType) { case Nonce: // Some OCSP responders have an additional encoding of OCTET string for the nonce. // We know the Nonce is 32 bytes (Per Sigma Spec. If given length is more than that, then check if this extra encoding was included. if(length > SIGMA_NONCE_LENGTH){ Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_OCTET_STRING_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; } DBG_ASSERT(length == SIGMA_NONCE_LENGTH); OcspResponseFields->nonce.buffer = current_ptr; OcspResponseFields->nonce.length = length; current_ptr+=length; break; default: /* unsupported extension. Check if it marked as critical. If so, return error else ignore this extension */ if(critical == DER_ENCODING_TRUE){ DBG_ASSERT(0); Status = X509_STATUS_UNSUPPORTED_CRITICAL_EXTENSION; break; } else{ /* Extension is non-criticial. So it is ok if we dont support it . Move current pointer to the next extension */ current_ptr += length; continue; } } // switch if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); break; } Status = X509_STATUS_SUCCESS; } // WHILE end of extensions }while(0); // do_while *ppCurrent = current_ptr; return Status; } #endif #ifndef X509_FOR_PSE_PR STATUS ParseCertExtensions(UINT8 **ppCurrent, UINT8 *pEnd, SessMgrCertificateFields* certificateFields) { UINT8 *current_ptr = *ppCurrent; UINT32 ExtensionType; BOOL critical; UINT32 length; STATUS Status; TAG_TYPE TagType; UINT8 EncodingBytes; UINT8 PaddedBits; SessMgrDataBuffer DataBuf; UINT32 ExtendedKeyUsageOcspType; do{ // Extensions = SEQ { extension } Each extension is associated with a Object ID. We support the extensions listed in ExtensionOID array. Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; while(current_ptr < pEnd){ // Each extension is a SEQ { extid (OID) , critical (BOOLEAN), extnValue (OCTET STRING) } Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; /* Next Field : Extension OID */ Status = ParseOID(¤t_ptr, pEnd, &ExtensionType, &CertExtensionOid[0][0], sizeof(CertExtensionOid)/sizeof(CertExtensionOid[0]), sizeof(CertExtensionOid[0])); // Note : We might have unsupported extensions. if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_UNKNOWN_OID){ DBG_ASSERT(0); break; } // Next field is "Critical". This indicates whether it is mandatory for the parser to understand the extension. This is an optinal field. default is false Status = ParseBoolean(¤t_ptr, pEnd, &critical, TRUE); if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_NOT_FOUND) break; if(Status == X509_STATUS_NOT_FOUND) critical = DER_ENCODING_FALSE; // Next Field: Extn Value This is a OCTET STRING Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_OCTET_STRING_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; switch(ExtensionType) { case AuthorityKeyId: /* Authority ID is a SEQ of {KeyIdentifier [optional TAG 0] , General Names [optional Tag 1], Certificate Serial number [optional Tag 2] */ /* Currently supported Intel and CA certs have general names and Certificate serial numbers as NULL */ Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; /* See if optional arguments are present */ // Note: InvalidTag means either the tag was not found (not an error if the field is optional) or the tag was not recognized. FIND_TAG_TYPE(current_ptr, TAG_NUMBER_AUTHORITY_KEY_ID, TagType); if(TagType != invalid_tag){ /* We have an implicit or explicit tag */ current_ptr++; /* Get Length of sequence */ Status = DecodeLength(current_ptr, pEnd, &length, &EncodingBytes); if(Status != X509_STATUS_SUCCESS) break; current_ptr += EncodingBytes; if(TagType == explicit_tag){ Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_OCTET_STRING_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; } /* Store the Authority Key ID into the data buffer */ certificateFields->AuthorityKeyId.buffer = current_ptr; certificateFields->AuthorityKeyId.length = length; current_ptr+=length; } FIND_TAG_TYPE(current_ptr, TAG_NUMBER_AUTHORITY_CERT_ISSUER_ID, TagType); if(TagType != invalid_tag){ current_ptr++; Status = DecodeLength(current_ptr, pEnd, &length, &EncodingBytes); if(Status != X509_STATUS_SUCCESS) break; current_ptr += EncodingBytes; if(TagType == explicit_tag){ /* Expecting a NULL. Encoding for NULL is 0x05 0x00 */ Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_NULL_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS || length !=0) break; } } FIND_TAG_TYPE(current_ptr, TAG_NUMBER_AUTHORITY_CERT_SERIAL_NUMBER_ID, TagType); if(TagType != invalid_tag){ current_ptr++; Status = DecodeLength(current_ptr, pEnd, &length, &EncodingBytes); if(Status != X509_STATUS_SUCCESS) break; current_ptr += EncodingBytes; if(TagType == explicit_tag){ /* Expecting a NULL. Encoding for NULL is 0x05 0x00 */ Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_NULL_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS || length !=0) break; } } break; case SubjectKeyId: // Next Field: Extn Value This is a OCTET STRING Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_OCTET_STRING_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; /* Store the Subject Key ID into the data buffer */ certificateFields->SubjectKeyId.buffer = current_ptr; certificateFields->SubjectKeyId.length = length; current_ptr+=length; break; case KeyUsage: /* KeyUsage is a bit string */ Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_BIT_STRING_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; /* KeyUsage is defined as a 16 bit value.So we expect the length to be 1 or 2. Add 1 to the check to account for the byte containing the number of bits paddd */ if(length != 2 && length != 3){ DBG_ASSERT(0); Status = X509_STATUS_ENCODING_ERROR; break; } /* current_ptr is pointing to the padded bits */ PaddedBits = *current_ptr; current_ptr++; /* decrementing length by one because length included the padded bits octet that we have already parsed*/ length--; if(length == 2){ certificateFields->keyUsage.value = *current_ptr; /* Shift by 8 */ certificateFields->keyUsage.value = certificateFields->keyUsage.value << 8; current_ptr++; } certificateFields->keyUsage.value = *current_ptr; current_ptr++; break; case ExtendedKeyUsage: /* ExtendedKeyUsage is a sequence of KeyPurposeId's */ Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; // KeyPurposeId is a object identifier Status = ParseOID(¤t_ptr, pEnd, &ExtendedKeyUsageOcspType, &ExtendedKeyUsageOcspSignOid[0][0], sizeof(ExtendedKeyUsageOcspSignOid)/sizeof(ExtendedKeyUsageOcspSignOid[0]), sizeof(ExtendedKeyUsageOcspSignOid[0])); if(Status != X509_STATUS_SUCCESS) break; /* we only support OcspSign in Extended key usage */ if(ExtendedKeyUsageOcspType != 0){ DBG_ASSERT(0); Status = X509_STATUS_UNSUPPORTED_TYPE; break; }else{ certificateFields->ExtendedKeyUsage.usage.OCSPSign = 1; } break; case BasicConstraint: /* Basic constraint is a SEQ { BOOLEAN (default false) , Integer [optional] */ certificateFields->basicConstraint.isBasicConstraintPresent = TRUE; Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; /* Next is a optional field for boolean */ Status = ParseBoolean(¤t_ptr, pEnd, &certificateFields->basicConstraint.isCa, TRUE); if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_NOT_FOUND) break; if(Status == X509_STATUS_NOT_FOUND) certificateFields->basicConstraint.isCa = DER_ENCODING_FALSE; // Next field is PathLenConstraint [Integer optional] Status = ParseInteger(¤t_ptr, pEnd, &DataBuf, TRUE, FALSE, NULL); if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_NOT_FOUND ) break; if(Status == X509_STATUS_NOT_FOUND) certificateFields->basicConstraint.pathLenConstraint = 0xFF; else{ Status = swapendian_memcpy((UINT8 *)&certificateFields->basicConstraint.pathLenConstraint, sizeof(UINT32), DataBuf.buffer, DataBuf.length); if(Status != X509_STATUS_SUCCESS) break; } Status = X509_STATUS_SUCCESS; break; case CertificatePolicy: Status = ParseCertificatePolicy(¤t_ptr, pEnd, &certificateFields->CertificatePolicy); break; case ProductType: /* Product type is a enumerated type */ Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_ENUMERATED_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS || length != 1) { Status = X509_STATUS_ENCODING_ERROR; break; } certificateFields->productType = (SessMgrProductType)*current_ptr; current_ptr++; break; default: /* unsupported extension. Check if it marked as critical. If so, return error else ignore this extension */ if(critical == DER_ENCODING_TRUE){ DBG_ASSERT(0); Status = X509_STATUS_UNSUPPORTED_CRITICAL_EXTENSION; break; } else{ /* Extension is non-criticial. So it is ok if we dont support it . Move current pointer to the next extension */ current_ptr += length; continue; } } // switch if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); break; } Status = X509_STATUS_SUCCESS; } // WHILE end of extensions }while(0); DBG_ASSERT(Status == X509_STATUS_SUCCESS); *ppCurrent = current_ptr; return Status; } /* Certificate Policy is a SEQ of Policy Information. Policy information is Seq of {Policy Identifier, Policy Qualifier [optional] } Policy Identifier is a Object ID Policy Qualifier Info is Seq {PolicyqualifierId, qualifier } PolicyqualifierId is a known Object id Qualifier is a CHOICE{cPSuri, UserNotice} we only support cpSuri which is a IA5string */ static STATUS ParseCertificatePolicy(UINT8 **ppCurrent, UINT8 *pEnd, SessMgrDataBuffer *CertificatePolicy) { UINT8 *current_ptr = *ppCurrent; UINT32 length; UINT8* end_of_PolicyInformation = NULL; UINT32 temp; STATUS Status; UINT8 EncodingBytes; do{ /* Certificate Policy is a SEQ of Policy Information */ Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; end_of_PolicyInformation = current_ptr + length; while(current_ptr < end_of_PolicyInformation){ /* Policy information is Seq of {Policy Identifier, Policy Qualifier [optional] */ Status = ParseIdAndLength(¤t_ptr, end_of_PolicyInformation, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; // Policy Identifier is a Object ID Status = ParseOID(¤t_ptr, end_of_PolicyInformation, &temp, &CertificatePolicyOid[0][0], sizeof(CertificatePolicyOid)/sizeof(CertificatePolicyOid[0]), sizeof(CertificatePolicyOid[0])); if(Status != X509_STATUS_SUCCESS) break; if(temp >= Max_Certificatepolicy){ /* this is a unsupported policy. Ignore */ current_ptr+= length; continue; } /* We have a supported policy !!!! */ // Next Field: PolicyQualifiers PolicyQualifiers is a SEQ of Policy Qualifier Info. However, we only support one Policy Qualifier Info Status = ParseIdAndLength(¤t_ptr, end_of_PolicyInformation, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; Status = ParseIdAndLength(¤t_ptr, end_of_PolicyInformation, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; Status = ParseOID(¤t_ptr, end_of_PolicyInformation, &temp, &CertificatePolicyQualifierIdOid[0][0], sizeof(CertificatePolicyQualifierIdOid)/sizeof(CertificatePolicyQualifierIdOid[0]), sizeof(CertificatePolicyQualifierIdOid[0])); if(Status != X509_STATUS_SUCCESS) break; if(temp >= Max_Certificatepolicy){ Status = X509_STATUS_ENCODING_ERROR; break; } // we have a supported policy qualifier id. Qualifier can be a CHOICE{cPSuri, UserNotice}. Note : Parser only supports cPSuri Status = ParseIdAndLength(¤t_ptr, end_of_PolicyInformation, DER_ENCODING_IA5_STRING_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; CertificatePolicy->buffer = current_ptr; CertificatePolicy->length = length; current_ptr +=length; } }while(0); DBG_ASSERT(Status == X509_STATUS_SUCCESS); *ppCurrent = current_ptr; return Status; } #endif static STATUS ParseSubjectPublicKeyInfo(UINT8 **ppCurrent, UINT8 *pEnd, UINT8 **pworkbuffer, SessMgrCertificateFields* certificateFields) { UINT8 *current_ptr = *ppCurrent; UINT8 *workbuffer_ptr = *pworkbuffer; UINT32 length; SessMgrEllipticCurveParameter params = unknownParameter; STATUS Status; UINT8 EncodingBytes; SessMgrDataBuffer* Key; SessMgrPublicKeyAlgoType* KeyType; /* Subject public key consist of a Algo Identifier and a BIT String containing the actual key */ Key = &certificateFields->subjectPublicKey; KeyType = &certificateFields->algorithmIdentifierForSubjectPublicKey; do{ Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; /* We first get the algo identifier */ Status = ParseAlgoIdentifier(¤t_ptr, pEnd, (UINT32*)KeyType, PublicKey_algo, ¶ms); if(Status != X509_STATUS_SUCCESS) break; /* Next is the Subject Public Key. It is a BIT string. */ Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_BIT_STRING_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; /* current_ptr is over the number of padding bits for the BIT string . We expect this to always be zero since we are not dealing with bit level data */ if(*current_ptr != 0x00){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } current_ptr++; certificateFields->EncodedSubjectPublicKey.buffer = current_ptr; certificateFields->EncodedSubjectPublicKey.length = length - 1; switch(*KeyType) { case X509_ecdsaPublicKey: // for ecdsa we know the length should be 66 bytes. if(length != 66){ Status = X509_STATUS_ENCODING_ERROR; break; } Key->buffer = workbuffer_ptr; Key->length = sizeof(SessMgrEcdsaPublicKey); workbuffer_ptr += sizeof(SessMgrEcdsaPublicKey); Status = ParseEcdsaPublicKey(¤t_ptr, pEnd, (SessMgrEcdsaPublicKey * )(Key->buffer), (SessMgrEllipticCurveParameter)params); break; case X509_intel_sigma_epidGroupPublicKey_epid11: Key->buffer = workbuffer_ptr; Key->length = sizeof(SessMgrEpidGroupPublicKey); workbuffer_ptr += sizeof(SessMgrEpidGroupPublicKey); Status = ParseEpidPublicKey(¤t_ptr, pEnd, (SessMgrEpidGroupPublicKey * )(Key->buffer)); #ifdef PRINT PrintEpidKey((void *)Key->buffer); #endif break; case X509_rsaPublicKey: Key->buffer = workbuffer_ptr; Key->length = sizeof(SessMgrRsaKey); workbuffer_ptr += sizeof(SessMgrRsaKey); Status = ParseRsaPublicKey(¤t_ptr, pEnd, (SessMgrRsaKey * )(Key->buffer)); break; default: DBG_ASSERT(0); } }while(0); if(Status == X509_STATUS_SUCCESS){ *pworkbuffer = workbuffer_ptr; *ppCurrent = current_ptr; } DBG_ASSERT(Status == X509_STATUS_SUCCESS); return Status; } static STATUS ParseRsaPublicKey(UINT8 **ppCurrent, UINT8 *pEnd, SessMgrRsaKey * RsaKey) { UINT8 *current_ptr = *ppCurrent; UINT32 length; STATUS Status; UINT8 EncodingBytes; /* The data portion of the BIT string contains a sequence of Seq { n , e} where n and e are integers */ Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } Status = ParseInteger(¤t_ptr, pEnd, &RsaKey->n, FALSE, FALSE, NULL); if(Status != X509_STATUS_SUCCESS) return X509_STATUS_ENCODING_ERROR; Status = ParseInteger(¤t_ptr, pEnd, &RsaKey->e, FALSE, FALSE, NULL); if(Status != X509_STATUS_SUCCESS) return X509_STATUS_ENCODING_ERROR; *ppCurrent = current_ptr; return X509_STATUS_SUCCESS; } static STATUS ParseEpidPublicKey(UINT8 **ppCurrent, UINT8 *pEnd, SessMgrEpidGroupPublicKey * EpidKey) { UINT8 *current_ptr = *ppCurrent; UINT32 length; SessMgrDataBuffer DataBuf; STATUS Status; UINT8 EncodingBytes; do{ /* We are expecting a sequence of {groupId [Integer], h1 [ECPoint], h2 [ECPoint], w [G2ECPoint] */ Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; /* Parse Integer */ Status = ParseInteger(¤t_ptr, pEnd, &DataBuf, FALSE, FALSE, NULL); if(Status != X509_STATUS_SUCCESS) break; Status = swapendian_memcpy((UINT8 *)&EpidKey->groupId, sizeof(UINT32), DataBuf.buffer, DataBuf.length); if(Status != X509_STATUS_SUCCESS) break; // Next Field : h1 [octet string] This is a ECPoint which is a octet string Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_OCTET_STRING_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; /* Make sure it has the first bype as 0x04 indicating key is uncompressed */ if(*current_ptr != 0x04){ Status = X509_STATUS_ENCODING_ERROR; break; } current_ptr++; EpidKey->h1x = current_ptr; EpidKey->h1y = current_ptr + 32; current_ptr += 64; // Next Field : h2 [octet string] This is a ECPoint which is a octet string Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_OCTET_STRING_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; /* Make sure it has the first bype as 0x04 indicating key is uncompressed */ if(*current_ptr != 0x04){ Status = X509_STATUS_ENCODING_ERROR; break; } current_ptr++; EpidKey->h2x = current_ptr; EpidKey->h2y = current_ptr + 32; current_ptr += 64; // Next Field : w [octet string] This is a ECPoint which is a octet string Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_OCTET_STRING_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; /* Make sure it has the first bype as 0x04 indicating key is uncompressed */ if(*current_ptr != 0x04){ Status = X509_STATUS_ENCODING_ERROR; break; } current_ptr++; EpidKey->wx0 = current_ptr; EpidKey->wx1 = current_ptr + 32; EpidKey->wx2 = current_ptr + 64; EpidKey->wy0 = current_ptr + 96; EpidKey->wy1 = current_ptr + 128; EpidKey->wy2 = current_ptr + 160; current_ptr += 192; }while(0); *ppCurrent = current_ptr; return Status; } static STATUS ParseEcdsaPublicKey(UINT8 **ppCurrent, UINT8 *pEnd, SessMgrEcdsaPublicKey * EcDsaKey, SessMgrEllipticCurveParameter params) { UINT8 *current_ptr = *ppCurrent; STATUS Status = X509_STATUS_SUCCESS; #ifdef X509_FOR_PSE_PR UNUSED(pEnd); #endif do{ /* Ecdsa Public Key is a Octet string. The SubjectPublicKey in the X509 definitiion is a bit string. We use the following logic to convert the Octet String as a bit string as per the Verifier Cert Spec defined by Xiaoyu. */ /* First octet of bit string is 0x04 indicating key is uncompressed. Note: The Key is in OCTET String form and we convert it to BIT STRING. Format : 0x04 | 64-bytes of keys */ if(*current_ptr != 0x04){ Status = X509_STATUS_ENCODING_ERROR; break; } current_ptr++; EcDsaKey->px = current_ptr; EcDsaKey->py = current_ptr + ECDSA_KEY_ELEMENT_SIZE; current_ptr += ECDSA_KEY_SIZE; EcDsaKey->eccParameter = params; }while(0); DBG_ASSERT(Status == X509_STATUS_SUCCESS); *ppCurrent = current_ptr; return Status; } /* We will never send an OID to the caller. All the OID are matched to a corresponding enumerated type and sent to the caller. */ static STATUS ParseOID(UINT8 **ppCurrent, UINT8 *pEnd, UINT32 *EnumVal, const UINT8 *OidList, UINT32 Max_Entries, UINT32 EntrySize ) { UINT8 *current_ptr = *ppCurrent; UINT32 length; UINT32 i; STATUS Status; UINT8 EncodingBytes; do{ Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_OBJECT_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; /* We have a encoded list of hard coded OIDs that we support in an array. Just compare the OID with that array. */ for(i = 0;i< Max_Entries; i++) { // // current_ptr could be out of bounds (speculatively) // and looping one too many times (also speculatively) // would be bad // sgx_lfence(); if(memcmp(current_ptr, OidList, length) == 0){ /* We found a match. i is the algorithm number that the caller is looking for */ *EnumVal= i; Status = X509_STATUS_SUCCESS; break; } OidList+=EntrySize; } if(i >= Max_Entries) { /* Never found a match */ *EnumVal= i; Status = X509_STATUS_UNKNOWN_OID; } current_ptr += length; }while(0); DBG_ASSERT(Status == X509_STATUS_SUCCESS || Status == X509_STATUS_UNKNOWN_OID); *ppCurrent = current_ptr; return Status; } static STATUS ParseSignatureValue(UINT8 **ppCurrent, UINT8 *pEnd, UINT8 **pworkbuffer, UINT32 WorkBufferSize, SessMgrDataBuffer *SignatureValueBuf, UINT8 SignatureAlgoId) { UINT8 *current_ptr = *ppCurrent; UINT32 length; UINT8 *workbuffer_ptr; STATUS Status; SessMgrDataBuffer DataBuf; UINT8 EncodingBytes; #ifdef X509_FOR_PSE_PR UNUSED(WorkBufferSize); #endif workbuffer_ptr = *pworkbuffer; do{ /* SignatureValue is a BIT string. The content within the BIT string is based on the signature algorithm */ Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_BIT_STRING_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS ) break; /* Next Byte is the number of padded. Because this is a signature, we expect the resulting value to be a multiple of 8 bits meaning no padding will be necessery */ // // current_ptr could be out of bounds (speculatively) // sgx_lfence(); if(*current_ptr != 0){ Status = X509_STATUS_ENCODING_ERROR; break; } // // the inc below requires this (i'm choosing to care about **read** buffer overflows) // if ((current_ptr + 1) >= pEnd) { Status = X509_STATUS_ENCODING_ERROR; break; } /* increment current pointer. decrement length to reflect size of actual bit string excluding the padding data*/ current_ptr++; length--; /* Rest of the buffer is parsed based on the algo */ switch(SignatureAlgoId) { /* ECDSA based sign algorithm */ case X509_ecdsa_with_SHA1: case X509_ecdsa_with_SHA256: /* For ECDSA, Signature value is represented as a SEQ {r, s} where r and s are integers. We use the workbuffer to store this */ /* First memset the buffers where keys will be copied to */ memset(workbuffer_ptr, 0, ECDSA_SIGNATURE_SIZE); Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; /* Parse R and S */ Status = ParseInteger(¤t_ptr, pEnd, &DataBuf, FALSE, FALSE, NULL); if(Status != X509_STATUS_SUCCESS || DataBuf.length > ECDSA_SIGNATURE_MAX_SIZE_R ){ Status = X509_STATUS_ENCODING_ERROR; break; } SESSMGR_MEMCPY_S(workbuffer_ptr + (ECDSA_SIGNATURE_MAX_SIZE_R - DataBuf.length), WorkBufferSize - (workbuffer_ptr - *pworkbuffer), DataBuf.buffer, DataBuf.length); workbuffer_ptr += ECDSA_SIGNATURE_MAX_SIZE_R; Status = ParseInteger(¤t_ptr, pEnd, &DataBuf, FALSE, FALSE, NULL); if(Status != X509_STATUS_SUCCESS || DataBuf.length > ECDSA_SIGNATURE_MAX_SIZE_S){ Status = X509_STATUS_ENCODING_ERROR; break; } SESSMGR_MEMCPY_S(workbuffer_ptr + (ECDSA_SIGNATURE_MAX_SIZE_S - DataBuf.length), WorkBufferSize - (workbuffer_ptr - *pworkbuffer), DataBuf.buffer, DataBuf.length); workbuffer_ptr += ECDSA_SIGNATURE_MAX_SIZE_S; break; /* All the RSA algorithms */ case X509_sha1withRSAEncryption: case X509_sha256WithRSAEncryption: /* Assuming here that all RSA algorithms just have a bit string signaure */ if(length > RSA_SIGNATURE_SIZE){ Status = X509_STATUS_ENCODING_ERROR; break; } memset(workbuffer_ptr, 0 , length); SESSMGR_MEMCPY_S(workbuffer_ptr, WorkBufferSize - (workbuffer_ptr - *pworkbuffer), current_ptr, length); workbuffer_ptr += length; current_ptr +=length; break; default: DBG_ASSERT(0); Status = X509_STATUS_INVALID_ARGS; } }while(0); if(Status == X509_STATUS_SUCCESS){ // Signature value has been parsed successfully. Store the offset in the workbuffer in the DataBuffer that will be passed back to the caller SignatureValueBuf->buffer = *pworkbuffer; // length is the amount of work buffer we have used up SignatureValueBuf->length = static_cast(workbuffer_ptr - *pworkbuffer); }else{ DBG_ASSERT(0); } *pworkbuffer = workbuffer_ptr; *ppCurrent = current_ptr; return Status; } static STATUS ParseAlgoIdentifier(UINT8 **ppCurrent, UINT8 *pEnd, UINT32* algoId, AlgorithmTypes Type, SessMgrEllipticCurveParameter *params) { /***** Format : Sequence Id + length + Object Id + length + OID value + parameters (optional) *****/ UINT8 *current_ptr = *ppCurrent; UINT32 length; UINT8 *end_of_sequence; STATUS Status; UINT8 EncodingBytes; Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } end_of_sequence = current_ptr + length; switch(Type){ case signature_algo: Status = ParseOID(¤t_ptr, pEnd, algoId, &HardCodedSignatureAlgorithmOid[0][0], static_cast(sizeof(HardCodedSignatureAlgorithmOid)/sizeof(HardCodedSignatureAlgorithmOid[0])), sizeof(HardCodedSignatureAlgorithmOid[0])); break; case PublicKey_algo: Status = ParseOID(¤t_ptr, pEnd, algoId, &HardCodedPublicKeyAlgorithmOid[0][0], static_cast(sizeof(HardCodedPublicKeyAlgorithmOid)/sizeof(HardCodedPublicKeyAlgorithmOid[0])), sizeof(HardCodedPublicKeyAlgorithmOid[0])); break; case Hash_algo: Status = ParseOID(¤t_ptr, pEnd, algoId, &HashAlgorithmOid[0][0], static_cast(sizeof(HashAlgorithmOid)/sizeof(HashAlgorithmOid[0])), sizeof(HashAlgorithmOid[0])); break; default: DBG_ASSERT(0); } if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } if(current_ptr < end_of_sequence){ /* We have some parameters */ Status = ParseAlgoParameters(¤t_ptr, end_of_sequence, (UINT32 *)params); if(Status == X509_STATUS_UNSUPPORTED_PARAMETER){ // As per spec, we just move on. current_ptr = end_of_sequence; *params = unknownParameter; Status = X509_STATUS_SUCCESS; } if(Status != X509_STATUS_SUCCESS) Status = X509_STATUS_ENCODING_ERROR; }else{ *params=unknownParameter; } *ppCurrent = current_ptr; return Status; } static STATUS ParseAlgoParameters(UINT8 **ppCurrent, UINT8 *pEnd, UINT32* param) { UINT8 *current_ptr = *ppCurrent; UINT32 length; UINT32 i; STATUS Status = X509_STATUS_SUCCESS; UINT8 EncodingBytes; if ((!param) || ((current_ptr + 1) >= pEnd)) { DBG_ASSERT(0); return X509_STATUS_INVALID_ARGS; } /* current_ptr is over the parameters. We currently support a NULL parameter or an object ID specifiying some algo specific data */ switch(*current_ptr) { case DER_ENCODING_NULL_ID: current_ptr++; /* NULL id is always followed by length 0x00 */ if(*current_ptr != 0) { DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } current_ptr++; *param = unknownParameter; break; case DER_ENCODING_OBJECT_ID: current_ptr++; /* Get Length of object identifier */ Status = DecodeLength(current_ptr, pEnd, &length, &EncodingBytes); if(Status != X509_STATUS_SUCCESS) break; current_ptr += EncodingBytes; /* This can describe the curve */ for(i = 0;i< MaxElipticCurveOidSupported; i++) { if(memcmp(current_ptr, EllipticCurveOid[i], length) == 0){ /* We found a match. "i+1" is the algorithm number that the caller is looking for */ *param = i; break; } } if(i >= MaxElipticCurveOidSupported) { /* Never found a match */ DBG_ASSERT(0); *param = unknownParameter; Status = X509_STATUS_ENCODING_ERROR; } current_ptr+=length; break; default: Status = X509_STATUS_UNSUPPORTED_PARAMETER; break; // unknown parameter. We ignore this. } *ppCurrent = current_ptr; return Status; } static STATUS ParseName(UINT8 **ppCurrent, UINT8 *pEnd, SessMgrX509Name* Name) { UINT8* end_of_sequence = NULL; UINT8 *current_ptr = *ppCurrent; UINT32 length; STATUS Status; UINT8 EncodingBytes; UINT32 NameType; memset(Name, 0, sizeof(SessMgrX509Name)); do{ Name->DistinguishedName = (char *)current_ptr; /* Format : Sequence [ Set [ Sequence [ OID Value] ] ] */ Status = ParseIdAndLength(¤t_ptr, pEnd, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; Name->DistinguishedNameSize = length + EncodingBytes + 1; end_of_sequence = current_ptr + length; /* Because Set can have variable number of data underneath it, we need to cap it using the length of the sequence We will use variable End_of_set and loop through until end is reached. */ while(current_ptr < end_of_sequence){ Status = ParseIdAndLength(¤t_ptr, end_of_sequence, DER_ENCODING_SET_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; Status = ParseIdAndLength(¤t_ptr, end_of_sequence, DER_ENCODING_SEQUENCE_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS) break; /* Expected value : Attribute type (OID) and value (Can be anything) */ Status = ParseOID(¤t_ptr, end_of_sequence, &NameType, &HardCodedNameOid[0][0], static_cast(sizeof(HardCodedNameOid)/sizeof(HardCodedNameOid[0])), sizeof(HardCodedNameOid[0])); // We might have some cases where we are getting an Unknown OID which we just ignore. if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_UNKNOWN_OID){ DBG_ASSERT(0); break; } /* Value can be any type. Mostly some form of ascii string */ Status = ParseIdAndLength(¤t_ptr, end_of_sequence, DER_ENCODING_UTF8_ID, &length, &EncodingBytes, TRUE); if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_NOT_FOUND) break; if(Status == X509_STATUS_NOT_FOUND){ // Has to be one of UTF8 or PRINTABLE_STRING or IA5 Status = ParseIdAndLength(¤t_ptr, end_of_sequence, DER_ENCODING_PRINTABLE_STRING_ID, &length, &EncodingBytes, TRUE); if(Status != X509_STATUS_SUCCESS && Status != X509_STATUS_NOT_FOUND) break; if(Status == X509_STATUS_NOT_FOUND){ Status = ParseIdAndLength(¤t_ptr, end_of_sequence, DER_ENCODING_IA5_STRING_ID, &length, &EncodingBytes, FALSE); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); break; } } } switch(NameType) { case commonName: Name->commonName = (char *)current_ptr; Name->commonNameSize = length; break; case organization: Name->organization = (char *)current_ptr; Name->organizationSize = length; break; case country: Name->country = (char *)current_ptr; Name->countrySize = length; break; case locality: Name->locality = (char *)current_ptr; Name->localitySize = length; break; case state: Name->state = (char *)current_ptr; Name->stateSize = length; break; case organizationUnit: Name->organizationUnit = (char *)current_ptr; Name->organizationUnitSize = length; break; case UserId: Name->UserId = (char *)current_ptr; Name->UserIdSize = length; break; default: // Dont support this NameType. Just continue. break; } current_ptr += length; } }while(0); *ppCurrent = current_ptr; return Status; } #define DecodeTime_FourBytes(current_ptr) ((1000*( (*current_ptr) - 0x30)) + \ (100*( *(current_ptr+1) - 0x30)) + \ (10*( *(current_ptr+2) - 0x30)) + \ (*(current_ptr + 3) - 0x30)) \ #define DecodeTime_TwoBytes(current_ptr) ((10*( (*current_ptr) - 0x30)) + \ (*(current_ptr + 1) - 0x30)) \ #if 0 UINT32 DecodeTime(UINT8 *current_ptr, UINT8 length) { UINT32 value = 0; UINT32 digit; int i; for(i=0; idate.data = 0; DateTime->time.data = 0; /* Check whether current_ptr is pointing to UTC time or Generalized time*/ if(*current_ptr == DER_ENCODING_UTC_TIME_ID){ /* Year format is YY */ isUTC = TRUE; } else if(*current_ptr == DER_ENCODING_GENERALIZED_TIME_ID){ /* Year format is YYYY */ isUTC = FALSE; }else{ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } current_ptr++; /* get Length of the time */ Status = DecodeLength(current_ptr, pEnd, &length, &EncodingBytes); if(Status != X509_STATUS_SUCCESS){ DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } current_ptr += EncodingBytes; date_start_ptr = current_ptr; /* Only difference between generalized and UTC is number of digits for the years. UTC has 2 digits and Generalized has 4 digits */ if(!isUTC){ DateTime->date.yearMonthDay.year = static_cast(DecodeTime_FourBytes(current_ptr)); current_ptr += 4; }else{ // per rfc3280, if XX >= 50 then 19XX, otherwise 20XX. However, here we always use 20XX. DateTime->date.yearMonthDay.year = static_cast(DecodeTime_TwoBytes(current_ptr)); current_ptr += 2; DateTime->date.yearMonthDay.year = static_cast(DateTime->date.yearMonthDay.year + 2000); } /* The next 8 bytes are common for both UTC and Generalized time */ DateTime->date.yearMonthDay.month = DecodeTime_TwoBytes(current_ptr) & 0xF; current_ptr += 2; DateTime->date.yearMonthDay.day = DecodeTime_TwoBytes(current_ptr) & 0x3F; current_ptr += 2; DateTime->time.hourMinuteSecond.hour = DecodeTime_TwoBytes(current_ptr) & 0x3F; current_ptr += 2; DateTime->time.hourMinuteSecond.minute = DecodeTime_TwoBytes(current_ptr) & 0x3F; current_ptr += 2; /* Next character can be a numeral(incase we have seconds), +, - or Z */ if(isdigit(*current_ptr)){ /* we have second. Get the next two bytes as seconds. */ DateTime->time.hourMinuteSecond.second = DecodeTime_TwoBytes(current_ptr) & 0x3F; current_ptr += 2; } /* Next character has to be +, - or Z */ switch(*current_ptr) { case '-': DateTime->time.hourMinuteSecond.timezone_is_neg = true; /* fallthrough */ case '+': current_ptr++; DateTime->time.hourMinuteSecond.timezone_hour = DecodeTime_TwoBytes(current_ptr) & 0x3F; current_ptr += 2; DateTime->time.hourMinuteSecond.timezone_minute = DecodeTime_TwoBytes(current_ptr) & 0x3F; current_ptr += 2; break; case 'Z': /* End of time */ current_ptr++; break; default: DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } // ensure the length parsed is equal to the specified length if ((current_ptr - date_start_ptr) != length) { DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } // date sanity check if (DateTime->date.yearMonthDay.year < 2000 || DateTime->date.yearMonthDay.year >= 2137 || DateTime->date.yearMonthDay.month < 1 || DateTime->date.yearMonthDay.month > 12 || DateTime->date.yearMonthDay.day < 1 || DateTime->date.yearMonthDay.day > 31 || DateTime->time.hourMinuteSecond.hour > 24 || DateTime->time.hourMinuteSecond.minute > 60 || DateTime->time.hourMinuteSecond.second > 60 || DateTime->time.hourMinuteSecond.timezone_hour > 24 || DateTime->time.hourMinuteSecond.timezone_minute > 60) { DBG_ASSERT(0); return X509_STATUS_ENCODING_ERROR; } *ppCurrent = current_ptr; return X509_STATUS_SUCCESS; } /** \param[in] Buffer Buffer which is pointing to a length field in Asn DER encoded form \param[out] Length Length of the Asn DER encoded type in bytes \param[out] EncodingBytes Number of bytes used for the encoding of the length \retval X509_STATUS_SUCCESS \retval STATUS_INVALID_PARAMS THere was some error in parsing the ASN Der encoded buffer. @brief Return length of the ASN DER encoded type */ static STATUS DecodeLength(UINT8* Buffer, UINT8* BufferEnd, UINT32* Length, UINT8* EncodingBytes) { // // some spectre vulnerabilities in here // // stop speculation here to make sure Buffer[0] is in bounds // before reading // sgx_lfence(); if(Buffer[0] < 0x81){ /* length is only one byte */ *Length = Buffer[0]; *EncodingBytes = 1; } else if ((Buffer[0] == 0x81) && (&Buffer[1] < BufferEnd)) { // // lfence for Buffer[1] // sgx_lfence(); /* length is two bytes */ *Length = Buffer[1]; *EncodingBytes = 2; } else if ((Buffer[0] == 0x82) && (&Buffer[2] < BufferEnd)) { // // lfence for Buffer[2] // sgx_lfence(); /* length is 3 bytes */ *Length = (Buffer[1] << 8) + Buffer[2]; *EncodingBytes = 3; }else{ return X509_STATUS_ENCODING_ERROR; } // // check for unsigned integer overflow // UINT32 tempLength = *EncodingBytes + *Length; if (((size_t) Buffer + tempLength) >= tempLength) { if ((Buffer + tempLength) > BufferEnd) { return X509_STATUS_ENCODING_ERROR; } // // X.509 cert includes length fields that // an attacker can control // sgx_lfence(); } else { return X509_STATUS_ENCODING_ERROR; } return X509_STATUS_SUCCESS; } static void SwapEndian(UINT8* ptr, int length) { UINT8 temp; int i; for(i=0;iWrite(gSessmgrCtx.StorageProtocol, SESS_MGR_TRUSTED_TIME_NVAR_NAME, &Blob, &StorageFlags, sizeof(gSessmgrCtx.TrustedTime), &gSessmgrCtx.TrustedTime, 0); if(Status != STATUS_SUCCESS){ DBG_ASSERT(0); return Status; } return STATUS_SUCCESS; } STATUS ConvertTimeToNtp(SessMgrDateTime Time, NTP_TIMESTAMP *NtpTime) { STATUS Status; RTC_TIME RtcTime; TZ_DST TimeZoneAndDST; memset(&RtcTime, 0, sizeof(RTC_TIME)); memset(NtpTime, 0, sizeof(NTP_TIMESTAMP)); if (Time.date.yearMonthDay.year < 2000 || Time.date.yearMonthDay.year >= 2137) return STATUS_FAILURE; RtcTime.Year = (UINT8)(Time.date.yearMonthDay.year - 2000); RtcTime.Month = (UINT8)Time.date.yearMonthDay.month; RtcTime.Day = (UINT8)Time.date.yearMonthDay.day; RtcTime.Hour = (UINT8)Time.time.hourMinuteSecond.hour; RtcTime.Min = (UINT8)Time.time.hourMinuteSecond.minute; RtcTime.Sec = (UINT8)Time.time.hourMinuteSecond.second; RtcTime.Status.DM = 1; // DM = 1 => Binary data representation. RtcTime.Status.HOURFORM = 1; // HOURFORM = 1 => 24 hour TimeZoneAndDST.DST = 0; TimeZoneAndDST.HalfHourAdjust = Time.time.hourMinuteSecond.timezone_minute ? 1 : 0; TimeZoneAndDST.RSVD = 0; TimeZoneAndDST.TimeZone = (Time.time.hourMinuteSecond.timezone_is_neg ? TIME_ZONE_OFFSET_SIGN_MASK : 0) | Time.time.hourMinuteSecond.timezone_hour; Status = PrtcLocalToNtp(&RtcTime, TimeZoneAndDST, NtpTime); if(Status != STATUS_SUCCESS){ DBG_ASSERT(0); return Status; } // PrtcLocalToNtp has a starting offset of year 1900 and will overflow in year 2037 // Shift the offset to start at year 2000, so that we can operate in years 2000-2137 NtpTime->Seconds -= SECONDS_FROM_1900_TO_2000; return STATUS_SUCCESS; } #endif // #ifndef X509_FOR_PSE_PR