mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2025-07-01 07:15:39 +00:00
Add video encryption support
This commit is contained in:
parent
6083a75d1b
commit
b74b6e883c
@ -32,6 +32,7 @@ extern "C" {
|
||||
// Values for 'encryptionFlags' field below
|
||||
#define ENCFLG_NONE 0x00000000
|
||||
#define ENCFLG_AUDIO 0x00000001
|
||||
#define ENCFLG_VIDEO 0x00000002
|
||||
#define ENCFLG_ALL 0xFFFFFFFF
|
||||
|
||||
// This function returns a string that you SHOULD append to the /launch and /resume
|
||||
|
@ -276,6 +276,18 @@ static PSDP_OPTION getAttributesList(char*urlSafeAddr) {
|
||||
if (EncryptionFeaturesSupported & SS_ENC_CONTROL_V2) {
|
||||
EncryptionFeaturesEnabled |= SS_ENC_CONTROL_V2;
|
||||
}
|
||||
|
||||
// If video encryption is supported by the host and desired by the client, use it
|
||||
if ((EncryptionFeaturesSupported & SS_ENC_VIDEO) && (StreamConfig.encryptionFlags & ENCFLG_VIDEO)) {
|
||||
EncryptionFeaturesEnabled |= SS_ENC_VIDEO;
|
||||
}
|
||||
else if ((EncryptionFeaturesRequested & SS_ENC_VIDEO) && !(StreamConfig.encryptionFlags & ENCFLG_VIDEO)) {
|
||||
// If video encryption is explicitly requested by the host but *not* by the client,
|
||||
// we'll encrypt anyway (since we are capable of doing so) and print a warning.
|
||||
Limelog("Enabling video encryption by host request despite client opt-out. Performance may suffer!");
|
||||
EncryptionFeaturesEnabled |= SS_ENC_VIDEO;
|
||||
}
|
||||
|
||||
snprintf(payloadStr, sizeof(payloadStr), "%u", EncryptionFeaturesEnabled);
|
||||
err |= addAttributeString(&optionHead, "x-ss-general.encryptionEnabled", payloadStr);
|
||||
}
|
||||
|
@ -9,6 +9,11 @@ typedef struct _QUEUED_DECODE_UNIT {
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
typedef struct _ENC_VIDEO_HEADER {
|
||||
uint8_t iv[16];
|
||||
uint8_t tag[16];
|
||||
} ENC_VIDEO_HEADER, *PENC_VIDEO_HEADER;
|
||||
|
||||
#define FLAG_CONTAINS_PIC_DATA 0x1
|
||||
#define FLAG_EOF 0x2
|
||||
#define FLAG_SOF 0x4
|
||||
|
@ -10,6 +10,8 @@ static RTP_VIDEO_QUEUE rtpQueue;
|
||||
static SOCKET rtpSocket = INVALID_SOCKET;
|
||||
static SOCKET firstFrameSocket = INVALID_SOCKET;
|
||||
|
||||
static PPLT_CRYPTO_CONTEXT decryptionCtx;
|
||||
|
||||
static PLT_THREAD udpPingThread;
|
||||
static PLT_THREAD receiveThread;
|
||||
static PLT_THREAD decoderThread;
|
||||
@ -36,6 +38,7 @@ static bool receivedFullFrame;
|
||||
void initializeVideoStream(void) {
|
||||
initializeVideoDepacketizer(StreamConfig.packetSize);
|
||||
RtpvInitializeQueue(&rtpQueue);
|
||||
decryptionCtx = PltCreateCryptoContext();
|
||||
receivedDataFromPeer = false;
|
||||
firstDataTimeMs = 0;
|
||||
receivedFullFrame = false;
|
||||
@ -43,6 +46,7 @@ void initializeVideoStream(void) {
|
||||
|
||||
// Clean up the video stream
|
||||
void destroyVideoStream(void) {
|
||||
PltDestroyCryptoContext(decryptionCtx);
|
||||
destroyVideoDepacketizer();
|
||||
RtpvCleanupQueue(&rtpQueue);
|
||||
}
|
||||
@ -80,14 +84,19 @@ static void VideoPingThreadProc(void* context) {
|
||||
// Receive thread proc
|
||||
static void VideoReceiveThreadProc(void* context) {
|
||||
int err;
|
||||
int bufferSize, receiveSize;
|
||||
int bufferSize, receiveSize, decryptedSize, minSize;
|
||||
char* buffer;
|
||||
char* encryptedBuffer;
|
||||
int queueStatus;
|
||||
bool useSelect;
|
||||
int waitingForVideoMs;
|
||||
bool encrypted;
|
||||
|
||||
receiveSize = StreamConfig.packetSize + MAX_RTP_HEADER_SIZE;
|
||||
bufferSize = receiveSize + sizeof(RTPV_QUEUE_ENTRY);
|
||||
encrypted = !!(EncryptionFeaturesEnabled & SS_ENC_VIDEO);
|
||||
decryptedSize = StreamConfig.packetSize + MAX_RTP_HEADER_SIZE;
|
||||
minSize = sizeof(RTP_PACKET) + ((EncryptionFeaturesEnabled & SS_ENC_VIDEO) ? sizeof(ENC_VIDEO_HEADER) : 0);
|
||||
receiveSize = decryptedSize + ((EncryptionFeaturesEnabled & SS_ENC_VIDEO) ? sizeof(ENC_VIDEO_HEADER) : 0);
|
||||
bufferSize = decryptedSize + sizeof(RTPV_QUEUE_ENTRY);
|
||||
buffer = NULL;
|
||||
|
||||
if (setNonFatalRecvTimeoutMs(rtpSocket, UDP_RECV_POLL_TIMEOUT_MS) < 0) {
|
||||
@ -99,6 +108,19 @@ static void VideoReceiveThreadProc(void* context) {
|
||||
useSelect = false;
|
||||
}
|
||||
|
||||
// Allocate a staging buffer to use for each received packet
|
||||
if (encrypted) {
|
||||
encryptedBuffer = (char*)malloc(receiveSize);
|
||||
if (encryptedBuffer == NULL) {
|
||||
Limelog("Video Receive: malloc() failed\n");
|
||||
ListenerCallbacks.connectionTerminated(-1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
encryptedBuffer = NULL;
|
||||
}
|
||||
|
||||
waitingForVideoMs = 0;
|
||||
while (!PltIsThreadInterrupted(&receiveThread)) {
|
||||
PRTP_PACKET packet;
|
||||
@ -108,11 +130,14 @@ static void VideoReceiveThreadProc(void* context) {
|
||||
if (buffer == NULL) {
|
||||
Limelog("Video Receive: malloc() failed\n");
|
||||
ListenerCallbacks.connectionTerminated(-1);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
err = recvUdpSocket(rtpSocket, buffer, receiveSize, useSelect);
|
||||
err = recvUdpSocket(rtpSocket,
|
||||
encrypted ? encryptedBuffer : buffer,
|
||||
receiveSize,
|
||||
useSelect);
|
||||
if (err < 0) {
|
||||
Limelog("Video Receive: recvUdpSocket() failed: %d\n", (int)LastSocketError());
|
||||
ListenerCallbacks.connectionTerminated(LastSocketFail());
|
||||
@ -153,18 +178,33 @@ static void VideoReceiveThreadProc(void* context) {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (err < (int)sizeof(RTP_PACKET)) {
|
||||
if (err < minSize) {
|
||||
// Runt packet
|
||||
continue;
|
||||
}
|
||||
|
||||
// Decrypt the packet into the buffer if encryption is enabled
|
||||
if (encrypted) {
|
||||
PENC_VIDEO_HEADER encHeader = (PENC_VIDEO_HEADER)encryptedBuffer;
|
||||
|
||||
if (!PltDecryptMessage(decryptionCtx, ALGORITHM_AES_GCM, 0,
|
||||
(unsigned char*)StreamConfig.remoteInputAesKey, sizeof(StreamConfig.remoteInputAesKey),
|
||||
encHeader->iv, sizeof(encHeader->iv),
|
||||
encHeader->tag, sizeof(encHeader->tag),
|
||||
((unsigned char*)(encHeader + 1)), err - sizeof(ENC_VIDEO_HEADER), // The ciphertext is after the header
|
||||
(unsigned char*)buffer, &err)) {
|
||||
Limelog("Failed to decrypt video packet!\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert fields to host byte-order
|
||||
packet = (PRTP_PACKET)&buffer[0];
|
||||
packet->sequenceNumber = BE16(packet->sequenceNumber);
|
||||
packet->timestamp = BE32(packet->timestamp);
|
||||
packet->ssrc = BE32(packet->ssrc);
|
||||
|
||||
queueStatus = RtpvAddPacket(&rtpQueue, packet, err, (PRTPV_QUEUE_ENTRY)&buffer[receiveSize]);
|
||||
queueStatus = RtpvAddPacket(&rtpQueue, packet, err, (PRTPV_QUEUE_ENTRY)&buffer[decryptedSize]);
|
||||
|
||||
if (queueStatus == RTPF_RET_QUEUED) {
|
||||
// The queue owns the buffer
|
||||
@ -175,6 +215,10 @@ static void VideoReceiveThreadProc(void* context) {
|
||||
if (buffer != NULL) {
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
if (encryptedBuffer != NULL) {
|
||||
free(encryptedBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void notifyKeyFrameReceived(void) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user