/// <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, SecurityAssociation> sender_to_sa = null; SecurityAssociation sa = null; try { sender_to_sa = _spi[sdm.SPI]; sa = sender_to_sa[return_path]; sa.HandleData(b, return_path, null); } catch { if (sender_to_sa == null && !SecurityPolicy.Supports(sdm.SPI)) { throw new Exception("Invalid SPI: " + sdm.SPI); } else if (sa == null) { NoSuchSA(sdm.SPI, return_path); throw new Exception("No SA for: " + return_path); } else if (sa.Closed) { throw new Exception("SA has been closed."); } else { throw; } } }
public override bool Equals(object o) { SecurityPolicy sp = o as SecurityPolicy; if (o == null) { return(false); } return((sp.Index == this.Index) && sp.Encryptor.Equals(this.Encryptor) && sp.Authenticator.Equals(this.Authenticator)); }
/// <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(SecurityAssociation 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.Value = 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>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(SecurityAssociation 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); 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.Value = lcert; sa.RemoteCertificate.Value = 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>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(SecurityAssociation 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 (idempotently) returns a new SecurityAssociation for the /// specified sender using the specified SA.</summary> virtual protected SecurityAssociation CreateSecurityAssociation(ISender Sender, int SPI) { if (!SecurityPolicy.Supports(SPI)) { throw new Exception("Unsupported SPI"); } SecurityAssociation sa = null; int count = 0; lock (_sync) { Dictionary <ISender, SecurityAssociation> sender_to_sa = null; if (_spi.ContainsKey(SPI)) { sender_to_sa = _spi[SPI]; } else { sender_to_sa = new Dictionary <ISender, SecurityAssociation>(); _spi[SPI] = sender_to_sa; } if (sender_to_sa.ContainsKey(Sender)) { sa = sender_to_sa[Sender]; } else { sa = new SecurityAssociation(Sender, SPI); sa.Subscribe(this, null); sa.StateChange += SAStateChange; sa.RequestUpdate += SARequestUpdate; sender_to_sa[Sender] = sa; } } return(sa); }
///<summary>Enables the SA if it has been properly setup.</summary> public void Enable() { // If both parties setup simultaneous SAs we could end up calling this // twice, once as a client and the other as server, this way we only // go through the whole process once. lock (_sync) { if (_called_enable == 1) { return; } else if (_closed == 1) { throw new Exception("Cannot enable a closed SA!"); } else if (_ldhe == null) { throw new Exception("Local DHE not set."); } else if (RDHE.Value == null) { throw new Exception("Remote DHE not set."); } else if (!_hash_verified) { throw new Exception("Hash is not verified!"); } else if (TimedOut) { throw new Exception("Timed out on this one!"); } _called_enable = 1; // Deriving the DHE exchange and determing the order of keys // Specifically, we need up to 4 keys for the sender/receiver encryption/ // authentication codes. So to determine the order, we say whomever has // the smallest gets the first set of keys. byte[] rdhe = (byte[])RDHE.Value; RDHE = null; byte[] key = _dh.DecryptKeyExchange(rdhe); _dh.Clear(); _dh = null; int i = 0; while (i < _ldhe.Length && _ldhe[i] == rdhe[i]) { i++; } bool same = i == _ldhe.Length; bool first = !same && (_ldhe[i] < rdhe[i]); _ldhe = null; // Gathering our security parameter objects SecurityPolicy sp = SecurityPolicy.GetPolicy(_spi); SymmetricAlgorithm in_sa = sp.CreateSymmetricAlgorithm(); HashAlgorithm in_ha = sp.CreateHashAlgorithm(); SymmetricAlgorithm out_sa = sp.CreateSymmetricAlgorithm(); HashAlgorithm out_ha = sp.CreateHashAlgorithm(); // Generating the total key length int key_length = key.Length + 2 + (in_sa.KeySize / 8 + in_sa.BlockSize / 8) * 2; KeyedHashAlgorithm in_kha = in_ha as KeyedHashAlgorithm; KeyedHashAlgorithm out_kha = out_ha as KeyedHashAlgorithm; if (in_kha != null) { key_length += (in_kha.HashSize / 8) * 2; } // Generating a key by repeatedly hashing the DHE value and the key so far SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider(); int usable_key_offset = key.Length; while (key.Length < key_length) { byte[] hash = sha1.ComputeHash(key); byte[] tmp_key = new byte[hash.Length + key.Length]; key.CopyTo(tmp_key, 0); hash.CopyTo(tmp_key, key.Length); key = tmp_key; } // Like a sub-session ID (see DTLS) short epoch = (short)((key[usable_key_offset] << 8) + key[usable_key_offset + 1]); usable_key_offset += 2; byte[] key0 = new byte[in_sa.KeySize / 8]; Array.Copy(key, usable_key_offset, key0, 0, key0.Length); usable_key_offset += key0.Length; byte[] key1 = new byte[in_sa.KeySize / 8]; Array.Copy(key, usable_key_offset, key1, 0, key1.Length); usable_key_offset += key1.Length; // Same may occur if we are forming a session with ourselves! if (same) { in_sa.Key = key0; out_sa.Key = key0; } else if (first) { in_sa.Key = key0; out_sa.Key = key1; } else { out_sa.Key = key0; in_sa.Key = key1; } if (in_kha != null) { byte[] hkey0 = new byte[in_kha.HashSize / 8]; Array.Copy(key, usable_key_offset, hkey0, 0, hkey0.Length); usable_key_offset += hkey0.Length; byte[] hkey1 = new byte[in_kha.HashSize / 8]; Array.Copy(key, usable_key_offset, hkey1, 0, hkey1.Length); usable_key_offset += hkey1.Length; if (same) { in_kha.Key = hkey0; out_kha.Key = hkey0; } else if (first) { in_kha.Key = hkey0; out_kha.Key = hkey1; } else { out_kha.Key = hkey0; in_kha.Key = hkey1; } } SecurityHandler sh = new SecurityHandler(in_sa, out_sa, in_ha, out_ha, epoch); sh.Update += UpdateSH; SecurityHandler to_close = _current_sh; if (to_close != null) { to_close.Close(); } _current_sh = sh; _last_epoch = _current_epoch; _current_epoch = epoch; } // All finished set the state (which will probably fire an event) State = SAState.Active; }
/// <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; SecurityAssociation sa = null; // This can be in a try statement since this is best effort anyway try { Dictionary <ISender, SecurityAssociation> 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.SAState.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; } } }