1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-11-26 19:39:53 +03:00

Cedar/Proto_OpenVPN: add support for GCM ciphers

This commit is contained in:
Davide Beatrici 2018-11-03 16:14:56 +01:00
parent aae9ec0492
commit 9afcc91ae1
2 changed files with 207 additions and 107 deletions

View File

@ -176,17 +176,31 @@ void OvsLog(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_CHANNEL *c, char *na
} }
// Encrypt the data // Encrypt the data
UINT OvsEncrypt(CIPHER *cipher, MD *md, UCHAR *iv, UCHAR *dest, UCHAR *src, UINT size) UINT OvsEncrypt(CIPHER *cipher, MD *md, UCHAR *iv, UCHAR *tag, UCHAR *dest, UCHAR *src, UINT src_size, UCHAR *aad, UINT aad_size)
{ {
UINT dest_size, ret;
// Validate arguments // Validate arguments
if (cipher == NULL || md == NULL) if (cipher == NULL || (cipher->IsAeadCipher == false && md == NULL))
{ {
return 0; return 0;
} }
// Encrypt if (cipher->IsAeadCipher)
dest_size = CipherProcess(cipher, iv, dest + md->Size + cipher->IvSize, src, size); {
// Encrypt in AEAD mode (no HMAC)
UINT dest_size = CipherProcessAead(cipher, iv, tag, 16, dest, src, src_size, aad, aad_size);
if (dest_size == 0)
{
Debug("OvsEncrypt(): CipherProcessAead() failed!\n");
return 0;
}
return dest_size;
}
else
{
// Encrypt in non-AEAD mode (with HMAC)
UINT ret;
UINT dest_size = CipherProcess(cipher, iv, dest + md->Size + cipher->IvSize, src, src_size);
if (dest_size == 0) if (dest_size == 0)
{ {
Debug("OvsEncrypt(): CipherProcess() failed!\n"); Debug("OvsEncrypt(): CipherProcess() failed!\n");
@ -207,20 +221,48 @@ UINT OvsEncrypt(CIPHER *cipher, MD *md, UCHAR *iv, UCHAR *dest, UCHAR *src, UINT
return dest_size + ret; return dest_size + ret;
} }
}
// Decrypt the data // Decrypt the data
UINT OvsDecrypt(CIPHER *cipher, MD *md, UCHAR *dest, UCHAR *src, UINT size) UINT OvsDecrypt(CIPHER *cipher, MD *md, UCHAR *iv, UCHAR *dest, UCHAR *src, UINT size)
{ {
UCHAR *hmac;
UCHAR *iv;
UCHAR hmac_test[128];
// Validate arguments // Validate arguments
if (cipher == NULL || md == NULL) if (cipher == NULL)
{ {
return 0; return 0;
} }
if (size < (md->Size + cipher->IvSize + sizeof(UINT))) if (cipher->IsAeadCipher)
{
UCHAR *tag = src;
if (iv == NULL || size <= OPENVPN_TAG_SIZE)
{
return 0;
}
src += OPENVPN_TAG_SIZE;
size -= OPENVPN_TAG_SIZE;
// Payload
if (size >= 1 && (cipher->BlockSize == 0 || (size % cipher->BlockSize) == 0))
{
// Decryption
UINT ret = CipherProcessAead(cipher, iv, tag, OPENVPN_TAG_SIZE, dest, src, size, iv, sizeof(UINT));
if (ret == 0)
{
Debug("OvsDecrypt(): CipherProcessAead() failed!\n");
}
return ret;
}
}
else
{
UCHAR *hmac;
UCHAR hmac_test[128];
if (md == NULL || iv == NULL || size < (md->Size + cipher->IvSize + sizeof(UINT)))
{ {
return 0; return 0;
} }
@ -243,7 +285,7 @@ UINT OvsDecrypt(CIPHER *cipher, MD *md, UCHAR *dest, UCHAR *src, UINT size)
} }
// IV // IV
iv = src; Copy(iv, src, cipher->IvSize);
src += cipher->IvSize; src += cipher->IvSize;
size -= cipher->IvSize; size -= cipher->IvSize;
@ -259,6 +301,7 @@ UINT OvsDecrypt(CIPHER *cipher, MD *md, UCHAR *dest, UCHAR *src, UINT size)
return ret; return ret;
} }
}
return 0; return 0;
} }
@ -373,17 +416,28 @@ void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p, UINT protocol)
OPENVPN_CHANNEL *c = se->Channels[recv_packet->KeyId]; OPENVPN_CHANNEL *c = se->Channels[recv_packet->KeyId];
if (c->Status == OPENVPN_CHANNEL_STATUS_ESTABLISHED) if (c->Status == OPENVPN_CHANNEL_STATUS_ESTABLISHED)
{ {
UINT size = OvsDecrypt(c->CipherDecrypt, c->MdRecv, s->TmpBuf, recv_packet->Data, recv_packet->DataSize); UINT size;
if (size >= sizeof(UINT))
{
UCHAR *data = s->TmpBuf; UCHAR *data = s->TmpBuf;
if (c->CipherDecrypt->IsAeadCipher)
{
// Update variable part (packet ID) of IV
Copy(c->IvRecv, recv_packet->Data, sizeof(recv_packet->PacketId));
// Update of last communication time // Decrypt
se->LastCommTick = s->Now; size = OvsDecrypt(c->CipherDecrypt, NULL, c->IvRecv, data, recv_packet->Data + sizeof(UINT), recv_packet->DataSize - sizeof(UINT));
}
else
{
// Decrypt
size = OvsDecrypt(c->CipherDecrypt, c->MdRecv, c->IvRecv, data, recv_packet->Data, recv_packet->DataSize);
// Seek buffer after the packet ID // Seek buffer after the packet ID
data += sizeof(UINT); data += sizeof(UINT);
size -= sizeof(UINT); size -= sizeof(UINT);
}
// Update of last communication time
se->LastCommTick = s->Now;
if (size < sizeof(ping_signature) || Cmp(data, ping_signature, sizeof(ping_signature)) != 0) if (size < sizeof(ping_signature) || Cmp(data, ping_signature, sizeof(ping_signature)) != 0)
{ {
@ -404,7 +458,6 @@ void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p, UINT protocol)
} }
} }
} }
}
OvsFreePacket(recv_packet); OvsFreePacket(recv_packet);
} }
@ -818,7 +871,7 @@ void OvsSetupSessionParameters(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_C
LIST *o; LIST *o;
BUF *b; BUF *b;
char opt_str[MAX_SIZE]; char opt_str[MAX_SIZE];
char *cipher_name; char *cipher_name, *md_name;
// Validate arguments // Validate arguments
if (s == NULL || se == NULL || c == NULL || data == NULL) if (s == NULL || se == NULL || c == NULL || data == NULL)
{ {
@ -897,7 +950,6 @@ void OvsSetupSessionParameters(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_C
if (se->Protocol == OPENVPN_PROTOCOL_TCP) if (se->Protocol == OPENVPN_PROTOCOL_TCP)
{ {
// TCP // TCP
// UDP
if (IsIP6(&se->ClientIp) == false) if (IsIP6(&se->ClientIp) == false)
{ {
StrCpy(c->Proto, sizeof(c->Proto), "TCPv4_SERVER"); StrCpy(c->Proto, sizeof(c->Proto), "TCPv4_SERVER");
@ -922,12 +974,9 @@ void OvsSetupSessionParameters(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_C
// Encryption algorithm // Encryption algorithm
cipher_name = EntryListStrValue(o, "cipher"); cipher_name = EntryListStrValue(o, "cipher");
c->CipherEncrypt = OvsGetCipher(cipher_name);
c->CipherDecrypt = OvsGetCipher(cipher_name);
// Hash algorithm // Hash algorithm
c->MdSend = OvsGetMd(EntryListStrValue(o, "auth")); md_name = EntryListStrValue(o, "auth");
c->MdRecv = NewMd(c->MdSend->Name);
// Random number generation // Random number generation
Rand(c->ServerKey.Random1, sizeof(c->ServerKey.Random1)); Rand(c->ServerKey.Random1, sizeof(c->ServerKey.Random1));
@ -954,13 +1003,26 @@ void OvsSetupSessionParameters(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_C
c->ExpansionKey, sizeof(c->ExpansionKey)); c->ExpansionKey, sizeof(c->ExpansionKey));
FreeBuf(b); FreeBuf(b);
// Set the key // Set up the encryption algorithm
c->CipherEncrypt = OvsGetCipher(cipher_name);
c->CipherDecrypt = OvsGetCipher(cipher_name);
SetCipherKey(c->CipherDecrypt, c->ExpansionKey + 0, false); SetCipherKey(c->CipherDecrypt, c->ExpansionKey + 0, false);
SetCipherKey(c->CipherEncrypt, c->ExpansionKey + 128, true); SetCipherKey(c->CipherEncrypt, c->ExpansionKey + 128, true);
if (c->CipherDecrypt->IsAeadCipher)
{
// In AEAD mode the IV is composed by the packet ID and a part of the HMAC key
Copy(c->IvRecv + sizeof(c->LastDataPacketId), c->ExpansionKey + 64, c->CipherDecrypt->IvSize - sizeof(c->LastDataPacketId));
Copy(c->IvSend + sizeof(c->LastDataPacketId), c->ExpansionKey + 192, c->CipherEncrypt->IvSize - sizeof(c->LastDataPacketId));
}
else
{
// Set up the hash algorithm
c->MdSend = OvsGetMd(md_name);
c->MdRecv = OvsGetMd(md_name);
SetMdKey(c->MdRecv, c->ExpansionKey + 64, c->MdRecv->Size); SetMdKey(c->MdRecv, c->ExpansionKey + 64, c->MdRecv->Size);
SetMdKey(c->MdSend, c->ExpansionKey + 192, c->MdSend->Size); SetMdKey(c->MdSend, c->ExpansionKey + 192, c->MdSend->Size);
}
FreeEntryList(o);
// We pass the cipher name sent from the OpenVPN client, unless it's a different cipher, to prevent a message such as: // We pass the cipher name sent from the OpenVPN client, unless it's a different cipher, to prevent a message such as:
// WARNING: 'cipher' is used inconsistently, local='cipher AES-128-GCM', remote='cipher aes-128-gcm' // WARNING: 'cipher' is used inconsistently, local='cipher AES-128-GCM', remote='cipher aes-128-gcm'
@ -979,9 +1041,11 @@ void OvsSetupSessionParameters(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_C
se->LinkMtu, se->LinkMtu,
se->TunMtu, se->TunMtu,
c->Proto, c->Proto,
cipher_name, c->MdSend->Name, c->CipherEncrypt->KeySize * 8); cipher_name, md_name, c->CipherEncrypt->KeySize * 8);
Debug("Building OptionStr: %s\n", c->ServerKey.OptionString);
FreeEntryList(o);
Debug("OvsSetupSessionParameters(): Built OptionString: %s\n", c->ServerKey.OptionString);
OvsLog(s, se, c, "LO_OPTION_STR_SEND", c->ServerKey.OptionString); OvsLog(s, se, c, "LO_OPTION_STR_SEND", c->ServerKey.OptionString);
} }
@ -1379,7 +1443,8 @@ OPENVPN_CHANNEL *OvsNewChannel(OPENVPN_SESSION *se, UCHAR key_id)
c->KeyId = key_id; c->KeyId = key_id;
Rand(c->NextIv, sizeof(c->NextIv)); Rand(c->IvSend, sizeof(c->IvSend));
Rand(c->IvRecv, sizeof(c->IvRecv));
//c->NextRekey = se->Server->Now + (UINT64)5000; //c->NextRekey = se->Server->Now + (UINT64)5000;
@ -1427,8 +1492,7 @@ UINT64 OvsNewServerSessionId(OPENVPN_SERVER *s)
// Build and submit the OpenVPN data packet // Build and submit the OpenVPN data packet
void OvsSendDataPacket(OPENVPN_CHANNEL *c, UCHAR key_id, UINT data_packet_id, void *data, UINT data_size) void OvsSendDataPacket(OPENVPN_CHANNEL *c, UCHAR key_id, UINT data_packet_id, void *data, UINT data_size)
{ {
UCHAR *encrypted_data; const UCHAR op = ((OPENVPN_P_DATA_V1 << 3) & 0xF8) | (key_id & 0x07);
UINT encrypted_size;
UCHAR *dest_data; UCHAR *dest_data;
UINT dest_size; UINT dest_size;
// Validate arguments // Validate arguments
@ -1437,29 +1501,63 @@ void OvsSendDataPacket(OPENVPN_CHANNEL *c, UCHAR key_id, UINT data_packet_id, vo
return; return;
} }
// Generate the data to be encrypted // [ xxx ] = unprotected
encrypted_size = sizeof(UINT) + data_size; // [ - xxx - ] = authenticated
encrypted_data = ZeroMalloc(encrypted_size); // [ * xxx * ] = encrypted and authenticated
WRITE_UINT(encrypted_data, data_packet_id); if (c->CipherEncrypt->IsAeadCipher)
Copy(encrypted_data + sizeof(UINT), data, data_size); {
// [ opcode ] [ - packet ID - ] [ TAG ] [ * packet payload * ]
UCHAR tag[16];
// Update variable part (packet ID) of IV
WRITE_UINT(c->IvSend, data_packet_id);
// Prepare a buffer to store the results // Prepare a buffer to store the results
dest_data = Malloc(sizeof(UCHAR) + c->MdSend->Size + c->CipherEncrypt->IvSize + encrypted_size + 256); dest_data = Malloc(sizeof(op) + sizeof(data_packet_id) + sizeof(tag) + data_size + 256);
// Encrypt // Set data size to the maximum known
dest_size = OvsEncrypt(c->CipherEncrypt, c->MdSend, c->NextIv, dest_data + sizeof(CHAR), encrypted_data, encrypted_size); dest_size = sizeof(op) + sizeof(data_packet_id) + sizeof(tag);
dest_size += sizeof(UCHAR);
// Update the NextIV // Write opcode
Copy(c->NextIv, dest_data + dest_size - c->CipherEncrypt->IvSize, c->CipherEncrypt->IvSize); dest_data[0] = op;
// Op-code // Write packet ID
dest_data[0] = ((OPENVPN_P_DATA_V1 << 3) & 0xF8) | (key_id & 0x07); WRITE_UINT(dest_data + sizeof(op), data_packet_id);
OvsSendPacketRawNow(c->Server, c->Session, dest_data, dest_size); // Write encrypted payload
dest_size += OvsEncrypt(c->CipherEncrypt, NULL, c->IvSend, tag, dest_data + dest_size, data, data_size, c->IvSend, sizeof(data_packet_id));
// Write authentication tag
Copy(dest_data + sizeof(op) + sizeof(data_packet_id), tag, sizeof(tag));
}
else
{
// [ opcode ] [ HMAC ] [ - IV - ] [ * packet ID * ] [ * packet payload * ]
UINT encrypted_size = sizeof(data_packet_id) + data_size;
UCHAR *encrypted_data = ZeroMalloc(encrypted_size);
WRITE_UINT(encrypted_data, data_packet_id);
Copy(encrypted_data + sizeof(data_packet_id), data, data_size);
// Prepare a buffer to store the results
dest_data = Malloc(sizeof(op) + c->MdSend->Size + c->CipherEncrypt->IvSize + encrypted_size + 256);
// Set data size to the maximum known
dest_size = sizeof(op);
// Write opcode
dest_data[0] = op;
// Write IV, encrypted packet ID and payload
dest_size += OvsEncrypt(c->CipherEncrypt, c->MdSend, c->IvSend, NULL, dest_data + sizeof(op), encrypted_data, encrypted_size, NULL, 0);
Free(encrypted_data); Free(encrypted_data);
// Update the IV
Copy(c->IvSend, dest_data + dest_size - c->CipherEncrypt->IvSize, c->CipherEncrypt->IvSize);
}
OvsSendPacketRawNow(c->Server, c->Session, dest_data, dest_size);
} }
// Build an OpenVPN control packet // Build an OpenVPN control packet

View File

@ -127,6 +127,7 @@
#define OPENVPN_MAX_SSL_RECV_BUF_SIZE (256 * 1024) // SSL receive buffer maximum length #define OPENVPN_MAX_SSL_RECV_BUF_SIZE (256 * 1024) // SSL receive buffer maximum length
#define OPENVPN_MAX_KEY_SIZE 64 // Maximum key size #define OPENVPN_MAX_KEY_SIZE 64 // Maximum key size
#define OPENVPN_TAG_SIZE 16 // Tag size (for packet authentication in AEAD mode)
#define OPENVPN_TMP_BUFFER_SIZE (65536 + 256) // Temporary buffer size #define OPENVPN_TMP_BUFFER_SIZE (65536 + 256) // Temporary buffer size
@ -241,9 +242,10 @@ struct OPENVPN_CHANNEL
CIPHER *CipherDecrypt; // Decryption algorithm CIPHER *CipherDecrypt; // Decryption algorithm
MD *MdSend; // Transmission MD algorithm MD *MdSend; // Transmission MD algorithm
MD *MdRecv; // Reception MD algorithm MD *MdRecv; // Reception MD algorithm
UCHAR IvSend[64]; // Transmission IV
UCHAR IvRecv[64]; // Reception IV
UCHAR MasterSecret[48]; // Master Secret UCHAR MasterSecret[48]; // Master Secret
UCHAR ExpansionKey[256]; // Expansion Key UCHAR ExpansionKey[256]; // Expansion Key
UCHAR NextIv[64]; // Next IV
UINT LastDataPacketId; // Previous Data Packet ID UINT LastDataPacketId; // Previous Data Packet ID
UINT64 EstablishedTick; // Established time UINT64 EstablishedTick; // Established time
UCHAR KeyId; // KEY ID UCHAR KeyId; // KEY ID