///<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;
}