mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2026-02-16 02:21:07 +00:00
Refactor OpenSSL usage into a platform-specific file to allow other crypto backends
This commit is contained in:
@@ -6,8 +6,6 @@
|
||||
|
||||
#include <enet/enet.h>
|
||||
|
||||
#include <openssl/evp.h>
|
||||
|
||||
// NV control stream packet header for TCP
|
||||
typedef struct _NVCTL_TCP_PACKET_HEADER {
|
||||
unsigned short type;
|
||||
@@ -65,11 +63,8 @@ static int currentEnetSequenceNumber;
|
||||
static bool idrFrameRequired;
|
||||
static LINKED_BLOCKING_QUEUE invalidReferenceFrameTuples;
|
||||
|
||||
static EVP_CIPHER_CTX* cipherContext;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
#define EVP_CIPHER_CTX_reset(x) EVP_CIPHER_CTX_cleanup(x); EVP_CIPHER_CTX_init(x)
|
||||
#endif
|
||||
static PPLT_CRYPTO_CONTEXT encryptionCtx;
|
||||
static PPLT_CRYPTO_CONTEXT decryptionCtx;
|
||||
|
||||
#define CONN_IMMEDIATE_POOR_LOSS_RATE 30
|
||||
#define CONN_CONSECUTIVE_POOR_LOSS_RATE 15
|
||||
@@ -251,7 +246,8 @@ int initializeControlStream(void) {
|
||||
lastConnectionStatusUpdate = CONN_STATUS_OKAY;
|
||||
currentEnetSequenceNumber = 0;
|
||||
usePeriodicPing = APP_VERSION_AT_LEAST(7, 1, 415);
|
||||
cipherContext = EVP_CIPHER_CTX_new();
|
||||
encryptionCtx = PltCreateCryptoContext();
|
||||
decryptionCtx = PltCreateCryptoContext();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -269,7 +265,8 @@ void freeFrameInvalidationList(PLINKED_BLOCKING_QUEUE_ENTRY entry) {
|
||||
// Cleans up control stream
|
||||
void destroyControlStream(void) {
|
||||
LC_ASSERT(stopping);
|
||||
EVP_CIPHER_CTX_free(cipherContext);
|
||||
PltDestroyCryptoContext(encryptionCtx);
|
||||
PltDestroyCryptoContext(decryptionCtx);
|
||||
PltCloseEvent(&invalidateRefFramesEvent);
|
||||
freeFrameInvalidationList(LbqDestroyLinkedBlockingQueue(&invalidReferenceFrameTuples));
|
||||
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) {
|
||||
bool ret = false;
|
||||
int len;
|
||||
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.
|
||||
memset(iv, 0, sizeof(iv));
|
||||
iv[0] = (unsigned char)encPacket->seq;
|
||||
|
||||
if (EVP_EncryptInit_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_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;
|
||||
return PltEncryptMessage(encryptionCtx, ALGORITHM_AES_GCM,
|
||||
(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
|
||||
(unsigned char*)packet, encryptedSize,
|
||||
((unsigned char*)(encPacket + 1)) + AES_GCM_TAG_LENGTH, &encryptedSize); // Write ciphertext after the GCM tag
|
||||
}
|
||||
|
||||
// Caller must free() *packet on success!!!
|
||||
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];
|
||||
|
||||
*packet = NULL;
|
||||
@@ -458,60 +421,28 @@ static bool decryptControlMessageToV1(PNVCTL_ENCRYPTED_PACKET_HEADER encPacket,
|
||||
memset(iv, 0, sizeof(iv));
|
||||
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;
|
||||
*packet = malloc(plaintextLength);
|
||||
if (*packet == NULL) {
|
||||
goto gcm_cleanup;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Decrypt into the packet we allocated
|
||||
if (EVP_DecryptUpdate(cipherContext, (unsigned char*)*packet, &plaintextLength,
|
||||
((unsigned char*)(encPacket + 1)) + AES_GCM_TAG_LENGTH, plaintextLength) != 1) {
|
||||
goto gcm_cleanup;
|
||||
if (!PltDecryptMessage(decryptionCtx, ALGORITHM_AES_GCM,
|
||||
(unsigned char*)StreamConfig.remoteInputAesKey, sizeof(StreamConfig.remoteInputAesKey),
|
||||
iv, sizeof(iv),
|
||||
(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.
|
||||
// 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);
|
||||
*packetLength = plaintextLength - 2;
|
||||
|
||||
ret = true;
|
||||
|
||||
gcm_cleanup:
|
||||
EVP_CIPHER_CTX_reset(cipherContext);
|
||||
|
||||
if (!ret && *packet) {
|
||||
free(*packet);
|
||||
*packet = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sendMessageEnet(short ptype, short paylen, const void* payload) {
|
||||
@@ -760,16 +691,12 @@ static void controlReceiveThreadFunc(void* context) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We (ab)use this lock to protect the cryptoContext too
|
||||
PltLockMutex(&enetMutex);
|
||||
ctlHdr = NULL;
|
||||
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);
|
||||
enet_packet_destroy(event.packet);
|
||||
continue;
|
||||
}
|
||||
PltUnlockMutex(&enetMutex);
|
||||
}
|
||||
else {
|
||||
// What do we do here???
|
||||
|
||||
@@ -4,13 +4,10 @@
|
||||
#include "LinkedBlockingQueue.h"
|
||||
#include "Input.h"
|
||||
|
||||
#include <openssl/evp.h>
|
||||
|
||||
static SOCKET inputSock = INVALID_SOCKET;
|
||||
static unsigned char currentAesIv[16];
|
||||
static bool initialized;
|
||||
static EVP_CIPHER_CTX* cipherContext;
|
||||
static bool cipherInitialized;
|
||||
static PPLT_CRYPTO_CONTEXT cryptoContext;
|
||||
|
||||
static LINKED_BLOCKING_QUEUE packetQueue;
|
||||
static PLT_THREAD inputSendThread;
|
||||
@@ -18,8 +15,6 @@ static PLT_THREAD inputSendThread;
|
||||
#define MAX_INPUT_PACKET_SIZE 128
|
||||
#define INPUT_STREAM_TIMEOUT_SEC 10
|
||||
|
||||
#define ROUND_TO_PKCS7_PADDED_LEN(x) ((((x) + 15) / 16) * 16)
|
||||
|
||||
// Contains input stream packets
|
||||
typedef struct _PACKET_HOLDER {
|
||||
int packetLength;
|
||||
@@ -36,19 +31,13 @@ typedef struct _PACKET_HOLDER {
|
||||
LINKED_BLOCKING_QUEUE_ENTRY entry;
|
||||
} 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
|
||||
int initializeInputStream(void) {
|
||||
memcpy(currentAesIv, StreamConfig.remoteInputAesIv, sizeof(currentAesIv));
|
||||
|
||||
// Initialized on first packet
|
||||
cipherInitialized = false;
|
||||
|
||||
LbqInitializeLinkedBlockingQueue(&packetQueue, 30);
|
||||
|
||||
cryptoContext = PltCreateCryptoContext();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -56,10 +45,7 @@ int initializeInputStream(void) {
|
||||
void destroyInputStream(void) {
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY entry, nextEntry;
|
||||
|
||||
if (cipherInitialized) {
|
||||
EVP_CIPHER_CTX_free(cipherContext);
|
||||
cipherInitialized = false;
|
||||
}
|
||||
PltDestroyCryptoContext(cryptoContext);
|
||||
|
||||
entry = LbqDestroyLinkedBlockingQueue(&packetQueue);
|
||||
|
||||
@@ -73,113 +59,38 @@ void destroyInputStream(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static int addPkcs7PaddingInPlace(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,
|
||||
static int encryptData(unsigned char* plaintext, int plaintextLen,
|
||||
unsigned char* ciphertext, int* ciphertextLen) {
|
||||
int ret;
|
||||
int len;
|
||||
|
||||
// Starting in Gen 7, AES GCM is used for encryption
|
||||
if (AppVersionQuad[0] >= 7) {
|
||||
if (!cipherInitialized) {
|
||||
if ((cipherContext = EVP_CIPHER_CTX_new()) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
cipherInitialized = true;
|
||||
if (!PltEncryptMessage(cryptoContext, ALGORITHM_AES_GCM,
|
||||
(unsigned char*)StreamConfig.remoteInputAesKey, sizeof(StreamConfig.remoteInputAesKey),
|
||||
currentAesIv, sizeof(currentAesIv),
|
||||
ciphertext, 16,
|
||||
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
|
||||
*ciphertextLen += 16;
|
||||
|
||||
ret = 0;
|
||||
|
||||
gcm_cleanup:
|
||||
EVP_CIPHER_CTX_reset(cipherContext);
|
||||
return 0;
|
||||
}
|
||||
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];
|
||||
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);
|
||||
paddedLength = addPkcs7PaddingInPlace(paddedData, plaintextLen);
|
||||
|
||||
if (EVP_EncryptUpdate(cipherContext, ciphertext, ciphertextLen, paddedData, paddedLength) != 1) {
|
||||
ret = -1;
|
||||
goto cbc_cleanup;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
cbc_cleanup:
|
||||
// Nothing to do
|
||||
;
|
||||
// Prior to Gen 7, 128-bit AES CBC is used for encryption
|
||||
return PltEncryptMessage(cryptoContext, ALGORITHM_AES_CBC,
|
||||
(unsigned char*)StreamConfig.remoteInputAesKey, sizeof(StreamConfig.remoteInputAesKey),
|
||||
currentAesIv, sizeof(currentAesIv),
|
||||
NULL, 0,
|
||||
paddedData, plaintextLen,
|
||||
ciphertext, ciphertextLen) ? 0 : -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Input thread proc
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "Platform.h"
|
||||
#include "PlatformSockets.h"
|
||||
#include "PlatformThreads.h"
|
||||
#include "PlatformCrypto.h"
|
||||
#include "Video.h"
|
||||
#include "RtpFecQueue.h"
|
||||
|
||||
|
||||
197
src/PlatformCrypto.c
Normal file
197
src/PlatformCrypto.c
Normal file
@@ -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);
|
||||
}
|
||||
35
src/PlatformCrypto.h
Normal file
35
src/PlatformCrypto.h
Normal file
@@ -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,7 +1,5 @@
|
||||
#include "Limelight-internal.h"
|
||||
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#define STUN_RECV_TIMEOUT_SEC 3
|
||||
|
||||
#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.messageLength = 0;
|
||||
reqMsg.magicCookie = htonl(STUN_MESSAGE_COOKIE);
|
||||
RAND_bytes(reqMsg.transactionId, sizeof(reqMsg.transactionId));
|
||||
PltGenerateRandomData(reqMsg.transactionId, sizeof(reqMsg.transactionId));
|
||||
|
||||
bytesRead = SOCKET_ERROR;
|
||||
for (i = 0; i < STUN_RECV_TIMEOUT_SEC * 1000 / UDP_RECV_POLL_TIMEOUT_MS && bytesRead <= 0; i++) {
|
||||
|
||||
Reference in New Issue
Block a user