mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2026-06-17 14:21:30 +00:00
Refactor OpenSSL usage into a platform-specific file to allow other crypto backends
This commit is contained in:
+23
-96
@@ -6,8 +6,6 @@
|
|||||||
|
|
||||||
#include <enet/enet.h>
|
#include <enet/enet.h>
|
||||||
|
|
||||||
#include <openssl/evp.h>
|
|
||||||
|
|
||||||
// NV control stream packet header for TCP
|
// NV control stream packet header for TCP
|
||||||
typedef struct _NVCTL_TCP_PACKET_HEADER {
|
typedef struct _NVCTL_TCP_PACKET_HEADER {
|
||||||
unsigned short type;
|
unsigned short type;
|
||||||
@@ -65,11 +63,8 @@ static int currentEnetSequenceNumber;
|
|||||||
static bool idrFrameRequired;
|
static bool idrFrameRequired;
|
||||||
static LINKED_BLOCKING_QUEUE invalidReferenceFrameTuples;
|
static LINKED_BLOCKING_QUEUE invalidReferenceFrameTuples;
|
||||||
|
|
||||||
static EVP_CIPHER_CTX* cipherContext;
|
static PPLT_CRYPTO_CONTEXT encryptionCtx;
|
||||||
|
static PPLT_CRYPTO_CONTEXT decryptionCtx;
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
||||||
#define EVP_CIPHER_CTX_reset(x) EVP_CIPHER_CTX_cleanup(x); EVP_CIPHER_CTX_init(x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CONN_IMMEDIATE_POOR_LOSS_RATE 30
|
#define CONN_IMMEDIATE_POOR_LOSS_RATE 30
|
||||||
#define CONN_CONSECUTIVE_POOR_LOSS_RATE 15
|
#define CONN_CONSECUTIVE_POOR_LOSS_RATE 15
|
||||||
@@ -251,7 +246,8 @@ int initializeControlStream(void) {
|
|||||||
lastConnectionStatusUpdate = CONN_STATUS_OKAY;
|
lastConnectionStatusUpdate = CONN_STATUS_OKAY;
|
||||||
currentEnetSequenceNumber = 0;
|
currentEnetSequenceNumber = 0;
|
||||||
usePeriodicPing = APP_VERSION_AT_LEAST(7, 1, 415);
|
usePeriodicPing = APP_VERSION_AT_LEAST(7, 1, 415);
|
||||||
cipherContext = EVP_CIPHER_CTX_new();
|
encryptionCtx = PltCreateCryptoContext();
|
||||||
|
decryptionCtx = PltCreateCryptoContext();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -269,7 +265,8 @@ void freeFrameInvalidationList(PLINKED_BLOCKING_QUEUE_ENTRY entry) {
|
|||||||
// Cleans up control stream
|
// Cleans up control stream
|
||||||
void destroyControlStream(void) {
|
void destroyControlStream(void) {
|
||||||
LC_ASSERT(stopping);
|
LC_ASSERT(stopping);
|
||||||
EVP_CIPHER_CTX_free(cipherContext);
|
PltDestroyCryptoContext(encryptionCtx);
|
||||||
|
PltDestroyCryptoContext(decryptionCtx);
|
||||||
PltCloseEvent(&invalidateRefFramesEvent);
|
PltCloseEvent(&invalidateRefFramesEvent);
|
||||||
freeFrameInvalidationList(LbqDestroyLinkedBlockingQueue(&invalidReferenceFrameTuples));
|
freeFrameInvalidationList(LbqDestroyLinkedBlockingQueue(&invalidReferenceFrameTuples));
|
||||||
PltDeleteMutex(&enetMutex);
|
PltDeleteMutex(&enetMutex);
|
||||||
@@ -390,57 +387,23 @@ static PNVCTL_TCP_PACKET_HEADER readNvctlPacketTcp(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool encryptControlMessage(PNVCTL_ENCRYPTED_PACKET_HEADER encPacket, PNVCTL_ENET_PACKET_HEADER_V2 packet) {
|
static bool encryptControlMessage(PNVCTL_ENCRYPTED_PACKET_HEADER encPacket, PNVCTL_ENET_PACKET_HEADER_V2 packet) {
|
||||||
bool ret = false;
|
|
||||||
int len;
|
|
||||||
unsigned char iv[16];
|
unsigned char iv[16];
|
||||||
|
int encryptedSize = sizeof(*packet) + packet->payloadLength;
|
||||||
|
|
||||||
// This is a truncating cast, but it's what Nvidia does, so we have to mimic it.
|
// This is a truncating cast, but it's what Nvidia does, so we have to mimic it.
|
||||||
memset(iv, 0, sizeof(iv));
|
memset(iv, 0, sizeof(iv));
|
||||||
iv[0] = (unsigned char)encPacket->seq;
|
iv[0] = (unsigned char)encPacket->seq;
|
||||||
|
|
||||||
if (EVP_EncryptInit_ex(cipherContext, EVP_aes_128_gcm(), NULL, NULL, NULL) != 1) {
|
return PltEncryptMessage(encryptionCtx, ALGORITHM_AES_GCM,
|
||||||
goto gcm_cleanup;
|
(unsigned char*)StreamConfig.remoteInputAesKey, sizeof(StreamConfig.remoteInputAesKey),
|
||||||
}
|
iv, sizeof(iv),
|
||||||
|
(unsigned char*)(encPacket + 1), AES_GCM_TAG_LENGTH, // Write tag into the space after the encrypted header
|
||||||
if (EVP_CIPHER_CTX_ctrl(cipherContext, EVP_CTRL_GCM_SET_IVLEN, 16, NULL) != 1) {
|
(unsigned char*)packet, encryptedSize,
|
||||||
goto gcm_cleanup;
|
((unsigned char*)(encPacket + 1)) + AES_GCM_TAG_LENGTH, &encryptedSize); // Write ciphertext after the GCM tag
|
||||||
}
|
|
||||||
|
|
||||||
if (EVP_EncryptInit_ex(cipherContext, NULL, NULL,
|
|
||||||
(const unsigned char*)StreamConfig.remoteInputAesKey, iv) != 1) {
|
|
||||||
goto gcm_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encrypt into the space after the encrypted header and GCM tag
|
|
||||||
int encryptedSize = sizeof(*packet) + packet->payloadLength;
|
|
||||||
if (EVP_EncryptUpdate(cipherContext, ((unsigned char*)(encPacket + 1)) + AES_GCM_TAG_LENGTH,
|
|
||||||
&encryptedSize, (const unsigned char*)packet, encryptedSize) != 1) {
|
|
||||||
goto gcm_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GCM encryption won't ever fill ciphertext here but we have to call it anyway
|
|
||||||
if (EVP_EncryptFinal_ex(cipherContext, ((unsigned char*)(encPacket + 1)), &len) != 1) {
|
|
||||||
goto gcm_cleanup;
|
|
||||||
}
|
|
||||||
LC_ASSERT(len == 0);
|
|
||||||
|
|
||||||
// Read the tag into the space after the encrypted header
|
|
||||||
if (EVP_CIPHER_CTX_ctrl(cipherContext, EVP_CTRL_GCM_GET_TAG, 16, (unsigned char*)(encPacket + 1)) != 1) {
|
|
||||||
ret = -1;
|
|
||||||
goto gcm_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = true;
|
|
||||||
|
|
||||||
gcm_cleanup:
|
|
||||||
EVP_CIPHER_CTX_reset(cipherContext);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Caller must free() *packet on success!!!
|
// Caller must free() *packet on success!!!
|
||||||
static bool decryptControlMessageToV1(PNVCTL_ENCRYPTED_PACKET_HEADER encPacket, PNVCTL_ENET_PACKET_HEADER_V1* packet, int* packetLength) {
|
static bool decryptControlMessageToV1(PNVCTL_ENCRYPTED_PACKET_HEADER encPacket, PNVCTL_ENET_PACKET_HEADER_V1* packet, int* packetLength) {
|
||||||
bool ret = false;
|
|
||||||
int len;
|
|
||||||
unsigned char iv[16];
|
unsigned char iv[16];
|
||||||
|
|
||||||
*packet = NULL;
|
*packet = NULL;
|
||||||
@@ -458,60 +421,28 @@ static bool decryptControlMessageToV1(PNVCTL_ENCRYPTED_PACKET_HEADER encPacket,
|
|||||||
memset(iv, 0, sizeof(iv));
|
memset(iv, 0, sizeof(iv));
|
||||||
iv[0] = (unsigned char)encPacket->seq;
|
iv[0] = (unsigned char)encPacket->seq;
|
||||||
|
|
||||||
if (EVP_DecryptInit_ex(cipherContext, EVP_aes_128_gcm(), NULL, NULL, NULL) != 1) {
|
|
||||||
goto gcm_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EVP_CIPHER_CTX_ctrl(cipherContext, EVP_CTRL_GCM_SET_IVLEN, 16, NULL) != 1) {
|
|
||||||
goto gcm_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EVP_DecryptInit_ex(cipherContext, NULL, NULL,
|
|
||||||
(const unsigned char*)StreamConfig.remoteInputAesKey, iv) != 1) {
|
|
||||||
goto gcm_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
int plaintextLength = encPacket->length - sizeof(encPacket->seq) - AES_GCM_TAG_LENGTH;
|
int plaintextLength = encPacket->length - sizeof(encPacket->seq) - AES_GCM_TAG_LENGTH;
|
||||||
*packet = malloc(plaintextLength);
|
*packet = malloc(plaintextLength);
|
||||||
if (*packet == NULL) {
|
if (*packet == NULL) {
|
||||||
goto gcm_cleanup;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt into the packet we allocated
|
if (!PltDecryptMessage(decryptionCtx, ALGORITHM_AES_GCM,
|
||||||
if (EVP_DecryptUpdate(cipherContext, (unsigned char*)*packet, &plaintextLength,
|
(unsigned char*)StreamConfig.remoteInputAesKey, sizeof(StreamConfig.remoteInputAesKey),
|
||||||
((unsigned char*)(encPacket + 1)) + AES_GCM_TAG_LENGTH, plaintextLength) != 1) {
|
iv, sizeof(iv),
|
||||||
goto gcm_cleanup;
|
(unsigned char*)(encPacket + 1), AES_GCM_TAG_LENGTH, // The tag is located right after the header
|
||||||
|
((unsigned char*)(encPacket + 1)) + AES_GCM_TAG_LENGTH, plaintextLength, // The ciphertext is after the tag
|
||||||
|
(unsigned char*)*packet, &plaintextLength)) {
|
||||||
|
free(*packet);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the GCM tag before calling EVP_DecryptFinal_ex()
|
|
||||||
if (EVP_CIPHER_CTX_ctrl(cipherContext, EVP_CTRL_GCM_SET_TAG, 16, (unsigned char*)(encPacket + 1)) != 1) {
|
|
||||||
ret = -1;
|
|
||||||
goto gcm_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GCM will never have additional plaintext here, but we need to call it to
|
|
||||||
// ensure that the GCM authentication tag is correct for this data.
|
|
||||||
if (EVP_DecryptFinal_ex(cipherContext, (unsigned char*)*packet, &len) != 1) {
|
|
||||||
goto gcm_cleanup;
|
|
||||||
}
|
|
||||||
LC_ASSERT(len == 0);
|
|
||||||
|
|
||||||
// Now we do an in-place V2 to V1 header conversion, so our existing parsing code doesn't have to change.
|
// Now we do an in-place V2 to V1 header conversion, so our existing parsing code doesn't have to change.
|
||||||
// All we need to do is eliminate the new length field in V2 by shifting everything by 2 bytes.
|
// All we need to do is eliminate the new length field in V2 by shifting everything by 2 bytes.
|
||||||
memmove(((unsigned char*)*packet) + 2, ((unsigned char*)*packet) + 4, plaintextLength - 4);
|
memmove(((unsigned char*)*packet) + 2, ((unsigned char*)*packet) + 4, plaintextLength - 4);
|
||||||
*packetLength = plaintextLength - 2;
|
*packetLength = plaintextLength - 2;
|
||||||
|
|
||||||
ret = true;
|
return true;
|
||||||
|
|
||||||
gcm_cleanup:
|
|
||||||
EVP_CIPHER_CTX_reset(cipherContext);
|
|
||||||
|
|
||||||
if (!ret && *packet) {
|
|
||||||
free(*packet);
|
|
||||||
*packet = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sendMessageEnet(short ptype, short paylen, const void* payload) {
|
static bool sendMessageEnet(short ptype, short paylen, const void* payload) {
|
||||||
@@ -760,16 +691,12 @@ static void controlReceiveThreadFunc(void* context) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We (ab)use this lock to protect the cryptoContext too
|
|
||||||
PltLockMutex(&enetMutex);
|
|
||||||
ctlHdr = NULL;
|
ctlHdr = NULL;
|
||||||
if (!decryptControlMessageToV1((PNVCTL_ENCRYPTED_PACKET_HEADER)event.packet->data, &ctlHdr, &packetLength)) {
|
if (!decryptControlMessageToV1((PNVCTL_ENCRYPTED_PACKET_HEADER)event.packet->data, &ctlHdr, &packetLength)) {
|
||||||
PltUnlockMutex(&enetMutex);
|
|
||||||
Limelog("Failed to decrypt control packet of size %d\n", event.packet->dataLength);
|
Limelog("Failed to decrypt control packet of size %d\n", event.packet->dataLength);
|
||||||
enet_packet_destroy(event.packet);
|
enet_packet_destroy(event.packet);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
PltUnlockMutex(&enetMutex);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// What do we do here???
|
// What do we do here???
|
||||||
|
|||||||
+22
-111
@@ -4,13 +4,10 @@
|
|||||||
#include "LinkedBlockingQueue.h"
|
#include "LinkedBlockingQueue.h"
|
||||||
#include "Input.h"
|
#include "Input.h"
|
||||||
|
|
||||||
#include <openssl/evp.h>
|
|
||||||
|
|
||||||
static SOCKET inputSock = INVALID_SOCKET;
|
static SOCKET inputSock = INVALID_SOCKET;
|
||||||
static unsigned char currentAesIv[16];
|
static unsigned char currentAesIv[16];
|
||||||
static bool initialized;
|
static bool initialized;
|
||||||
static EVP_CIPHER_CTX* cipherContext;
|
static PPLT_CRYPTO_CONTEXT cryptoContext;
|
||||||
static bool cipherInitialized;
|
|
||||||
|
|
||||||
static LINKED_BLOCKING_QUEUE packetQueue;
|
static LINKED_BLOCKING_QUEUE packetQueue;
|
||||||
static PLT_THREAD inputSendThread;
|
static PLT_THREAD inputSendThread;
|
||||||
@@ -18,8 +15,6 @@ static PLT_THREAD inputSendThread;
|
|||||||
#define MAX_INPUT_PACKET_SIZE 128
|
#define MAX_INPUT_PACKET_SIZE 128
|
||||||
#define INPUT_STREAM_TIMEOUT_SEC 10
|
#define INPUT_STREAM_TIMEOUT_SEC 10
|
||||||
|
|
||||||
#define ROUND_TO_PKCS7_PADDED_LEN(x) ((((x) + 15) / 16) * 16)
|
|
||||||
|
|
||||||
// Contains input stream packets
|
// Contains input stream packets
|
||||||
typedef struct _PACKET_HOLDER {
|
typedef struct _PACKET_HOLDER {
|
||||||
int packetLength;
|
int packetLength;
|
||||||
@@ -36,19 +31,13 @@ typedef struct _PACKET_HOLDER {
|
|||||||
LINKED_BLOCKING_QUEUE_ENTRY entry;
|
LINKED_BLOCKING_QUEUE_ENTRY entry;
|
||||||
} PACKET_HOLDER, *PPACKET_HOLDER;
|
} PACKET_HOLDER, *PPACKET_HOLDER;
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
||||||
#define EVP_CIPHER_CTX_reset(x) EVP_CIPHER_CTX_cleanup(x); EVP_CIPHER_CTX_init(x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Initializes the input stream
|
// Initializes the input stream
|
||||||
int initializeInputStream(void) {
|
int initializeInputStream(void) {
|
||||||
memcpy(currentAesIv, StreamConfig.remoteInputAesIv, sizeof(currentAesIv));
|
memcpy(currentAesIv, StreamConfig.remoteInputAesIv, sizeof(currentAesIv));
|
||||||
|
|
||||||
// Initialized on first packet
|
|
||||||
cipherInitialized = false;
|
|
||||||
|
|
||||||
LbqInitializeLinkedBlockingQueue(&packetQueue, 30);
|
LbqInitializeLinkedBlockingQueue(&packetQueue, 30);
|
||||||
|
|
||||||
|
cryptoContext = PltCreateCryptoContext();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,10 +45,7 @@ int initializeInputStream(void) {
|
|||||||
void destroyInputStream(void) {
|
void destroyInputStream(void) {
|
||||||
PLINKED_BLOCKING_QUEUE_ENTRY entry, nextEntry;
|
PLINKED_BLOCKING_QUEUE_ENTRY entry, nextEntry;
|
||||||
|
|
||||||
if (cipherInitialized) {
|
PltDestroyCryptoContext(cryptoContext);
|
||||||
EVP_CIPHER_CTX_free(cipherContext);
|
|
||||||
cipherInitialized = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry = LbqDestroyLinkedBlockingQueue(&packetQueue);
|
entry = LbqDestroyLinkedBlockingQueue(&packetQueue);
|
||||||
|
|
||||||
@@ -73,113 +59,38 @@ void destroyInputStream(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int addPkcs7PaddingInPlace(unsigned char* plaintext, int plaintextLen) {
|
static int encryptData(unsigned char* plaintext, int plaintextLen,
|
||||||
int i;
|
|
||||||
int paddedLength = ROUND_TO_PKCS7_PADDED_LEN(plaintextLen);
|
|
||||||
unsigned char paddingByte = (unsigned char)(16 - (plaintextLen % 16));
|
|
||||||
|
|
||||||
for (i = plaintextLen; i < paddedLength; i++) {
|
|
||||||
plaintext[i] = paddingByte;
|
|
||||||
}
|
|
||||||
|
|
||||||
return paddedLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int encryptData(const unsigned char* plaintext, int plaintextLen,
|
|
||||||
unsigned char* ciphertext, int* ciphertextLen) {
|
unsigned char* ciphertext, int* ciphertextLen) {
|
||||||
int ret;
|
// Starting in Gen 7, AES GCM is used for encryption
|
||||||
int len;
|
|
||||||
|
|
||||||
if (AppVersionQuad[0] >= 7) {
|
if (AppVersionQuad[0] >= 7) {
|
||||||
if (!cipherInitialized) {
|
if (!PltEncryptMessage(cryptoContext, ALGORITHM_AES_GCM,
|
||||||
if ((cipherContext = EVP_CIPHER_CTX_new()) == NULL) {
|
(unsigned char*)StreamConfig.remoteInputAesKey, sizeof(StreamConfig.remoteInputAesKey),
|
||||||
return -1;
|
currentAesIv, sizeof(currentAesIv),
|
||||||
}
|
ciphertext, 16,
|
||||||
cipherInitialized = true;
|
plaintext, plaintextLen,
|
||||||
}
|
&ciphertext[16], ciphertextLen)) {
|
||||||
|
return -1;
|
||||||
// Gen 7 servers use 128-bit AES GCM
|
|
||||||
if (EVP_EncryptInit_ex(cipherContext, EVP_aes_128_gcm(), NULL, NULL, NULL) != 1) {
|
|
||||||
ret = -1;
|
|
||||||
goto gcm_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gen 7 servers uses 16 byte IVs
|
|
||||||
if (EVP_CIPHER_CTX_ctrl(cipherContext, EVP_CTRL_GCM_SET_IVLEN, 16, NULL) != 1) {
|
|
||||||
ret = -1;
|
|
||||||
goto gcm_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize again but now provide our key and current IV
|
|
||||||
if (EVP_EncryptInit_ex(cipherContext, NULL, NULL,
|
|
||||||
(const unsigned char*)StreamConfig.remoteInputAesKey, currentAesIv) != 1) {
|
|
||||||
ret = -1;
|
|
||||||
goto gcm_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encrypt into the caller's buffer, leaving room for the auth tag to be prepended
|
|
||||||
if (EVP_EncryptUpdate(cipherContext, &ciphertext[16], ciphertextLen, plaintext, plaintextLen) != 1) {
|
|
||||||
ret = -1;
|
|
||||||
goto gcm_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GCM encryption won't ever fill ciphertext here but we have to call it anyway
|
|
||||||
if (EVP_EncryptFinal_ex(cipherContext, ciphertext, &len) != 1) {
|
|
||||||
ret = -1;
|
|
||||||
goto gcm_cleanup;
|
|
||||||
}
|
|
||||||
LC_ASSERT(len == 0);
|
|
||||||
|
|
||||||
// Read the tag into the caller's buffer
|
|
||||||
if (EVP_CIPHER_CTX_ctrl(cipherContext, EVP_CTRL_GCM_GET_TAG, 16, ciphertext) != 1) {
|
|
||||||
ret = -1;
|
|
||||||
goto gcm_cleanup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment the ciphertextLen to account for the tag
|
// Increment the ciphertextLen to account for the tag
|
||||||
*ciphertextLen += 16;
|
*ciphertextLen += 16;
|
||||||
|
return 0;
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
gcm_cleanup:
|
|
||||||
EVP_CIPHER_CTX_reset(cipherContext);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// PKCS7 padding may need to be added in-place, so we must copy this into a buffer
|
||||||
|
// that can safely be modified.
|
||||||
unsigned char paddedData[MAX_INPUT_PACKET_SIZE];
|
unsigned char paddedData[MAX_INPUT_PACKET_SIZE];
|
||||||
int paddedLength;
|
|
||||||
|
|
||||||
if (!cipherInitialized) {
|
|
||||||
if ((cipherContext = EVP_CIPHER_CTX_new()) == NULL) {
|
|
||||||
ret = -1;
|
|
||||||
goto cbc_cleanup;
|
|
||||||
}
|
|
||||||
cipherInitialized = true;
|
|
||||||
|
|
||||||
// Prior to Gen 7, 128-bit AES CBC is used for encryption
|
|
||||||
if (EVP_EncryptInit_ex(cipherContext, EVP_aes_128_cbc(), NULL,
|
|
||||||
(const unsigned char*)StreamConfig.remoteInputAesKey, currentAesIv) != 1) {
|
|
||||||
ret = -1;
|
|
||||||
goto cbc_cleanup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pad the data to the required block length
|
|
||||||
memcpy(paddedData, plaintext, plaintextLen);
|
memcpy(paddedData, plaintext, plaintextLen);
|
||||||
paddedLength = addPkcs7PaddingInPlace(paddedData, plaintextLen);
|
|
||||||
|
|
||||||
if (EVP_EncryptUpdate(cipherContext, ciphertext, ciphertextLen, paddedData, paddedLength) != 1) {
|
// Prior to Gen 7, 128-bit AES CBC is used for encryption
|
||||||
ret = -1;
|
return PltEncryptMessage(cryptoContext, ALGORITHM_AES_CBC,
|
||||||
goto cbc_cleanup;
|
(unsigned char*)StreamConfig.remoteInputAesKey, sizeof(StreamConfig.remoteInputAesKey),
|
||||||
}
|
currentAesIv, sizeof(currentAesIv),
|
||||||
|
NULL, 0,
|
||||||
ret = 0;
|
paddedData, plaintextLen,
|
||||||
|
ciphertext, ciphertextLen) ? 0 : -1;
|
||||||
cbc_cleanup:
|
|
||||||
// Nothing to do
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input thread proc
|
// Input thread proc
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
#include "PlatformSockets.h"
|
#include "PlatformSockets.h"
|
||||||
#include "PlatformThreads.h"
|
#include "PlatformThreads.h"
|
||||||
|
#include "PlatformCrypto.h"
|
||||||
#include "Video.h"
|
#include "Video.h"
|
||||||
#include "RtpFecQueue.h"
|
#include "RtpFecQueue.h"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,197 @@
|
|||||||
|
#include "Limelight-internal.h"
|
||||||
|
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
|
||||||
|
static int addPkcs7PaddingInPlace(unsigned char* plaintext, int plaintextLen) {
|
||||||
|
int paddedLength = ROUND_TO_PKCS7_PADDED_LEN(plaintextLen);
|
||||||
|
unsigned char paddingByte = (unsigned char)(16 - (plaintextLen % 16));
|
||||||
|
|
||||||
|
memset(&plaintext[plaintextLen], paddingByte, paddedLength - plaintextLen);
|
||||||
|
|
||||||
|
return paddedLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For CBC modes, inputData buffer must be allocated with length rounded up to next multiple of 16 and inputData buffer may be modified!
|
||||||
|
bool PltEncryptMessage(PPLT_CRYPTO_CONTEXT ctx, int algorithm,
|
||||||
|
unsigned char* key, int keyLength,
|
||||||
|
unsigned char* iv, int ivLength,
|
||||||
|
unsigned char* tag, int tagLength,
|
||||||
|
unsigned char* inputData, int inputDataLength,
|
||||||
|
unsigned char* outputData, int* outputDataLength) {
|
||||||
|
bool ret = false;
|
||||||
|
const EVP_CIPHER* cipher;
|
||||||
|
|
||||||
|
switch (algorithm) {
|
||||||
|
case ALGORITHM_AES_CBC:
|
||||||
|
LC_ASSERT(keyLength == 16);
|
||||||
|
LC_ASSERT(tag == NULL);
|
||||||
|
LC_ASSERT(tagLength == 0);
|
||||||
|
cipher = EVP_aes_128_cbc();
|
||||||
|
break;
|
||||||
|
case ALGORITHM_AES_GCM:
|
||||||
|
LC_ASSERT(keyLength == 16);
|
||||||
|
LC_ASSERT(tag != NULL);
|
||||||
|
LC_ASSERT(tagLength > 0);
|
||||||
|
cipher = EVP_aes_128_gcm();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LC_ASSERT(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (algorithm == ALGORITHM_AES_GCM) {
|
||||||
|
if (EVP_EncryptInit_ex(ctx->ctx, cipher, NULL, NULL, NULL) != 1) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVP_CIPHER_CTX_ctrl(ctx->ctx, EVP_CTRL_GCM_SET_IVLEN, ivLength, NULL) != 1) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVP_EncryptInit_ex(ctx->ctx, NULL, NULL, key, iv) != 1) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!ctx->initialized) {
|
||||||
|
if (EVP_EncryptInit_ex(ctx->ctx, cipher, NULL, key, iv) != 1) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputDataLength = addPkcs7PaddingInPlace(inputData, inputDataLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVP_EncryptUpdate(ctx->ctx, outputData, outputDataLength, inputData, inputDataLength) != 1) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (algorithm == ALGORITHM_AES_GCM) {
|
||||||
|
int len;
|
||||||
|
|
||||||
|
// GCM encryption won't ever fill ciphertext here but we have to call it anyway
|
||||||
|
if (EVP_EncryptFinal_ex(ctx->ctx, outputData, &len) != 1) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
LC_ASSERT(len == 0);
|
||||||
|
|
||||||
|
if (EVP_CIPHER_CTX_ctrl(ctx->ctx, EVP_CTRL_GCM_GET_TAG, tagLength, tag) != 1) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (algorithm == ALGORITHM_AES_GCM) {
|
||||||
|
EVP_CIPHER_CTX_reset(ctx->ctx);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PltDecryptMessage(PPLT_CRYPTO_CONTEXT ctx, int algorithm,
|
||||||
|
unsigned char* key, int keyLength,
|
||||||
|
unsigned char* iv, int ivLength,
|
||||||
|
unsigned char* tag, int tagLength,
|
||||||
|
unsigned char* inputData, int inputDataLength,
|
||||||
|
unsigned char* outputData, int* outputDataLength) {
|
||||||
|
bool ret = false;
|
||||||
|
const EVP_CIPHER* cipher;
|
||||||
|
|
||||||
|
switch (algorithm) {
|
||||||
|
case ALGORITHM_AES_CBC:
|
||||||
|
LC_ASSERT(keyLength == 16);
|
||||||
|
LC_ASSERT(tag == NULL);
|
||||||
|
LC_ASSERT(tagLength == 0);
|
||||||
|
cipher = EVP_aes_128_cbc();
|
||||||
|
break;
|
||||||
|
case ALGORITHM_AES_GCM:
|
||||||
|
LC_ASSERT(keyLength == 16);
|
||||||
|
LC_ASSERT(tag != NULL);
|
||||||
|
LC_ASSERT(tagLength > 0);
|
||||||
|
cipher = EVP_aes_128_gcm();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LC_ASSERT(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (algorithm == ALGORITHM_AES_GCM) {
|
||||||
|
if (EVP_DecryptInit_ex(ctx->ctx, cipher, NULL, NULL, NULL) != 1) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVP_CIPHER_CTX_ctrl(ctx->ctx, EVP_CTRL_GCM_SET_IVLEN, ivLength, NULL) != 1) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVP_DecryptInit_ex(ctx->ctx, NULL, NULL, key, iv) != 1) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!ctx->initialized) {
|
||||||
|
if (EVP_DecryptInit_ex(ctx->ctx, cipher, NULL, key, iv) != 1) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVP_DecryptUpdate(ctx->ctx, outputData, outputDataLength, inputData, inputDataLength) != 1) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (algorithm == ALGORITHM_AES_GCM) {
|
||||||
|
int len;
|
||||||
|
|
||||||
|
// Set the GCM tag before calling EVP_DecryptFinal_ex()
|
||||||
|
if (EVP_CIPHER_CTX_ctrl(ctx->ctx, EVP_CTRL_GCM_SET_TAG, tagLength, tag) != 1) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GCM will never have additional plaintext here, but we need to call it to
|
||||||
|
// ensure that the GCM authentication tag is correct for this data.
|
||||||
|
if (EVP_DecryptFinal_ex(ctx->ctx, outputData, &len) != 1) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
LC_ASSERT(len == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (algorithm == ALGORITHM_AES_GCM) {
|
||||||
|
EVP_CIPHER_CTX_reset(ctx->ctx);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
PPLT_CRYPTO_CONTEXT PltCreateCryptoContext(void) {
|
||||||
|
PPLT_CRYPTO_CONTEXT ctx = malloc(sizeof(*ctx));
|
||||||
|
if (!ctx) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->initialized = false;
|
||||||
|
ctx->ctx = EVP_CIPHER_CTX_new();
|
||||||
|
if (!ctx->ctx) {
|
||||||
|
free(ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PltDestroyCryptoContext(PPLT_CRYPTO_CONTEXT ctx) {
|
||||||
|
EVP_CIPHER_CTX_free(ctx->ctx);
|
||||||
|
free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PltGenerateRandomData(unsigned char* data, int length) {
|
||||||
|
RAND_bytes(data, length);
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
// Hide the real OpenSSL definition from other code
|
||||||
|
typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX;
|
||||||
|
|
||||||
|
typedef struct _PLT_CRYPTO_CONTEXT {
|
||||||
|
bool initialized; // Used for CBC only
|
||||||
|
EVP_CIPHER_CTX* ctx;
|
||||||
|
} PLT_CRYPTO_CONTEXT, *PPLT_CRYPTO_CONTEXT;
|
||||||
|
|
||||||
|
#define ROUND_TO_PKCS7_PADDED_LEN(x) ((((x) + 15) / 16) * 16)
|
||||||
|
|
||||||
|
PPLT_CRYPTO_CONTEXT PltCreateCryptoContext();
|
||||||
|
void PltDestroyCryptoContext(PPLT_CRYPTO_CONTEXT ctx);
|
||||||
|
|
||||||
|
#define ALGORITHM_AES_CBC 1
|
||||||
|
#define ALGORITHM_AES_GCM 2
|
||||||
|
|
||||||
|
bool PltEncryptMessage(PPLT_CRYPTO_CONTEXT ctx, int algorithm,
|
||||||
|
unsigned char* key, int keyLength,
|
||||||
|
unsigned char* iv, int ivLength,
|
||||||
|
unsigned char* tag, int tagLength,
|
||||||
|
unsigned char* inputData, int inputDataLength,
|
||||||
|
unsigned char* outputData, int* outputDataLength);
|
||||||
|
|
||||||
|
bool PltDecryptMessage(PPLT_CRYPTO_CONTEXT ctx, int algorithm,
|
||||||
|
unsigned char* key, int keyLength,
|
||||||
|
unsigned char* iv, int ivLength,
|
||||||
|
unsigned char* tag, int tagLength,
|
||||||
|
unsigned char* inputData, int inputDataLength,
|
||||||
|
unsigned char* outputData, int* outputDataLength);
|
||||||
|
|
||||||
|
void PltGenerateRandomData(unsigned char* data, int length);
|
||||||
+1
-3
@@ -1,7 +1,5 @@
|
|||||||
#include "Limelight-internal.h"
|
#include "Limelight-internal.h"
|
||||||
|
|
||||||
#include <openssl/rand.h>
|
|
||||||
|
|
||||||
#define STUN_RECV_TIMEOUT_SEC 3
|
#define STUN_RECV_TIMEOUT_SEC 3
|
||||||
|
|
||||||
#define STUN_MESSAGE_BINDING_REQUEST 0x0001
|
#define STUN_MESSAGE_BINDING_REQUEST 0x0001
|
||||||
@@ -85,7 +83,7 @@ int LiFindExternalAddressIP4(const char* stunServer, unsigned short stunPort, un
|
|||||||
reqMsg.messageType = htons(STUN_MESSAGE_BINDING_REQUEST);
|
reqMsg.messageType = htons(STUN_MESSAGE_BINDING_REQUEST);
|
||||||
reqMsg.messageLength = 0;
|
reqMsg.messageLength = 0;
|
||||||
reqMsg.magicCookie = htonl(STUN_MESSAGE_COOKIE);
|
reqMsg.magicCookie = htonl(STUN_MESSAGE_COOKIE);
|
||||||
RAND_bytes(reqMsg.transactionId, sizeof(reqMsg.transactionId));
|
PltGenerateRandomData(reqMsg.transactionId, sizeof(reqMsg.transactionId));
|
||||||
|
|
||||||
bytesRead = SOCKET_ERROR;
|
bytesRead = SOCKET_ERROR;
|
||||||
for (i = 0; i < STUN_RECV_TIMEOUT_SEC * 1000 / UDP_RECV_POLL_TIMEOUT_MS && bytesRead <= 0; i++) {
|
for (i = 0; i < STUN_RECV_TIMEOUT_SEC * 1000 / UDP_RECV_POLL_TIMEOUT_MS && bytesRead <= 0; i++) {
|
||||||
|
|||||||
Reference in New Issue
Block a user