/// <summary> /// Checks whether the CMAC is valid and correct or not /// </summary> /// <param name="m3Received">Message 3 from client</param> /// <param name="m3GaStr">Message 3 ECC Ga String</param> /// <param name="sigmaSequenceCheck">Service Provider Sequence (State) Check</param> /// <returns>Boolean whether the CMAC was correct</returns> private bool CheckCmac(M3RequestMessage m3Received, String m3GaStr) { log.Debug("CheckCmac(.) started."); if (m3Received == null || m3GaStr == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } // The received CMAC should match a computed CMAC string m3PsSecPropStr = bMessage.BaToBlobStr(m3Received.reqM3Body.secProperty); string m3QuoteStr = bMessage.BaToBlobStr(m3Received.reqM3Body.quote); string m3CmacBlobStr = m3GaStr + m3PsSecPropStr + m3QuoteStr; byte[] m3CmacBlobBa = bMessage.BlobStrToBa(m3CmacBlobStr); // Calculate the m3 CMAC value byte[] m3CmacSmk = cmacAES.Value(sigmaSequenceCheck.currentSmk, m3CmacBlobBa); string calcM3CmacSmkStr = bMessage.BaToBlobStr(m3CmacSmk); string m3aesCmacStr = bMessage.BaToBlobStr(m3Received.reqM3Body.aesCmac); bool cmacCorrect = String.Equals(m3aesCmacStr, calcM3CmacSmkStr, StringComparison.Ordinal); if (!cmacCorrect) { log.Debug("!!! CMAC Check Failure !!!"); } log.DebugFormat("CheckCmac(.) returning {0}.", cmacCorrect); return(cmacCorrect); }
//Build an M3 request public void buildM3Request(out M3RequestMessage m3Req) { string request = Constants.Request; var m3Msg = new M3RequestMessage(request); var m3Body = new ReqMsg3Body(); m3Body.aesCmac = Constants.sampleCmacsmk; m3Body.gaX = Constants.sampleGaXba; m3Body.gaY = Constants.sampleGaYba; m3Body.secProperty = Constants.sampleM3secProp; m3Body.quote = Constants.sampleQuote; //load a complete message object m3Msg.reqM3Body = m3Body; m3Req = m3Msg; return; }
/// <summary> /// Checks whether the received Message is the correct length or not /// Compare the reported length against the actual length (base16 string length/2) /// </summary> /// <param name="m3Received">Message 3 from client</param> /// <param name="m3ReceivedString">Message 3 as a string</param> /// <returns>Boolean of whether message length is correct</returns> private bool CheckMessageLength(M3RequestMessage m3Received, String m3ReceivedString) { log.Debug("CheckMessageLength(.) started."); if (m3Received == null || m3ReceivedString == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } bool correctLength = BitConverter.ToUInt32(m3Received.reqHeader.msgLength, 0) == (m3ReceivedString.Length / 2); if (!correctLength) { log.Debug("!!! Length Check Error !!!"); } log.DebugFormat("CheckMessageLength(.) returning {0}.", correctLength); return(correctLength); }
/// <summary> /// Process Message 3 and Build Message 4 /// </summary> /// <param name="Request">Message 3 Client Response</param> /// <param name="sigmaSequenceCheck">Service Provider Sequence (State) Check</param> /// <returns>Message 4 Repsonse to Client</returns> public async Task <M4ResponseMessage> ProcessMessage3(HttpRequestMessage Request, SpSequenceCheck sigmaSequenceChk) { log.Debug("ProcessMessage3(.) started."); if (Request == null || sigmaSequenceChk == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.PreconditionFailed); options.LogThrownException(e); throw e; } sigmaSequenceCheck = sigmaSequenceChk; // Update Client state if (!sigmaSequenceCheck.UpdateState(Constants.SequenceState.Msg3)) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.PreconditionFailed); options.LogThrownException(e); throw e; } var result = Request.Content.ReadAsStringAsync(); string jsonMsg3Request = result.Result; M3RequestMessage m3Received = new M3RequestMessage(); try { // Attempt to parse request in message 3 m3Received = JsonConvert.DeserializeObject <M3RequestMessage>(jsonMsg3Request); } catch (Exception msg3ReqError) { log.DebugFormat("******* Message 3 JSON Content Error: {0}", msg3ReqError); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } if (m3Received == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.PreconditionFailed); options.LogThrownException(e); throw e; } // Check the nonce and the base16 encoded length of the inbound request string m3ReceivedString = m3Received.GetMsgString(); log.Info("******* Received M3 Request"); log.DebugFormat("{0}{1}", Request.Headers, jsonMsg3Request); log.DebugFormat("M3 Base16 Encoded String: {0}", m3ReceivedString); // If failed a check, throw an error // Getting to this point means there was a problem with the M3 content (including possible quote check failure). // Reset the state machine and return "Forbidden" // Check whether to use Nonce or not, and is valid bool nonceCheck = sigmaSequenceCheck.currentNonce.SequenceEqual(m3Received.reqHeader.nonce); if (!nonceCheck) { log.Debug("Invalid Message 3 Nonce"); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.Forbidden); options.LogThrownException(e); throw e; } // Check the message has the correct length bool lengthCheck = CheckMessageLength(m3Received, m3ReceivedString); if (!lengthCheck) { log.Debug("Invalid Message 3 Length"); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.Forbidden); options.LogThrownException(e); throw e; } // Check the M3 components & Ga byte[] m3Ga = new byte[m3Received.reqM3Body.gaX.Length + m3Received.reqM3Body.gaY.Length]; System.Buffer.BlockCopy(m3Received.reqM3Body.gaX, 0, m3Ga, 0, m3Received.reqM3Body.gaX.Length); System.Buffer.BlockCopy(m3Received.reqM3Body.gaY, 0, m3Ga, m3Received.reqM3Body.gaX.Length, m3Received.reqM3Body.gaY.Length); string m3GaStr = bMessage.BaToBlobStr(m3Ga); string currentGaStr = bMessage.BaToBlobStr(sigmaSequenceCheck.currentGa); // Check the ga is correct bool gaCheck = CheckGa(currentGaStr, m3GaStr); if (!gaCheck) { log.Debug("Invalid Message 3 ga"); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.Forbidden); options.LogThrownException(e); throw e; } // Check that the CMAC is correct bool cmacCheck = CheckCmac(m3Received, m3GaStr); if (!cmacCheck) { log.Debug("Invalid Message 3 CMAC"); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.Forbidden); options.LogThrownException(e); throw e; } string m3QuoteStr = bMessage.BaToBlobStr(m3Received.reqM3Body.quote); if (String.IsNullOrEmpty(m3QuoteStr)) { log.Debug("Message 3 Quote is NULL"); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.Forbidden); options.LogThrownException(e); throw e; } // Get MRSIGNER and ISVPRODID from the Quote to find the correct enclave type from Enclave.json string MRSIGNERString = m3QuoteStr.Substring((int)Constants.QuoteInfo.MRSIGNEROffset * 2, Constants.QuoteInfo.MRSIGNERSize * 2); // MR Enclave String from Quote log.InfoFormat("Quote came from enclave with MRSIGNER: {0}", MRSIGNERString); ushort ISVPRODID = (ushort)BitConverter.ToInt16(bMessage.BlobStrToBa(m3QuoteStr.Substring((int)Constants.QuoteInfo.ISVPRODIDOffset * 2, 4)), 0); log.DebugFormat("ISVPRODID:\t{0}", ISVPRODID); sigmaSequenceCheck.SetEnclaveType(MRSIGNERString, ISVPRODID); bool quoteOk = CheckQuoteOk(m3Received, m3QuoteStr, currentGaStr); if (!quoteOk) { log.Debug("Invalid Message 3 Quote"); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.Forbidden); options.LogThrownException(e); throw e; } // Check whether using Debug quote or not bool debugCheck = CheckDebug(m3QuoteStr); if (!debugCheck) { log.Debug("Invalid Message 3 - Using Debug or Production quote when opposite is expected"); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.Forbidden); options.LogThrownException(e); throw e; } // Check Signature Type bool sigTypeCheck = CheckSignatureType(m3QuoteStr); if (!sigTypeCheck) { log.Debug("Invalid Message 3 Signature"); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.Forbidden); options.LogThrownException(e); throw e; } // Check ISV SVN bool isvSVNCheck = CheckISVSVN(m3QuoteStr); if (!isvSVNCheck) { log.Debug("Invalid Message 3 ISV SVN"); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.Forbidden); options.LogThrownException(e); throw e; } // Successful // At this point, we know that a valid message 3 was received and we can send the quote to IAS. // Complete the state transition. M4ResponseMessage msg4Respsonse = new M4ResponseMessage(); try { sigmaSequenceCheck.m3Received = true; Msg4Builder msgProcessor = new Msg4Builder(); msg4Respsonse = await msgProcessor.BuildMessage4(sigmaSequenceCheck, m3Received); } catch (HttpRequestException re) { options.LogCaughtErrorException(re); log.Debug("Failed to create Message 4. " + re.Message); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } log.Debug("ProcessMessage3(.) returning."); return(msg4Respsonse); }
/// <summary> /// Check that the Quote is correct /// </summary> /// <param name="m3Received">Message 3 from client</param> /// <param name="m3QuoteStr">Message 3 quote string</param> /// <param name="sigmaSequenceCheck">Service Provider Sequence (State) Check</param> /// <param name="currentGaStr">Message 3 ECC Ga String</param> /// <returns>Boolean whether the provided quote was valid</returns> private bool CheckQuoteOk(M3RequestMessage m3Received, String m3QuoteStr, String currentGaStr) { log.Debug("CheckQuoteOk(.) started."); if (m3Received == null || m3QuoteStr == null || currentGaStr == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // The Quote should match a pre-provisioned enclave measurement, and the quote signature should be valid. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Ensure quote is within defined range bool qSizeLimits = (m3Received.reqM3Body.quote.Length > MsgFieldLimits.UINT32_MINIMUM_QUOTE_SIZE && m3Received.reqM3Body.quote.Length < MsgFieldLimits.UINT32_PRACTICAL_SIZE_LIMIT); if (!qSizeLimits) { log.Debug("Quote size is invalid: " + m3Received.reqM3Body.quote.Length + ". Expected range: " + MsgFieldLimits.UINT32_MINIMUM_QUOTE_SIZE + " to " + MsgFieldLimits.UINT32_PRACTICAL_SIZE_LIMIT); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.BadRequest); options.LogThrownException(e); throw e; } // Look at the enclave measurement stored in Constants.QuoteInfo from the settings file. // Assume the measurement is stored as a base 16 encoded string, but verify the input. bool qMeasurementMatch = false; bool qMeasurementRequired = false; string mrEncString = m3QuoteStr.Substring((int)Constants.QuoteInfo.mrEncOffset * 2, Constants.QuoteInfo.mrEncSize * 2); // MR Enclave String from Quote log.DebugFormat("m3 quote measurement: {0}", mrEncString); log.DebugFormat("recorded quote measurement: {0}", sigmaSequenceCheck.enclaveType.MRENCLAVE); // Check the stored quote string from user input by attempting to convert to byte array form. if (!String.IsNullOrEmpty(sigmaSequenceCheck.enclaveType.MRENCLAVE)) { qMeasurementRequired = true; byte[] mrEnclaveTestBa = bMessage.BlobStrToBa(sigmaSequenceCheck.enclaveType.MRENCLAVE); // Convert the MRenclave byte array back to string form to ensure uniform base16 Encoding from user input sigmaSequenceCheck.enclaveType.MRENCLAVE = bMessage.BaToBlobStr(mrEnclaveTestBa); qMeasurementMatch = String.Equals(mrEncString, sigmaSequenceCheck.enclaveType.MRENCLAVE, StringComparison.Ordinal); log.DebugFormat("converted quote measurement: {0}", sigmaSequenceCheck.enclaveType.MRENCLAVE); log.DebugFormat("quote compare result: {0}", qMeasurementMatch); } // Derive the "VK" key using VK = AES-CMAC(KDK, 0x01||’VK’||0x00||0x80 ||0x00) byte[] VK = bMessage.KeyLabelToKey(Constants.VK, sigmaSequenceCheck.currentKDK); // Compute a SHA-256 hash of (ga||gb||VK). string currentGbStr = bMessage.BaToBlobStr(sigmaSequenceCheck.currentGb); string vKstring = bMessage.BaToBlobStr(VK); string gabvkStr = currentGaStr + currentGbStr + vKstring; byte[] gabvkBa = bMessage.BlobStrToBa(gabvkStr); byte[] gabvkHashBa = null; // Klocwork issues related to qRptSha... // Klocwork thinks tha this object is dereferenced before null check. However, if it is // null, then an exception is thrown below, outside of our try-catch-finally block, and // all other paths go through the try-catch-finally, where it gets properly disposed. // It appears that klocwork somehow became confused in this code flow. using (SHA256Cng qRptSha = new SHA256Cng()) { if (qRptSha == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } try { gabvkHashBa = qRptSha.ComputeHash(gabvkBa); string gabvkHashString = bMessage.BaToBlobStr(gabvkHashBa); // Retrieve the non-zero (first 32) Bytes of the "REPORTDATA" field from the quote string rptDataString = m3QuoteStr.Substring((int)Constants.QuoteInfo.reportDataOffset * 2, Constants.QuoteInfo.reportDataSize * 2); bool qHashMatch = false; log.DebugFormat("m3 (ga||gb||VK) hash: {0}", rptDataString); log.DebugFormat("computed (ga||gb||VK) hash: {0}", gabvkHashString); qHashMatch = String.Equals(rptDataString, gabvkHashString, StringComparison.Ordinal); log.DebugFormat("hash compare result: {0}", qHashMatch); // Evaluate the quote check conditions if (qSizeLimits && qHashMatch && (qMeasurementMatch || !qMeasurementRequired)) { log.Debug("CheckQuoteOk(.) returning true."); return(true); } log.Debug("!!! Quote Check Failure !!!"); PrintQuote(m3QuoteStr); } catch (Exception e) { options.LogCaughtErrorException(e); log.Debug("Error checking Quote. " + e.Message); HttpResponseException newException = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(newException); throw newException; } } log.Debug("CheckQuoteOk(.) returning false."); return(false); }
/// <summary> /// Retrieves the Client ID from the request /// </summary> /// <param name="request">Provisioning request</param> /// <returns>String ID of the requesting client</returns> public static string GetClientID(HttpRequestMessage request, string requestType) { log.DebugFormat("GetClientID({0}) started.", requestType); // Note: requestType==null case is handled in code farther down. if (request == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.BadRequest); options.LogThrownException(e); throw e; } // Need to get Client state (id) information var result = request.Content.ReadAsStringAsync(); string jsonMsgRequest = result.Result; string ID = null; try { switch (requestType) { case "Provision": ProvisionRequestMessage pReceived = JsonConvert.DeserializeObject <ProvisionRequestMessage>(jsonMsgRequest); ID = BitConverter.ToString(pReceived.reqHeader.nonce); break; case "Msg0": M0RequestMessage m0Received = JsonConvert.DeserializeObject <M0RequestMessage>(jsonMsgRequest); ID = BitConverter.ToString(m0Received.reqHeader.nonce); break; case "Msg1": M1RequestMessage m1Received = JsonConvert.DeserializeObject <M1RequestMessage>(jsonMsgRequest); ID = BitConverter.ToString(m1Received.reqHeader.nonce); break; case "Msg3": M3RequestMessage m3Received = JsonConvert.DeserializeObject <M3RequestMessage>(jsonMsgRequest); ID = BitConverter.ToString(m3Received.reqHeader.nonce); break; default: { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } } } catch (Exception msgError) { options.LogCaughtErrorException(msgError); log.DebugFormat("******* Message JSON Content Error: {0}\n", msgError); HttpResponseException newException = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(newException); throw newException; } if (ID != null) { ID = ID.Replace("-", "").Trim(); } else { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } log.DebugFormat("GetClientID({0}) returning.", requestType); return(ID); }