///<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!"); } _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) UpdateState(States.Active); _active = _state == States.Active; }