/// <summary> /// Process Message 1 and create Message 2 /// </summary> /// <param name="Request">Client Provisioning request</param> /// <param name="sigmaSequenceCheck">Service Provider Sequence (State) Check</param> /// <returns>Message 2 content</returns> public M2ResponseMessage ProcessMessage1(HttpRequestMessage Request, SpSequenceCheck sigmaSequenceCheck) { log.Debug("ProcessMessage1(.) started."); if (Request == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.BadRequest); options.LogThrownException(e); throw e; } if (sigmaSequenceCheck == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } // Check and parse Message 1 M1RequestMessage m1Received = VerifyMessage1IsValid(Request, sigmaSequenceCheck); CalculateDiffieHellmanExchange(sigmaSequenceCheck, m1Received); // Successful process of Message 1 // Create Message 2 Msg2Builder msgProcessor = new Msg2Builder(); M2ResponseMessage msg2Response = msgProcessor.BuildMessage2(sigmaSequenceCheck, m1Received, gidBaString, gbXLittleEndian, gbYLittleEndian, sigSPXLittleEndian, sigSPYLittleEndian, cMACsmk); log.Debug("ProcessMessage1(.) returning."); return(msg2Response); }
/// <summary> /// Build Message 2 response for Non-IAS Connection; Used for Debug only /// </summary> /// <returns>Message 2 response to client</returns> private void BuildNonIasMessage2(SpSequenceCheck sigmaSequenceCheck) { // NOTE: This debugging path bypasses the IAS connection and ignores the SigRL. // This path is intended for temporary testing of the sigma messaging with the SGX client. // // Production Server MUST use the IAS connection for a secure check of the enclave. // Update the M2 response fields and add a null SigRL for this debug path. // m2 = gb||SPID||Quote Type||SigSP(gb||ga)||MACsmk(gb||SPID||Type||SigSP(gb||ga));SigRL log.Debug("BuildNonIasMessage2(.) started."); if (sigmaSequenceCheck == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } bMessage.buildM2Response(out msg2); msg2.respHeader.respStatus = BitConverter.GetBytes((UInt32)enStatusCodes.raErrIasGetSuccess); if (SpStartup.iasConnectionMgr.SPID.Length != 16) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.PreconditionFailed); options.LogThrownException(e); throw e; } // We need endianness agreement between client and server for sending each message term msg2.respMsg2Body.gbX = _gbXLittleEndian; msg2.respMsg2Body.gbY = _gbYLittleEndian; msg2.respMsg2Body.spId = SpStartup.iasConnectionMgr.SPID; msg2.respMsg2Body.sigLinkType = Constants.sltype; msg2.respMsg2Body.kdfId = Constants.kdfId; msg2.respMsg2Body.sigSpX = _sigSPXLittleEndian; msg2.respMsg2Body.sigSpY = _sigSPYLittleEndian; msg2.respMsg2Body.cmacsmk = _cMACsmk; // Since this debug path does not include a dialog with the IAS server, // there is no sigRL to retrieve. // Build the sigRL message fields to reflect a sigRL of zero length. msg2.respMsg2Body.sigrlSize = MsgInitValues.DS_ZERO_BA4; msg2.respMsg2Body.sigRl = null; msg2.respHeader.msgLength = BitConverter.GetBytes((UInt32)msg2.GetMsgString().Length / 2); msg2.respHeader.sessionNonce = sigmaSequenceCheck.currentNonce; sigmaSequenceCheck.m1Received = true; string msg2JsonString = JsonConvert.SerializeObject(msg2); log.Info("*********** Msg1 Processing Complete - Sending Msg2"); log.DebugFormat("Msg2 JSON String: {0}", msg2JsonString); log.DebugFormat("Msg2 16bit encoded string: {0}", msg2.GetMsgString()); log.Debug("BuildNonIasMessage2(.) returning."); }
/// <summary> /// Build Message 2 /// </summary> /// <param name="sigmaSequenceCheck">Service Provider Sequence (State) Check</param> /// <param name="m1Received">Message 1</param> /// <returns>Message 2 Repsonse to Client</returns> public M2ResponseMessage BuildMessage2(SpSequenceCheck sigmaSequenceCheck, M1RequestMessage m1Received, String gidBaString, byte[] gbXLittleEndian, byte[] gbYLittleEndian, byte[] sigSPXLittleEndian, byte[] sigSPYLittleEndian, byte[] cMACsmk) { log.Debug("BuildMessage2(.) started."); if (sigmaSequenceCheck == null || m1Received == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } // Update Client state if (!sigmaSequenceCheck.UpdateState(Constants.SequenceState.Msg2)) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.PreconditionFailed); options.LogThrownException(e); throw e; } SetDiffieHellmanExchange(gidBaString, gbXLittleEndian, gbYLittleEndian, sigSPXLittleEndian, sigSPYLittleEndian, cMACsmk); // Check that Diffie Hellman values are initialized if (!initialized) { return(null); } log.Debug("Building message 2"); if (SpStartup.iasConnectionMgr.UseIAS) { // Connect to IAS for Signature Revocation List BuildIASMessage2(sigmaSequenceCheck); } else { // Simulate IAS // For testing the SGX client connection only, override the IAS connection settings by setting "UseIAS" to false. // This setting should be temporary, and once the SGX messaging is working, set "UseIAS" to true. BuildNonIasMessage2(sigmaSequenceCheck); } log.Debug("BuildMessage2(.) returning."); return(msg2); }
/// <summary> /// Process Message 0 /// </summary> /// <param name="Request">Client Provisioning request</param> /// <param name="sigmaSequenceCheck">Service Provider Sequence (State) Check</param> /// <returns>Message 0 response</returns> public M0ResponseMessage ProcessMessage0(HttpRequestMessage Request, SpSequenceCheck sigmaSequenceCheck) { log.Debug("ProcessMessage0(.) started."); if (Request == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.BadRequest); options.LogThrownException(e); throw e; } if (sigmaSequenceCheck == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } // Check and parse Message 0 M0RequestMessage m0Received = VerifyMessage0IsValid(Request, sigmaSequenceCheck); if (m0Received == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } M0ResponseMessage msg0Response = new M0ResponseMessage(); msg0Response.respHeader.sessionNonce = m0Received.reqHeader.nonce; // Successful process of Message 0 log.Debug("ProcessMessage0(.) returning."); return(msg0Response); }
/// <summary> /// Calculate values for Diffie Hellman values /// </summary> /// <param name="sigmaSequenceCheck">Service Provider Sequence (State) Check</param> /// <param name="m1Received">Message 1</param> private void CalculateDiffieHellmanExchange(SpSequenceCheck sigmaSequenceCheck, M1RequestMessage m1Received) { log.Debug("CalculateDiffieHellmanExchange(..) started."); if (sigmaSequenceCheck == null || m1Received == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } // (Intel Performance Primitives) IPP Wrapper ippApiWrapper ippWrapper = new ippApiWrapper(); // Process message 1 and build the message 2 response. // Capture the GID and Base16 encode the gid field per the IAS API specificaiton: // "{gid} = Base 16-encoded representation of QE EPID group ID encoded as a Little Endian integer". // NOTE: This conversion assumes that the GID was sent without modification in the "pltfrmGid" message field. byte[] gidBa = m1Received.reqM1Body.pltfrmGid; // Required that the GID is supplied with no conversions from the SGX client and reverse the byte order // to convert to Big Endian integer for use later in the routine when encoding as a Base 16 string. Array.Reverse(gidBa); gidBaString = bMessage.BaToBlobStr(gidBa); // In crypto terms, a = Alice (Client) and b = Bob (Service provider) // so ga = secret shared by client and gb = secret shared by service provide gaLittleEndianstr = m1Received.GetGaString(); // Received in Little Endian format gaXLittleEndianstr = ""; // ga = gaX|gaY -- take the first half of ga to get gaX if (gaLittleEndianstr != null) { gaXLittleEndianstr = gaLittleEndianstr.Substring(0, gaLittleEndianstr.Length / 2); } gaXLittleEndian = bMessage.BlobStrToBa(gaXLittleEndianstr); gaXBigEndian = bMessage.BlobStrToBa(gaXLittleEndianstr); Array.Reverse(gaXBigEndian, 0, gaXBigEndian.Length); gaXBigEndianstr = bMessage.BaToBlobStr(gaXBigEndian); gaYLittleEndianstr = ""; if (gaLittleEndianstr != null) { gaYLittleEndianstr = gaLittleEndianstr.Substring(gaLittleEndianstr.Length / 2); } gaYLittleEndian = bMessage.BlobStrToBa(gaYLittleEndianstr); gaYBigEndian = bMessage.BlobStrToBa(gaYLittleEndianstr); Array.Reverse(gaYBigEndian, 0, gaYBigEndian.Length); gaYBigEndianstr = bMessage.BaToBlobStr(gaYBigEndian); // Capture the Little Endian representation of ga for later message checking sigmaSequenceCheck.currentGa = bMessage.BlobStrToBa(gaXLittleEndianstr + gaYLittleEndianstr); { // Use IPP or some other alternative if the user configuration does not select MS bcrypt for Diffie-Hellman key exchange. // NOTE: The use of IPP allows for the use of the standard wrapper functions in the SGX SDK. log.Debug("Calling IPP Wrapper for Diffie-Hellman key exchange..."); ippWrapper.InitDiffieHellman(); ippWrapper.GetDHPublicKey(ref gbXLittleEndian, ref gbYLittleEndian); gbXLittleEndianstr = bMessage.BaToBlobStr(gbXLittleEndian); gbYLittleEndianstr = bMessage.BaToBlobStr(gbYLittleEndian); gbLittleEndianStr = string.Concat(gbXLittleEndianstr, gbYLittleEndianstr); sigmaSequenceCheck.currentGb = bMessage.BlobStrToBa(gbXLittleEndianstr + gbYLittleEndianstr); log.DebugFormat("Server Public key: {0}", gbLittleEndianStr); // Derive the shared key byte[] sharedKey256 = new byte[Constants.SharedKeylen]; ippWrapper.GetDHSharedSecret(gaXLittleEndian, gaYLittleEndian, ref sharedKey256); derivedKeyStr = bMessage.BaToBlobStr(sharedKey256); log.DebugFormat("Shared secret: {0}", derivedKeyStr); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Use the full 256 bits of the derived DH key to further derive our smk //SMK is derived from the Diffie-Hellman shared secret elliptic curve field element // between the service provider and the // application enclave: // First compute Key Definition Key: KDK = AES-CMAC(0x00, gab x-coordinate) // Then SMK = AES-CMAC ( KDK, 0x01||’SMK’||0x00||0x80||0x00) byte[] derivedKeyBa = bMessage.BlobStrToBa(derivedKeyStr); // Store the KDK for further key derivation. sigmaSequenceCheck.currentKDK = cmacAES.Value(MsgInitValues.DS_ZERO_BA16, derivedKeyBa); // Store the SMK for the current session for use in processing message 3 sigmaSequenceCheck.currentSmk = bMessage.KeyLabelToKey(Constants.SMK, sigmaSequenceCheck.currentKDK); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Build each term by retrieving elements, // concatenating, converting and performing crypto operations. string gbgaStr = gbXLittleEndianstr + gbYLittleEndianstr + gaXLittleEndianstr + gaYLittleEndianstr; byte[] gbgaBa = bMessage.BlobStrToBa(gbgaStr); // gb||ga // Sign the gb||ga element Service Provider -- we use sigSP to notate this Service Provider signature // Get the Private Key from the settings file byte[] privateKey; try { privateKey = Constants.spPrivKeyBlob;// spPrivKeyBlob; } catch (Exception e) { options.LogCaughtErrorException(e); log.Debug("Failed to get private key: " + e.Message); HttpResponseException newException = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(newException); throw newException; } CngKey signSgxKey = CngKey.Import(privateKey, CngKeyBlobFormat.EccPrivateBlob); ECDsaCng ecDsaSig = new ECDsaCng(signSgxKey); // Elliptic Curve Digital Signature Algorithm, Signature ecDsaSig.HashAlgorithm = CngAlgorithm.Sha256; byte[] sigSPpubKey = ecDsaSig.Key.Export(CngKeyBlobFormat.EccPublicBlob); string sigSPpubKeyBlob = "{ 0x" + BitConverter.ToString(sigSPpubKey).Replace("-", ", 0x") + " }"; byte[] sigSP = ecDsaSig.SignData(gbgaBa); // Input is LittleEndian, but output is BigEndian string sigSPstring = bMessage.BaToBlobStr(sigSP); // Separate the X and Y components of the signature -- Big Endian at this point string sigSPXBigEndianstr = sigSPstring.Substring(0, (sigSPstring.Length / 2)); byte[] sigSPXBigEndian = bMessage.BlobStrToBa(sigSPXBigEndianstr); string sigSPYBigEndianstr = sigSPstring.Substring(sigSPstring.Length / 2); byte[] sigSPYBigEndian = bMessage.BlobStrToBa(sigSPYBigEndianstr); // Swap the byte order of the signed data back to Little Endian and convert to combined string (X|Y) sigSPXLittleEndian = sigSPXBigEndian.Reverse().ToArray(); string sigSPXLittleEndianstr = bMessage.BaToBlobStr(sigSPXLittleEndian); sigSPYLittleEndian = sigSPYBigEndian.Reverse().ToArray(); string sigSPYLittleEndianstr = bMessage.BaToBlobStr(sigSPYLittleEndian); string sigSPLittleEndianstring = sigSPXLittleEndianstr + sigSPYLittleEndianstr; // sigSPLittleEndianstring is in Little Endian format as required, ready to send to client // Get the signature link type try { if (SpStartup.iasConnectionMgr.LinkableQuotes) { log.Debug("Using Linkable Quotes"); // If the SP policy setting dictates the use of linkable quotes, explicitly // overwrite the default value in the sltype container. // Otherwise, the default value will be an un-linkable quote type. System.Buffer.BlockCopy(Constants.linkableBa, 0, Constants.sltype, 0, Constants.linkableBa.Length); } } catch (Exception e) { options.LogCaughtErrorException(e); log.Debug("Failed to get Linked Quote: " + e.Message); HttpResponseException newException = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(newException); throw newException; } string sigTypeStr = bMessage.BaToBlobStr(Constants.sltype); string kdfIdStr = bMessage.BaToBlobStr(Constants.kdfId); string gbSpidSigSPstring = gbLittleEndianStr + bMessage.BaToBlobStr(SpStartup.iasConnectionMgr.SPID) + sigTypeStr + kdfIdStr + sigSPLittleEndianstring; byte[] macBlob = bMessage.BlobStrToBa(gbSpidSigSPstring); // Compute the CMAKsmk of (gb||SPID||Type||KDF-ID||SigSP(gb||ga)) cMACsmk = cmacAES.Value(sigmaSequenceCheck.currentSmk, macBlob); string cMACsmkStr = bMessage.BaToBlobStr(cMACsmk); ecDsaSig.Dispose(); log.Debug("CalculateDiffieHellmanExchange(..) returning."); }
/// <summary> /// Verify that Message 1 is valid /// </summary> /// <param name="Request">Client Message 1 Response</param> /// <param name="sigmaSequenceCheck">Service Provider Sequence (State) Check</param> /// <returns>Parsed and validated Message 1</returns> private M1RequestMessage VerifyMessage1IsValid(HttpRequestMessage request, SpSequenceCheck sigmaSequenceCheck) { log.Debug("VerifyMessage1IsValid(.) started."); if (request == null || sigmaSequenceCheck == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } // Update Client state if (!sigmaSequenceCheck.UpdateState(Constants.SequenceState.Msg1)) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.PreconditionFailed); options.LogThrownException(e); throw e; } // Check m1 and if valid, process, create a nonce, get the sigRL and return m2. // M1 = msg1 = m1 = s1 = ga||GID var result = request.Content.ReadAsStringAsync(); string jsonMsg1Request = result.Result; M1RequestMessage m1Received = new M1RequestMessage(); try { m1Received = JsonConvert.DeserializeObject <M1RequestMessage>(jsonMsg1Request); } catch (Exception msg1reqError) { options.LogCaughtErrorException(msg1reqError); log.DebugFormat("******* Message 1 JSON Content Error: {0}", msg1reqError); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } string m1ReceivedString = m1Received.GetMsgString(); log.Info("******* Received M1 Request"); log.DebugFormat("{0}{1}", request.Headers, jsonMsg1Request); log.DebugFormat("M1 Base 16 Encoded String: {0}", m1ReceivedString); // Check the nonce and the base16 encoded length of the inbound request bool nonceCheckSuccess = false; try { log.Debug("Checking nonce"); nonceCheckSuccess = sigmaSequenceCheck.currentNonce.SequenceEqual(m1Received.reqHeader.nonce); } catch (Exception e) { options.LogCaughtErrorException(e); log.DebugFormat("****Message 1 Nonce Error: {0}", e); HttpResponseException newException = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(newException); throw newException; } if (!nonceCheckSuccess) { log.Debug("Msg1 Nonce check failed"); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.Forbidden); options.LogThrownException(e); throw e; } // Compare the reported length against the actual length (base16 string length/2) // Could BigEndian a replay attempt if the nonce field does not match. // Could also be other tampering if other fields do not pass checks. // Restart the session, and reject the request. if (!(BitConverter.ToUInt32(m1Received.reqHeader.msgLength, 0) == (m1ReceivedString.Length / 2))) { log.Debug("Msg1 Message length check failed"); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.Forbidden); options.LogThrownException(e); throw e; } if (m1Received.reqM1Body.gaX.SequenceEqual(MsgInitValues.DS_EMPTY_BA32)) { log.Debug("Msg1 GaX check failed"); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.Forbidden); options.LogThrownException(e); throw e; } if (m1Received.reqM1Body.gaY.SequenceEqual(MsgInitValues.DS_EMPTY_BA32)) { log.Debug("Msg1 GaY check failed"); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.Forbidden); options.LogThrownException(e); throw e; } if (m1Received.reqM1Body.pltfrmGid.SequenceEqual(MsgInitValues.DS_EMPTY_BA32)) { log.Debug("Msg1 Platform Group ID check failed"); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.Forbidden); options.LogThrownException(e); throw e; } log.Debug("VerifyMessage1IsValid(.) returning."); return(m1Received); }
/// <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> /// Builds Message 2 using connection IAS /// </summary> /// <param name="sigmaSequenceCheck">Service Provider Sequence (state) check</param> /// <returns>Boolean whether the message creation was successful or not</returns> private Boolean BuildIASMessage2(SpSequenceCheck sigmaSequenceCheck) { string iasGetRequestString = null; log.Debug("BuildIASMessage2(.) started."); if (sigmaSequenceCheck == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } try { log.Debug("Using IAS"); // Get URI to IAS iasGetRequestString = SpStartup.iasConnectionMgr.iasUri + Constants.SigRLUri + _gidBaString; log.DebugFormat("Sending IAS GET Request using: {0}", iasGetRequestString); } catch (Exception getReqError) { options.LogCaughtErrorException(getReqError); log.Debug("Failed to get IAS Uri"); // Copy error msg2 object to that which will be returned msg2 = CreateErrorMessage(getReqError); log.Debug("BuildIASMessage2(.) returning false."); return(false); } try { // Check connectivity with IAS Server // Use the received m1 as a trigger to check for a sigRL ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; int retryCount = Constants.retryCount; HttpResponseMessage iasSigRlResponse = null; byte[] internalError = BitConverter.GetBytes((UInt32)enStatusCodes.raErrIasInternal); while (retryCount-- > 0) { // Instantiate an HttResponseMessage object for the GET SigRL request with IAS. var iasResult = sigmaSequenceCheck.iasClient.GetAsync(iasGetRequestString); iasSigRlResponse = iasResult.Result; // retry if we have an internal IAS error if (iasSigRlResponse.StatusCode == HttpStatusCode.InternalServerError) { log.Debug("IAS error. Retrying..."); } else { break; } } // Check for failure on max attempts if (iasSigRlResponse.StatusCode == HttpStatusCode.InternalServerError) { throw new HttpResponseException(System.Net.HttpStatusCode.ServiceUnavailable); } log.DebugFormat("******* IAS GET Response: {0}", iasSigRlResponse.ReasonPhrase); var result = iasSigRlResponse.Content.ReadAsStringAsync(); // Signature Revocation List response from IAS string nnSigRlInput = result.Result; Boolean error = false; String errorReason = "Unknown Error"; // Check Signature Revocation List status code switch (iasSigRlResponse.StatusCode) { case HttpStatusCode.OK: { // With a successful IAS GET Response, finish building Msg2. // Decode the sigRl from the IAS response and populate the M2 sigRL fields, // store GID, and ga until the Sigma sequence is complete. // // Update the M2 response fields for a successful response and add the SigRL // if the IAS server returned a non-null list. // // m2 = gb||SPID||Quote Type||SigSP(gb||ga)||MACsmk(gb||SPID||Type||SigSP(gb||ga));SigRL bMessage.buildM2Response(out msg2); msg2.respHeader.respStatus = BitConverter.GetBytes((UInt32)enStatusCodes.raErrIasGetSuccess); // We need endianness agreement between client and server for sending each message term. // This protocol implementation assumes LittleEndian between client and server. msg2.respMsg2Body.gbX = _gbXLittleEndian; msg2.respMsg2Body.gbY = _gbYLittleEndian; msg2.respMsg2Body.spId = SpStartup.iasConnectionMgr.SPID; msg2.respMsg2Body.sigLinkType = Constants.sltype; msg2.respMsg2Body.kdfId = Constants.kdfId; msg2.respMsg2Body.sigSpX = _sigSPXLittleEndian; msg2.respMsg2Body.sigSpY = _sigSPYLittleEndian; msg2.respMsg2Body.cmacsmk = _cMACsmk; // Check for Null Signature Revocation List if (iasSigRlResponse.Content.Headers.ContentLength == 0) { msg2.respMsg2Body.sigrlSize = MsgInitValues.DS_ZERO_BA4; msg2.respMsg2Body.sigRl = null; } else { // The SigRL can have a variable content length; so just accept the length from // the IAS response as long as the size fits a predetermined practical size limit. if (iasSigRlResponse.Content.Headers.ContentLength <= MsgFieldLimits.UINT32_PRACTICAL_SIZE_LIMIT) { msg2.respMsg2Body.sigrlSize = BitConverter.GetBytes((UInt32)iasSigRlResponse.Content.Headers.ContentLength); // Parse non-null content and test with a non-null SigRL if IAS can deliver base64 encoded content similar to: // AAIADgAAAAEAAAABAAAAAHPUffSvHLYJc1GcvVLdoHZSfTo1qY7YqCtL3lqnWz4WI/JeLqDkU7eXpm5tdn1PoXEULgOSPJA8DJigmj4rBEU= // NOTE: testing was done with simulated content as shown above. ////////////////////////////////////////////////////////// byte[] nnSigRlBa = null; nnSigRlBa = Convert.FromBase64String(nnSigRlInput); msg2.respMsg2Body.sigrlSize = BitConverter.GetBytes((UInt32)nnSigRlBa.Length); // Report the length of the actual SigRl data, not the base64 string msg2.respMsg2Body.sigRl = nnSigRlBa; ////////////////////////////////////////////////////////// } else //Signature Revocation List is too big; May allow for larger sizes { log.Debug("****** Error SigRL Exceeds Internal Limit ******"); break; } } // Update the length field in the message header msg2.respHeader.msgLength = BitConverter.GetBytes((UInt32)msg2.GetMsgString().Length / 2); // Update the state machine // Capture the message nonce as the "current nonce" msg2.respHeader.sessionNonce = sigmaSequenceCheck.currentNonce; // Complete the state transition sigmaSequenceCheck.m1Received = true; string msg2JsonString = JsonConvert.SerializeObject(msg2); log.Debug("*********** Msg1 Processing Complete - Sending Msg2"); log.DebugFormat("{0}", msg2JsonString); break; } case HttpStatusCode.Unauthorized: { msg2.respHeader.respStatus = BitConverter.GetBytes((UInt32)RaSpRef.enStatusCodes.raErrIasUnauth); errorReason = "****** Sending Msg2 \"Unauthorized\" Error Response"; break; } case HttpStatusCode.NotFound: { msg2.respHeader.respStatus = BitConverter.GetBytes((UInt32)RaSpRef.enStatusCodes.raErrIasNotFound); errorReason = "****** Sending Msg2 \"Not Found\" Error Response"; break; } case HttpStatusCode.InternalServerError: // This should never be executed { msg2.respHeader.respStatus = BitConverter.GetBytes((UInt32)RaSpRef.enStatusCodes.raErrIasInternal); errorReason = "****** Sending Msg2 \"Internal Server Error\" Error Response"; break; } default: { msg2.respHeader.respStatus = BitConverter.GetBytes((UInt32)RaSpRef.enStatusCodes.raErrUnknown); errorReason = "****** Sending Msg2 \"Unknown\" Error Response"; break; } } // Print messages on error if (error) { string msg2ErrorMsgString = msg2.GetMsgString(); msg2.respHeader.protocolVer = MsgInitValues.PROTOCOL; msg2.respHeader.msgLength = BitConverter.GetBytes((UInt32)msg2ErrorMsgString.Length / 2); string msg2ErrorMsgJsonString = JsonConvert.SerializeObject(msg2); log.Debug(errorReason); log.Debug("SigRL Response Status: " + BitConverter.ToString(msg2.respHeader.respStatus)); log.DebugFormat("{0}", msg2ErrorMsgJsonString); } iasSigRlResponse.Dispose(); } catch (HttpResponseException) { // This catch block is to prevent subsequent catch blocks from catching HttpResponseExceptions. // Because we are using Visual Studio 2012, we are limited to an older version of the C# // language that does not support exception filters. When C# 6 is available to us, it may // be decided that exception filters are a better solution than this catch block. throw; } catch (Exception getReqError) { // Copy error msg2 object to that which will be returned msg2 = CreateErrorMessage(getReqError); log.Debug("BuildIASMessage2(.) returning false."); return(false); } // Success log.Debug("BuildIASMessage2(.) returning true."); return(true); }
/// <summary> /// Verify that Message 0 is valid /// </summary> /// <param name="Request">Client Message 0 Response</param> /// <param name="sigmaSequenceCheck">Service Provider Sequence (State) Check</param> /// <returns>Parsed and validated Message 0</returns> private M0RequestMessage VerifyMessage0IsValid(HttpRequestMessage request, SpSequenceCheck sigmaSequenceCheck) { log.Debug("VerifyMessage0IsValid(.) started."); if (request == null || sigmaSequenceCheck == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } // Update Client state if (!sigmaSequenceCheck.UpdateState(Constants.SequenceState.Msg0)) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.PreconditionFailed); options.LogThrownException(e); throw e; } // Check m0 and if valid, process var result = request.Content.ReadAsStringAsync(); string jsonMsg0Request = result.Result; M0RequestMessage m0Received = new M0RequestMessage(); try { m0Received = JsonConvert.DeserializeObject <M0RequestMessage>(jsonMsg0Request); } catch (Exception msg0reqError) { options.LogCaughtErrorException(msg0reqError); log.DebugFormat("******* Message 0 JSON Content Error: {0}", msg0reqError); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } string m0ReceivedString = m0Received.GetMsgString(); log.Info("******* Received M0 Request"); log.DebugFormat("{0}{1}", request.Headers, jsonMsg0Request); log.DebugFormat("M0 Base 16 Encoded String: {0}", m0ReceivedString); // Check the nonce and the base16 encoded length of the inbound request bool nonceCheckSuccess = false; try { log.Debug("Checking nonce"); nonceCheckSuccess = sigmaSequenceCheck.currentNonce.SequenceEqual(m0Received.reqHeader.nonce); } catch (Exception e) { options.LogCaughtErrorException(e); log.DebugFormat("****Message 0 Nonce Error: {0}", e); HttpResponseException newException = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(newException); throw newException; } if (!nonceCheckSuccess) { log.Debug("Msg0 Nonce check failed"); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.Forbidden); options.LogThrownException(e); throw e; } // Compare the reported length against the actual length (base16 string length/2) // Could BigEndian a replay attempt if the nonce field does not match. // Could also be other tampering if other fields do not pass checks. // Restart the session, and reject the request. if (!(BitConverter.ToUInt32(m0Received.reqHeader.msgLength, 0) == (m0ReceivedString.Length / 2))) { log.Debug("Msg0 Message length check failed"); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.Forbidden); options.LogThrownException(e); throw e; } if (m0Received.reqM0Body.ExtGID.SequenceEqual(MsgInitValues.DS_EMPTY_BA4)) { log.Debug("Msg0: Extended GID wasn't sent"); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.Forbidden); options.LogThrownException(e); throw e; } // NOTE: Extended GID = 0 indicates that IAS is selected for enclave verification. // This Service Provider only supports IAS for enclave verification at this time. // Note to ISV: if non-Intel Attestation Service (i.e. Extended GID != 0) is being used, replace this logic // to point to your service. if (!m0Received.reqM0Body.ExtGID.SequenceEqual(MsgInitValues.DS_ZERO_BA4)) { log.Debug("Msg0: Invalid Extended GID. This server only processes Extended GID = 0"); HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.Unauthorized); options.LogThrownException(e); throw e; } log.Debug("VerifyMessage0IsValid(.) returning."); return(m0Received); }