/// <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> /// Process message 1 and create message 2 for the client /// </summary> /// <param name="data">Thread Data, input parameter (HttpRequestMessage) from the client</param> public void ProcessMessage(object data) { msg2Response = null; log.Debug("ProcessMessage(.) started."); try { if (data == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } HttpRequestMessage request = (HttpRequestMessage)data; mClient = ClientDatabase.GetTransaction(request, Constants.msg1Str); if (mClient == null) { Exception e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } // kill client wait thread so we don't time out. mClient.killTimerThread(); // NOTE: There is a potential race-condition where the client is removed from the database during the time that the cilent sends its response // Can choose to check to re-add the client here in that case log.Debug("\n ***** State: Starting Message 1/2 sequence for client: " + mClient.ID + "\n"); Msg1 m1 = new Msg1(); M2ResponseMessage m2Response = m1.ProcessMessage1(request, mClient.sigmaSequenceCheck); // Set Client State msg2Response = m2Response; } catch (HttpResponseException re) { options.LogCaughtException(re); httpRE = re; } catch (Exception ex) { options.LogCaughtException(ex); threadException = ex; } finally { log.Debug("ProcessMessage(.) returning."); } }
public async Task <IHttpActionResult> Msg1() { log.Debug("Msg1(.) started."); bool error = false; HttpStatusCode errorCode = System.Net.HttpStatusCode.InternalServerError; M2ResponseMessage m2Response = null; try { m2Response = await ClientDatabase.Message1(this.Request); } catch (WebException we) { error = true; log.Debug("Web Exception in Provisioning Request: " + we.Message); HttpWebResponse WebResponse = we.Response as HttpWebResponse; errorCode = (HttpStatusCode)WebResponse.StatusCode; } catch (HttpResponseException re) { error = true; log.Debug("HttpResponseException in Message 1/2: " + re.Message); errorCode = (HttpStatusCode)re.Response.StatusCode; } catch (Exception e) { error = true; log.Debug("******* M1 Content Error"); log.Debug("Error: " + e.Message); errorCode = System.Net.HttpStatusCode.InternalServerError; } // Return response to client if (error) { log.DebugFormat("Msg1(.) returning HTTP status code {0}.", errorCode); try { ClientDatabase.RemoveTransaction(this.Request, Constants.msg1Str); } catch (Exception e) // This catches HttpResponseException also, which is what we want here { log.Debug("Exception in Message 1/2 while error cleanup RemoveTransaction(): " + e.Message); } return(StatusCode(errorCode)); } else { log.Debug("Msg1(.) returning HTTP status success."); return(Json(m2Response)); } }
/// <summary> /// Create a Message 2 for error condition /// </summary> /// <returns>Message 2 error message</returns> private M2ResponseMessage CreateErrorMessage(Exception errorMessage) { log.Debug("CreateErrorMessage(.) started."); if (errorMessage == null) { HttpResponseException e = new HttpResponseException(System.Net.HttpStatusCode.InternalServerError); options.LogThrownException(e); throw e; } // Handle the case where there is no HTTP response from IAS // There was a problem with the GET SigRL request - respond to Msg1 and abort the sequence. log.Debug("\n****** IAS GET Failure - aborting message sequence"); log.DebugFormat("**** IAS Error **** \n{0}", errorMessage); log.Debug("******"); // Create an "Empty" Msg2 instance to return error information M2ResponseMessage msg2ErrorMsg = new M2ResponseMessage(); string msg2ErrorMsgString = msg2ErrorMsg.GetMsgString(); // NOTE: Additional HTTP errors and exceptions should be handled here string exceptionString = errorMessage.InnerException.Message; switch (exceptionString) { case "An error occurred while sending the request.": msg2ErrorMsg.respHeader.respStatus = BitConverter.GetBytes((UInt32)RaSpRef.enStatusCodes.raErrIasUnknown); break; default: msg2ErrorMsg.respHeader.respStatus = BitConverter.GetBytes((UInt32)RaSpRef.enStatusCodes.raErrUnknown); break; } // Update the length field even though it should be the same as the default length. msg2ErrorMsg.respHeader.msgLength = BitConverter.GetBytes((UInt32)msg2ErrorMsgString.Length / 2); string msg2ErrorMsgJsonString = JsonConvert.SerializeObject(msg2ErrorMsg); log.Debug("****** Sending Msg2 Error Response"); log.Debug("SigRL Response Status: " + BitConverter.ToString(msg2.respHeader.respStatus)); log.Debug(msg2ErrorMsgJsonString); log.Debug("M2ResponseMessage(.) returning."); return(msg2ErrorMsg); }
//Build an M2 response public void buildM2Response(out M2ResponseMessage m2Resp) { string respond = Constants.Respond; byte[] tempM2Bytes = { 0x00 }; var m2Body = new ResponseM2Body(); var m2Response = new M2ResponseMessage(respond); //Populate message body components m2Response.respMsg2Body.gbX = Constants.sampleGbXba; m2Response.respMsg2Body.gbY = Constants.sampleGbYba; m2Response.respMsg2Body.spId = SpStartup.iasConnectionMgr.SPID; m2Response.respMsg2Body.sigLinkType = Constants.sltype; m2Response.respMsg2Body.kdfId = Constants.kdfId; //the m2 slReserved field is automatically initialized in the instance m2Response.respMsg2Body.sigSpX = Constants.sigSpXba; m2Response.respMsg2Body.sigSpY = Constants.sigSpYba; using (RNGCryptoServiceProvider nonceGen = new RNGCryptoServiceProvider()) { if (nonceGen == null) { Exception e = new Exception("Internal Error: nonceGen is Null"); options.LogThrownException(e); throw e; } byte[] sessionNonce = Constants.sn2; nonceGen.GetBytes(sessionNonce); //Generate a new nonce m2Response.respMsg2Body.cmacsmk = sessionNonce; m2Response.respMsg2Body.sigrlSize = MsgInitValues.DS_EMPTY_BA4; m2Response.respMsg2Body.sigRl = null; //Copy each reference to its output parameter m2Resp = m2Response; } return; }
/// <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); }