/// <summary>2b) Receive a DHEWithCertificateAndCAs, verify the certificate and attempt /// to find a matching Certificate for the list of CAs, if you find one, /// finish the DHE handshake and send the certificate via a DHEWithCertificate</summary> /// <param name="sa">A security association that we wish to perform the /// specified control operation on.</param> /// <param name="scm">The received SecurityControlMessage.</param> /// <param name="scm_reply">A prepared reply message (with headers and such.</param> /// <param name="return_path">Where to send the result.</param> /// <param name="low_level_sender">We expect the return_path to not be an edge or /// some other type of "low level" sender, so this contains the parsed out value.</param> protected void HandleControlDHEWithCertificateAndCAs(PeerSecAssociation sa, SecurityControlMessage scm, SecurityControlMessage scm_reply, ISender return_path, ISender low_level_sender) { ProtocolLog.WriteIf(ProtocolLog.Security, GetHashCode() + " Received DHEWithCertificateAndCAs from: " + low_level_sender); if (sa == null) { sa = CreateSecurityAssociation(low_level_sender, scm.SPI, false); } byte[] cert = new byte[scm.Certificate.Length]; scm.Certificate.CopyTo(cert, 0); X509Certificate rcert = new X509Certificate(cert); _ch.Verify(rcert, low_level_sender); HashAlgorithm sha1 = new SHA1CryptoServiceProvider(); scm.Verify((RSACryptoServiceProvider)rcert.RSA, sha1); X509Certificate lcert = null; if (SecurityPolicy.GetPolicy(scm.SPI).PreExchangedKeys) { lcert = _ch.DefaultCertificate; } else { lcert = _ch.FindCertificate(scm.CAs); } sa.LocalCertificate = lcert; sa.RemoteCertificate = rcert; sa.RDHE.Value = scm.DHE; sa.DHEWithCertificateAndCAsInHash.Value = MemBlock.Reference(sha1.ComputeHash((byte[])scm.Packet)); scm_reply.LocalCookie = scm.RemoteCookie; scm_reply.RemoteCookie = scm.LocalCookie; scm_reply.DHE = sa.LDHE; scm_reply.Certificate = MemBlock.Reference(lcert.RawData); scm_reply.Type = SecurityControlMessage.MessageType.DHEWithCertificate; lock (_private_key_lock) { scm_reply.Sign(_private_key, sha1); } sa.DHEWithCertificateHash.Value = MemBlock.Reference(sha1.ComputeHash((byte[])scm_reply.Packet)); ICopyable to_send = new CopyList(SecureControl, scm_reply.Packet); return_path.Send(to_send); ProtocolLog.WriteIf(ProtocolLog.Security, GetHashCode() + " Successful DHEWithCertificateAndCAs from: " + low_level_sender); }
/// <summary>Removes the specified SA from our database.</summary> override protected void RemoveSA(SecurityAssociation sa) { PeerSecAssociation psa = sa as PeerSecAssociation; if (psa == null) { throw new Exception("Invalid PeerSecAssociation: " + sa); } lock (_sync) { if (_spi.ContainsKey(psa.SPI)) { _spi[psa.SPI].Remove(psa.Sender); } } }
/// <summary>This (idempotently) returns a new SecurityAssociation for the /// specified sender using the specified SA.</summary> protected PeerSecAssociation CreateSecurityAssociation(ISender Sender, int SPI, bool start) { if (!SecurityPolicy.Supports(SPI)) { throw new Exception("Unsupported SPI"); } PeerSecAssociation sa = null; lock (_sync) { Dictionary <ISender, PeerSecAssociation> sender_to_sa = null; if (_spi.ContainsKey(SPI)) { sender_to_sa = _spi[SPI]; } else { sender_to_sa = new Dictionary <ISender, PeerSecAssociation>(); _spi[SPI] = sender_to_sa; } if (sender_to_sa.ContainsKey(Sender)) { sa = sender_to_sa[Sender]; } else { sa = new PeerSecAssociation(Sender, _ch, SPI); sa.Subscribe(this, null); sa.StateChangeEvent += SAStateChange; sa.RequestUpdate += SARequestUpdate; sender_to_sa[Sender] = sa; } } if (start && sa.State != SecurityAssociation.States.Active) { StartSA(sa); } return(sa); }
/// <summary>1b) Receive a Cookie which responds with a CookieResponse</summary> /// <param name="sa">A security association that we wish to perform the /// specified control operation on.</param> /// <param name="calc_cookie">Cookie value for the association sender.</param> /// <param name="scm">The received SecurityControlMessage.</param> /// <param name="scm_reply">A prepared reply message (with headers and such.</param> /// <param name="return_path">Where to send the result.</param> /// <param name="low_level_sender">We expect the return_path to not be an edge or /// some other type of "low level" sender, so this contains the parsed out value.</param> protected void HandleControlCookie(PeerSecAssociation sa, MemBlock calc_cookie, SecurityControlMessage scm, SecurityControlMessage scm_reply, ISender return_path, ISender low_level_sender) { ProtocolLog.WriteIf(ProtocolLog.Security, GetHashCode() + " Received Cookie from: " + low_level_sender); scm_reply.Type = SecurityControlMessage.MessageType.CookieResponse; scm_reply.RemoteCookie = scm.LocalCookie; scm_reply.LocalCookie = calc_cookie; if (SecurityPolicy.GetPolicy(scm.SPI).PreExchangedKeys) { scm_reply.CAs = new List <MemBlock>(0); } else { scm_reply.CAs = _ch.SupportedCAs; } ICopyable to_send = new CopyList(SecureControl, scm_reply.Packet); return_path.Send(to_send); ProtocolLog.WriteIf(ProtocolLog.Security, GetHashCode() + " Successful Cookie from: " + low_level_sender); }
/// <summary>This is SecureData that needs to get to an SA.</summary> protected void HandleData(MemBlock b, ISender return_path) { SecurityDataMessage sdm = new SecurityDataMessage(b); Dictionary <ISender, PeerSecAssociation> sender_to_sa = null; PeerSecAssociation sa = null; try { sender_to_sa = _spi[sdm.SPI]; sa = sender_to_sa[return_path]; sa.HandleData(b, return_path, null); } catch (Exception e) { if (sender_to_sa == null && !SecurityPolicy.Supports(sdm.SPI)) { ProtocolLog.WriteIf(ProtocolLog.SecurityExceptions, String.Format( "Unsupported SPI, from {0}, message: {1}", return_path, sdm)); } else { if (sa == null) { ProtocolLog.WriteIf(ProtocolLog.SecurityExceptions, String.Format( "No SA, from {0}, message: {1}", return_path, sdm)); } else if (sa.Closed) { ProtocolLog.WriteIf(ProtocolLog.SecurityExceptions, String.Format( "SA, {0}, has been closed, from {1}, message: {2}.", sa, return_path, sdm)); } else { ProtocolLog.WriteIf(ProtocolLog.SecurityExceptions, String.Format( "SA, {0}, from {1}, message: {2}, causes unhandled exception : {3}", sa, return_path, sdm, e)); } NoSuchSA(sdm.SPI, return_path); } } }
/// <summary>2b) Receive a DHEWithCertificateAndCAs, verify the certificate and attempt /// to find a matching Certificate for the list of CAs, if you find one, /// finish the DHE handshake and send the certificate via a DHEWithCertificate</summary> /// <param name="sa">A security association that we wish to perform the /// specified control operation on.</param> /// <param name="scm">The received SecurityControlMessage.</param> /// <param name="scm_reply">A prepared reply message (with headers and such.</param> /// <param name="return_path">Where to send the result.</param> /// <param name="low_level_sender">We expect the return_path to not be an edge or /// some other type of "low level" sender, so this contains the parsed out value.</param> protected void HandleControlDHEWithCertificateAndCAs(PeerSecAssociation sa, SecurityControlMessage scm, SecurityControlMessage scm_reply, ISender return_path, ISender low_level_sender) { ProtocolLog.WriteIf(ProtocolLog.Security, GetHashCode() + " Received DHEWithCertificateAndCAs from: " + low_level_sender); if(sa == null) { sa = CreateSecurityAssociation(low_level_sender, scm.SPI, false); } byte[] cert = new byte[scm.Certificate.Length]; scm.Certificate.CopyTo(cert, 0); X509Certificate rcert = new X509Certificate(cert); _ch.Verify(rcert, low_level_sender); HashAlgorithm sha1 = new SHA1CryptoServiceProvider(); scm.Verify((RSACryptoServiceProvider) rcert.RSA, sha1); X509Certificate lcert = null; if(SecurityPolicy.GetPolicy(scm.SPI).PreExchangedKeys) { lcert = _ch.DefaultCertificate; } else { lcert = _ch.FindCertificate(scm.CAs); } sa.LocalCertificate = lcert; sa.RemoteCertificate = rcert; sa.RDHE.Value = scm.DHE; sa.DHEWithCertificateAndCAsInHash.Value = MemBlock.Reference(sha1.ComputeHash((byte[]) scm.Packet)); scm_reply.LocalCookie = scm.RemoteCookie; scm_reply.RemoteCookie = scm.LocalCookie; scm_reply.DHE = sa.LDHE; scm_reply.Certificate = MemBlock.Reference(lcert.RawData); scm_reply.Type = SecurityControlMessage.MessageType.DHEWithCertificate; lock(_private_key_lock) { scm_reply.Sign(_private_key, sha1); } sa.DHEWithCertificateHash.Value = MemBlock.Reference(sha1.ComputeHash((byte[]) scm_reply.Packet)); ICopyable to_send = new CopyList(SecureControl, scm_reply.Packet); return_path.Send(to_send); ProtocolLog.WriteIf(ProtocolLog.Security, GetHashCode() + " Successful DHEWithCertificateAndCAs from: " + low_level_sender); }
/// <summary>2a) Receive a CookieResponse that contains a list of CAs, if you have /// a Certificate that supports one of the CAs send it along with a DHE /// and a list of your supported CAs in a DHEWithCertificateAndCAs.</summary> /// <param name="sa">A security association that we wish to perform the /// specified control operation on.</param> /// <param name="scm">The received SecurityControlMessage.</param> /// <param name="scm_reply">A prepared reply message (with headers and such.</param> /// <param name="return_path">Where to send the result.</param> /// <param name="low_level_sender">We expect the return_path to not be an edge or /// some other type of "low level" sender, so this contains the parsed out value.</param> protected void HandleControlCookieResponse(PeerSecAssociation sa, SecurityControlMessage scm, SecurityControlMessage scm_reply, ISender return_path, ISender low_level_sender) { ProtocolLog.WriteIf(ProtocolLog.Security, GetHashCode() + " Received CookieResponse from: " + low_level_sender); if(sa == null) { throw new Exception("No valid SA!"); } // This seems like unnecessary code scm_reply.Type = SecurityControlMessage.MessageType.CookieResponse; X509Certificate lcert = null; if(SecurityPolicy.GetPolicy(scm.SPI).PreExchangedKeys) { lcert = _ch.DefaultCertificate; } else { lcert = _ch.FindCertificate(scm.CAs); } sa.RemoteCookie.Value = scm.LocalCookie; sa.LocalCertificate = lcert; scm_reply.Certificate = lcert.RawData; scm_reply.DHE = sa.LDHE; scm_reply.LocalCookie = scm.RemoteCookie; scm_reply.RemoteCookie = scm.LocalCookie; scm_reply.Type = SecurityControlMessage.MessageType.DHEWithCertificateAndCAs; if(SecurityPolicy.GetPolicy(scm.SPI).PreExchangedKeys) { scm_reply.CAs = new List<MemBlock>(0); } else { scm_reply.CAs = _ch.SupportedCAs; } HashAlgorithm sha1 = new SHA1CryptoServiceProvider(); lock(_private_key_lock) { scm_reply.Sign(_private_key, sha1); } sa.DHEWithCertificateAndCAsOutHash.Value = sha1.ComputeHash((byte[]) scm_reply.Packet); ICopyable to_send = new CopyList(Security, SecureControl, scm_reply.Packet); _rrman.SendRequest(return_path, ReqrepManager.ReqrepType.Request, to_send, this, sa); ProtocolLog.WriteIf(ProtocolLog.Security, GetHashCode() + " Successful CookieResponse from: " + low_level_sender); }
/// <summary>1b) Receive a Cookie which responds with a CookieResponse</summary> /// <param name="sa">A security association that we wish to perform the /// specified control operation on.</param> /// <param name="calc_cookie">Cookie value for the association sender.</param> /// <param name="scm">The received SecurityControlMessage.</param> /// <param name="scm_reply">A prepared reply message (with headers and such.</param> /// <param name="return_path">Where to send the result.</param> /// <param name="low_level_sender">We expect the return_path to not be an edge or /// some other type of "low level" sender, so this contains the parsed out value.</param> protected void HandleControlCookie(PeerSecAssociation sa, MemBlock calc_cookie, SecurityControlMessage scm, SecurityControlMessage scm_reply, ISender return_path, ISender low_level_sender) { ProtocolLog.WriteIf(ProtocolLog.Security, String.Format( "{0}, Received Cookie from: {1}, In-Cookie: {2}", GetHashCode(), low_level_sender, scm.LocalCookie)); scm_reply.Type = SecurityControlMessage.MessageType.CookieResponse; scm_reply.RemoteCookie = scm.LocalCookie; scm_reply.LocalCookie = calc_cookie; if(SecurityPolicy.GetPolicy(scm.SPI).PreExchangedKeys) { scm_reply.CAs = new List<MemBlock>(0); } else { scm_reply.CAs = _ch.SupportedCAs; } ICopyable to_send = new CopyList(SecureControl, scm_reply.Packet); return_path.Send(to_send); ProtocolLog.WriteIf(ProtocolLog.Security, String.Format( "{0}, Successful Cookie from: {1}, Out-Cookie: {2}", GetHashCode(), low_level_sender, calc_cookie)); }
/// <summary>1a) Send a Cookie</summary> /// <param name="sa">A security association that we wish to perform the /// specified control operation on.</param> protected void HandleControlNoSuchSA(PeerSecAssociation sa) { if(sa == null) { ProtocolLog.WriteIf(ProtocolLog.Security, GetHashCode() + " NoSuchSA received, but we have no SA either!"); } else { ProtocolLog.WriteIf(ProtocolLog.Security, GetHashCode() + " NoSuchSA received, handling..."); StartSA(sa); } }
/// <summary>This begins the SecurityAssociation exchange protocol over the /// specified SecurityAssociation.</summary> protected void StartSA(PeerSecAssociation sa) { SecurityControlMessage scm_reply = new SecurityControlMessage(); scm_reply.Version = Version; scm_reply.SPI = sa.SPI; scm_reply.Type = SecurityControlMessage.MessageType.Cookie; scm_reply.LocalCookie = CalculateCookie(sa.Sender); ICopyable to_send = new CopyList(Security, SecureControl, scm_reply.Packet); _rrman.SendRequest(sa.Sender, ReqrepManager.ReqrepType.Request, to_send, this, sa); }
/// <summary>This (idempotently) returns a new SecurityAssociation for the /// specified sender using the specified SA.</summary> virtual protected PeerSecAssociation CreateSecurityAssociation(ISender Sender, int SPI, bool start) { if(!SecurityPolicy.Supports(SPI)) { throw new Exception("Unsupported SPI"); } PeerSecAssociation sa = null; lock(_sync) { Dictionary<ISender, PeerSecAssociation> sender_to_sa = null; if(_spi.ContainsKey(SPI)) { sender_to_sa = _spi[SPI]; } else { sender_to_sa = new Dictionary<ISender, PeerSecAssociation>(); _spi[SPI] = sender_to_sa; } if(sender_to_sa.ContainsKey(Sender)) { sa = sender_to_sa[Sender]; } else { sa = new PeerSecAssociation(Sender, _ch, SPI); sa.Subscribe(this, null); sa.StateChangeEvent += SAStateChange; sa.RequestUpdate += SARequestUpdate; sender_to_sa[Sender] = sa; } } if(start && sa.State != SecurityAssociation.States.Active && sa.Start()) { StartSA(sa); } return sa; }
/// <summary>This is the control state machine. There are three paths in /// the state machine, iniator, receiver, and bidirectional. The /// bidirectional case occurs when two remote ISenders that are matched /// together initiate a handshake at the same time, otherwise the initiator /// /receiver pattern is followed. The high level overview for the states /// are: /// 1a) Send a Cookie /// 1b) Receive a Cookie which responds with a CookieResponse /// 2a) Receive a CookieResponse that contains a list of CAs, if you have /// a Certificate that supports one of the CAs send it along with a DHE /// and a list of your supported CAs in a DHEWithCertificateAndCAs. /// 2b) Receive a DHEWithCertificateAndCAs, verify the certificate and attempt /// to find a matching Certificate for the list of CAs, if you find one, /// finish the DHE handshake and send the certificate via a DHEWithCertificate /// 3a) Receive a DHEWithCertificate, verify the certificate and DHE and /// send a Confirm that you are ready to Verify the stack and start the /// system. /// 3b) Receive a Confirm, verify the entire stack and send a Confirm /// 4a)Receive a Confirm, verify the entire stack and all set to go /// </summary> protected void HandleControl(MemBlock b, ISender return_path) { ISender low_level_sender = return_path; if (low_level_sender is ReqrepManager.ReplyState) { low_level_sender = ((ReqrepManager.ReplyState)low_level_sender).ReturnPath; } SecurityControlMessage scm = new SecurityControlMessage(b); MemBlock calc_cookie = CalculateCookie(low_level_sender); if (scm.Version != Version) { ProtocolLog.WriteIf(ProtocolLog.SecurityExceptions, String.Format( "Invalid version expected {0}, found {1}, from {2}, message: {3}", Version, scm.Version, low_level_sender, scm)); return; } else if (!SecurityPolicy.Supports(scm.SPI)) { ProtocolLog.WriteIf(ProtocolLog.SecurityExceptions, String.Format( "Unsupported SPI, from {0}, message: {1}", low_level_sender, scm)); return; } else if ((scm.Type != SecurityControlMessage.MessageType.Cookie && scm.Type != SecurityControlMessage.MessageType.NoSuchSA) && !scm.RemoteCookie.Equals(calc_cookie)) { ProtocolLog.WriteIf(ProtocolLog.SecurityExceptions, String.Format( "Invalid cookie, Expected {0}, Found {1}, From {2}, Message {3}", calc_cookie, scm.RemoteCookie, low_level_sender, scm)); return; } SecurityControlMessage scm_reply = new SecurityControlMessage(); scm_reply.Version = Version; scm_reply.SPI = scm.SPI; PeerSecAssociation sa = null; // This can be in a try statement since this is best effort anyway try { Dictionary <ISender, PeerSecAssociation> sender_to_sa = _spi[scm.SPI]; sa = sender_to_sa[low_level_sender]; } catch { } if (sa != null) { // Reset cases where no state has been set yet or state is attempting // to be re-established, if other message types arrive, they will // expect pre-set state and will fail. The mechanisms instilled thus // far should prevent this, but this makes it explicit. Now only // wayward messages will arrive to other states. if (scm.Type == SecurityControlMessage.MessageType.NoSuchSA || scm.Type == SecurityControlMessage.MessageType.Cookie || scm.Type == SecurityControlMessage.MessageType.CookieResponse || scm.Type == SecurityControlMessage.MessageType.DHEWithCertificateAndCAs) { sa.TryReset(); } if (sa.Closed) { ProtocolLog.WriteIf(ProtocolLog.SecurityExceptions, GetHashCode() + " SA closed, clearing SA state! " + sa); sa = null; } else if (sa.State == SecurityAssociation.States.Active && // Two nodes can demand a confirm at the same time, if this // message doesn't pass, then one side will close due to timeout scm.Type != SecurityControlMessage.MessageType.Confirm) { ProtocolLog.WriteIf(ProtocolLog.SecurityExceptions, String.Format( "{0}, {1}: {2}", GetHashCode(), "SA Active, received message", scm.Type)); return; } } try { switch (scm.Type) { case SecurityControlMessage.MessageType.NoSuchSA: HandleControlNoSuchSA(sa); break; case SecurityControlMessage.MessageType.Cookie: HandleControlCookie(sa, calc_cookie, scm, scm_reply, return_path, low_level_sender); break; case SecurityControlMessage.MessageType.CookieResponse: HandleControlCookieResponse(sa, scm, scm_reply, return_path, low_level_sender); break; case SecurityControlMessage.MessageType.DHEWithCertificateAndCAs: HandleControlDHEWithCertificateAndCAs(sa, scm, scm_reply, return_path, low_level_sender); break; case SecurityControlMessage.MessageType.DHEWithCertificate: HandleControlDHEWithCertificate(sa, scm, scm_reply, return_path, low_level_sender); break; case SecurityControlMessage.MessageType.Confirm: HandleControlConfirm(sa, scm, scm_reply, return_path, low_level_sender); break; default: throw new Exception("Invalid message!"); } } catch (Exception e) { if (scm.Type == SecurityControlMessage.MessageType.DHEWithCertificateAndCAs || scm.Type == SecurityControlMessage.MessageType.DHEWithCertificate || scm.Type == SecurityControlMessage.MessageType.Confirm) { NoSuchSA(scm.SPI, return_path); } if (sa != null && sa.Closed) { ProtocolLog.WriteIf(ProtocolLog.SecurityExceptions, GetHashCode() + " SA closed! " + sa); return; } ProtocolLog.WriteIf(ProtocolLog.SecurityExceptions, String.Format( "Error in {0}, unhandled exception from {1}, message: {2}, exception: {3}", GetHashCode(), low_level_sender, scm, e)); } }
public void SHUpdateTest() { callback_count = 0; int spi = SecurityPolicy.DefaultSPI; MockSender sender1 = new MockSender(null, null, null, 2); MockSender sender2 = new MockSender(null, null, null, 2); PeerSecAssociation sa1 = new PeerSecAssociation(sender1, null, spi); sa1.StateChangeEvent += StateChange; sender2.Receiver = sa1; MockDataHandler mdh1 = new MockDataHandler(); sa1.Subscribe(mdh1, null); PeerSecAssociation sa2 = new PeerSecAssociation(sender2, null, spi); sender1.Receiver = sa2; MockDataHandler mdh2 = new MockDataHandler(); sa2.Subscribe(mdh2, null); Setup(sa1, sa2); sa1.RequestUpdate += Callback; sa2.RequestUpdate += Callback; byte[] b = null; Random rand = new Random(); MemBlock mb = null; int current_epoch = sa1.CurrentEpoch; for(int i = 0; i < 80; i++) { b = new byte[128]; rand.NextBytes(b); mb = MemBlock.Reference(b); sa1.Send(mb); Assert.IsTrue(mdh2.Contains(mb), "Contains" + i); if(i % 20 == 0 && i != 0) { Assert.AreEqual(callback_count, 1, "Callback count " + i); callback_count = 0; Setup(sa1, sa2); } else { if(i % 20 == 1 && i != 1) { Assert.IsFalse(current_epoch == sa1.CurrentEpoch, "Current epoch " + i); current_epoch = sa1.CurrentEpoch; } Assert.AreEqual(current_epoch, sa1.CurrentEpoch, "Current epoch " + i); } } }
public void Test() { int spi = SecurityPolicy.DefaultSPI; MockSender sender1 = new MockSender(null, null, null, 2); MockSender sender2 = new MockSender(null, null, null, 2); PeerSecAssociation sa1 = new PeerSecAssociation(sender1, null, spi); sa1.StateChangeEvent += StateChange; sender2.Receiver = sa1; MockDataHandler mdh1 = new MockDataHandler(); sa1.Subscribe(mdh1, null); PeerSecAssociation sa2 = new PeerSecAssociation(sender2, null, spi); sender1.Receiver = sa2; MockDataHandler mdh2 = new MockDataHandler(); sa2.Subscribe(mdh2, null); byte[] b = null; Random rand = new Random(); MemBlock mb = null; int current_epoch = sa1.CurrentEpoch; for(int i = 0; i < 5; i++) { Setup(sa1, sa2); b = new byte[128]; rand.NextBytes(b); mb = MemBlock.Reference(b); sa1.Send(mb); Assert.IsTrue(mdh2.Contains(mb), "Contains" + i); Assert.AreEqual(state, sa1.State, "State == Active" + i); Assert.IsFalse(current_epoch == sa1.CurrentEpoch, "Current epoch " + i); current_epoch = sa1.CurrentEpoch; } sa1.CheckState(); sa1.CheckState(); b = new byte[128]; rand.NextBytes(b); mb = MemBlock.Reference(b); try { sa1.Send(mb); } catch {} Assert.IsTrue(!mdh2.Contains(mb), "Failed!"); Assert.AreEqual(state, sa1.State, "State == Failed"); }
/// <summary>This is the control state machine. There are three paths in /// the state machine, iniator, receiver, and bidirectional. The /// bidirectional case occurs when two remote ISenders that are matched /// together initiate a handshake at the same time, otherwise the initiator /// /receiver pattern is followed. The high level overview for the states /// are: /// 1a) Send a Cookie /// 1b) Receive a Cookie which responds with a CookieResponse /// 2a) Receive a CookieResponse that contains a list of CAs, if you have /// a Certificate that supports one of the CAs send it along with a DHE /// and a list of your supported CAs in a DHEWithCertificateAndCAs. /// 2b) Receive a DHEWithCertificateAndCAs, verify the certificate and attempt /// to find a matching Certificate for the list of CAs, if you find one, /// finish the DHE handshake and send the certificate via a DHEWithCertificate /// 3a) Receive a DHEWithCertificate, verify the certificate and DHE and /// send a Confirm that you are ready to Verify the stack and start the /// system. /// 3b) Receive a Confirm, verify the entire stack and send a Confirm /// 4a)Receive a Confirm, verify the entire stack and all set to go /// </summary> protected void HandleControl(MemBlock b, ISender return_path) { ISender low_level_sender = return_path; if (low_level_sender is ReqrepManager.ReplyState) { low_level_sender = ((ReqrepManager.ReplyState)low_level_sender).ReturnPath; } SecurityControlMessage scm = new SecurityControlMessage(b); MemBlock calc_cookie = CalculateCookie(low_level_sender); if (scm.Version != Version) { throw new Exception("Invalid version: " + scm.Version); } else if (!SecurityPolicy.Supports(scm.SPI)) { throw new Exception("No support for SPI: " + scm.SPI); } else if (!scm.RemoteCookie.Equals(calc_cookie)) { if (scm.Type != SecurityControlMessage.MessageType.Cookie && scm.Type != SecurityControlMessage.MessageType.NoSuchSA) { throw new Exception("Invalid cookie!"); } } SecurityControlMessage scm_reply = new SecurityControlMessage(); scm_reply.Version = Version; scm_reply.SPI = scm.SPI; PeerSecAssociation sa = null; // This can be in a try statement since this is best effort anyway try { Dictionary <ISender, PeerSecAssociation> sender_to_sa = _spi[scm.SPI]; sa = sender_to_sa[low_level_sender]; } catch { } if (sa != null) { sa.Reset(); if (sa.Closed) { throw new Exception("SA closed!"); } else if (sa.State == SecurityAssociation.States.Active) { return; } } try { switch (scm.Type) { case SecurityControlMessage.MessageType.NoSuchSA: HandleControlNoSuchSA(sa); break; case SecurityControlMessage.MessageType.Cookie: HandleControlCookie(sa, calc_cookie, scm, scm_reply, return_path, low_level_sender); break; case SecurityControlMessage.MessageType.CookieResponse: HandleControlCookieResponse(sa, scm, scm_reply, return_path, low_level_sender); break; case SecurityControlMessage.MessageType.DHEWithCertificateAndCAs: HandleControlDHEWithCertificateAndCAs(sa, scm, scm_reply, return_path, low_level_sender); break; case SecurityControlMessage.MessageType.DHEWithCertificate: HandleControlDHEWithCertificates(sa, scm, scm_reply, return_path, low_level_sender); break; case SecurityControlMessage.MessageType.Confirm: HandleControlConfirm(sa, scm, scm_reply, return_path, low_level_sender); break; default: throw new Exception("Invalid message!"); } } catch { if (sa != null && sa.Closed) { throw new Exception("SA closed."); } else { throw; } } }
/// <summary>3a) Receive a DHEWithCertificate, verify the certificate and DHE and /// send a Confirm that you are ready to Verify the stack and start the /// system.</summary> /// <param name="sa">A security association that we wish to perform the /// specified control operation on.</param> /// <param name="scm">The received SecurityControlMessage.</param> /// <param name="scm_reply">A prepared reply message (with headers and such.</param> /// <param name="return_path">Where to send the result.</param> /// <param name="low_level_sender">We expect the return_path to not be an edge or /// some other type of "low level" sender, so this contains the parsed out value.</param> protected void HandleControlDHEWithCertificate(PeerSecAssociation sa, SecurityControlMessage scm, SecurityControlMessage scm_reply, ISender return_path, ISender low_level_sender) { ProtocolLog.WriteIf(ProtocolLog.Security, GetHashCode() + " Received DHEWithCertificate from: " + low_level_sender); if(sa == null) { throw new Exception("No valid SA!"); } byte[] cert = new byte[scm.Certificate.Length]; scm.Certificate.CopyTo(cert, 0); X509Certificate rcert = new X509Certificate(cert); HashAlgorithm sha1 = new SHA1CryptoServiceProvider(); scm.Verify((RSACryptoServiceProvider) rcert.RSA, sha1); _ch.Verify(rcert, low_level_sender); sa.RemoteCertificate = rcert; sa.RDHE.Value = scm.DHE; scm_reply.LocalCookie = scm.RemoteCookie; scm_reply.RemoteCookie = scm.LocalCookie; scm_reply.Hash = MemBlock.Reference(sha1.ComputeHash((byte[]) scm.Packet)); scm_reply.Type = SecurityControlMessage.MessageType.Confirm; lock(_private_key_lock) { scm_reply.Sign(_private_key, sha1); } ICopyable to_send = new CopyList(Security, SecureControl, scm_reply.Packet); _rrman.SendRequest(return_path, ReqrepManager.ReqrepType.Request, to_send, this, sa); ProtocolLog.WriteIf(ProtocolLog.Security, GetHashCode() + " Successful DHEWithCertificate from: " + low_level_sender); }
/// <summary>3b) Receive a Confirm, verify the entire stack and send a Confirm /// 4a)Receive a Confirm, verify the entire stack and all set to go</summary> /// <param name="sa">A security association that we wish to perform the /// specified control operation on.</param> /// <param name="scm">The received SecurityControlMessage.</param> /// <param name="scm_reply">A prepared reply message (with headers and such.</param> /// <param name="return_path">Where to send the result.</param> /// <param name="low_level_sender">We expect the return_path to not be an edge or /// some other type of "low level" sender, so this contains the parsed out value.</param> protected void HandleControlConfirm(PeerSecAssociation sa, SecurityControlMessage scm, SecurityControlMessage scm_reply, ISender return_path, ISender low_level_sender) { ProtocolLog.WriteIf(ProtocolLog.Security, GetHashCode() + " Received Confirm from: " + low_level_sender); if(sa == null) { throw new Exception("No valid SA!"); } HashAlgorithm sha1 = new SHA1CryptoServiceProvider(); scm.Verify((RSACryptoServiceProvider) sa.RemoteCertificate.RSA, sha1); if(return_path == low_level_sender) { sa.VerifyResponse(scm.Hash); } else { sa.VerifyRequest(scm.Hash); scm_reply.LocalCookie = scm.RemoteCookie; scm_reply.RemoteCookie = scm.LocalCookie; scm_reply.Hash = sa.DHEWithCertificateAndCAsInHash.Value; scm_reply.Type = SecurityControlMessage.MessageType.Confirm; lock(_private_key_lock) { scm_reply.Sign(_private_key, sha1); } ICopyable to_send = new CopyList(SecureControl, scm_reply.Packet); return_path.Send(to_send); } sa.Enable(); ProtocolLog.WriteIf(ProtocolLog.Security, GetHashCode() + " Successful Confirm from: " + low_level_sender); }
protected void Setup(PeerSecAssociation sa1, PeerSecAssociation sa2) { sa1.Reset(); sa2.Reset(); sa1.RDHE.Value = sa2.LDHE; sa2.RDHE.Value = sa1.LDHE; Random rand = new Random(); byte[] b = new byte[128]; rand.NextBytes(b); MemBlock mb = MemBlock.Reference(b); sa1.DHEWithCertificateAndCAsOutHash.Value = mb; sa1.VerifyResponse(mb); b = new byte[128]; rand.NextBytes(b); mb = MemBlock.Reference(b); sa2.DHEWithCertificateHash.Value = mb; sa2.VerifyRequest(mb); sa1.Enable(); sa2.Enable(); // This is just for kicks sa1.Enable(); }