mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2025-07-01 15:25:43 +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
|
// Values for 'encryptionFlags' field below
|
||||||
#define ENCFLG_NONE 0x00000000
|
#define ENCFLG_NONE 0x00000000
|
||||||
#define ENCFLG_AUDIO 0x00000001
|
#define ENCFLG_AUDIO 0x00000001
|
||||||
|
#define ENCFLG_VIDEO 0x00000002
|
||||||
#define ENCFLG_ALL 0xFFFFFFFF
|
#define ENCFLG_ALL 0xFFFFFFFF
|
||||||
|
|
||||||
// This function returns a string that you SHOULD append to the /launch and /resume
|
// 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) {
|
if (EncryptionFeaturesSupported & SS_ENC_CONTROL_V2) {
|
||||||
EncryptionFeaturesEnabled |= 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);
|
snprintf(payloadStr, sizeof(payloadStr), "%u", EncryptionFeaturesEnabled);
|
||||||
err |= addAttributeString(&optionHead, "x-ss-general.encryptionEnabled", payloadStr);
|
err |= addAttributeString(&optionHead, "x-ss-general.encryptionEnabled", payloadStr);
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,11 @@ typedef struct _QUEUED_DECODE_UNIT {
|
|||||||
|
|
||||||
#pragma pack(push, 1)
|
#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_CONTAINS_PIC_DATA 0x1
|
||||||
#define FLAG_EOF 0x2
|
#define FLAG_EOF 0x2
|
||||||
#define FLAG_SOF 0x4
|
#define FLAG_SOF 0x4
|
||||||
|
@ -10,6 +10,8 @@ static RTP_VIDEO_QUEUE rtpQueue;
|
|||||||
static SOCKET rtpSocket = INVALID_SOCKET;
|
static SOCKET rtpSocket = INVALID_SOCKET;
|
||||||
static SOCKET firstFrameSocket = INVALID_SOCKET;
|
static SOCKET firstFrameSocket = INVALID_SOCKET;
|
||||||
|
|
||||||
|
static PPLT_CRYPTO_CONTEXT decryptionCtx;
|
||||||
|
|
||||||
static PLT_THREAD udpPingThread;
|
static PLT_THREAD udpPingThread;
|
||||||
static PLT_THREAD receiveThread;
|
static PLT_THREAD receiveThread;
|
||||||
static PLT_THREAD decoderThread;
|
static PLT_THREAD decoderThread;
|
||||||
@ -36,6 +38,7 @@ static bool receivedFullFrame;
|
|||||||
void initializeVideoStream(void) {
|
void initializeVideoStream(void) {
|
||||||
initializeVideoDepacketizer(StreamConfig.packetSize);
|
initializeVideoDepacketizer(StreamConfig.packetSize);
|
||||||
RtpvInitializeQueue(&rtpQueue);
|
RtpvInitializeQueue(&rtpQueue);
|
||||||
|
decryptionCtx = PltCreateCryptoContext();
|
||||||
receivedDataFromPeer = false;
|
receivedDataFromPeer = false;
|
||||||
firstDataTimeMs = 0;
|
firstDataTimeMs = 0;
|
||||||
receivedFullFrame = false;
|
receivedFullFrame = false;
|
||||||
@ -43,6 +46,7 @@ void initializeVideoStream(void) {
|
|||||||
|
|
||||||
// Clean up the video stream
|
// Clean up the video stream
|
||||||
void destroyVideoStream(void) {
|
void destroyVideoStream(void) {
|
||||||
|
PltDestroyCryptoContext(decryptionCtx);
|
||||||
destroyVideoDepacketizer();
|
destroyVideoDepacketizer();
|
||||||
RtpvCleanupQueue(&rtpQueue);
|
RtpvCleanupQueue(&rtpQueue);
|
||||||
}
|
}
|
||||||
@ -80,14 +84,19 @@ static void VideoPingThreadProc(void* context) {
|
|||||||
// Receive thread proc
|
// Receive thread proc
|
||||||
static void VideoReceiveThreadProc(void* context) {
|
static void VideoReceiveThreadProc(void* context) {
|
||||||
int err;
|
int err;
|
||||||
int bufferSize, receiveSize;
|
int bufferSize, receiveSize, decryptedSize, minSize;
|
||||||
char* buffer;
|
char* buffer;
|
||||||
|
char* encryptedBuffer;
|
||||||
int queueStatus;
|
int queueStatus;
|
||||||
bool useSelect;
|
bool useSelect;
|
||||||
int waitingForVideoMs;
|
int waitingForVideoMs;
|
||||||
|
bool encrypted;
|
||||||
|
|
||||||
receiveSize = StreamConfig.packetSize + MAX_RTP_HEADER_SIZE;
|
encrypted = !!(EncryptionFeaturesEnabled & SS_ENC_VIDEO);
|
||||||
bufferSize = receiveSize + sizeof(RTPV_QUEUE_ENTRY);
|
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;
|
buffer = NULL;
|
||||||
|
|
||||||
if (setNonFatalRecvTimeoutMs(rtpSocket, UDP_RECV_POLL_TIMEOUT_MS) < 0) {
|
if (setNonFatalRecvTimeoutMs(rtpSocket, UDP_RECV_POLL_TIMEOUT_MS) < 0) {
|
||||||
@ -99,6 +108,19 @@ static void VideoReceiveThreadProc(void* context) {
|
|||||||
useSelect = false;
|
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;
|
waitingForVideoMs = 0;
|
||||||
while (!PltIsThreadInterrupted(&receiveThread)) {
|
while (!PltIsThreadInterrupted(&receiveThread)) {
|
||||||
PRTP_PACKET packet;
|
PRTP_PACKET packet;
|
||||||
@ -108,11 +130,14 @@ static void VideoReceiveThreadProc(void* context) {
|
|||||||
if (buffer == NULL) {
|
if (buffer == NULL) {
|
||||||
Limelog("Video Receive: malloc() failed\n");
|
Limelog("Video Receive: malloc() failed\n");
|
||||||
ListenerCallbacks.connectionTerminated(-1);
|
ListenerCallbacks.connectionTerminated(-1);
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = recvUdpSocket(rtpSocket, buffer, receiveSize, useSelect);
|
err = recvUdpSocket(rtpSocket,
|
||||||
|
encrypted ? encryptedBuffer : buffer,
|
||||||
|
receiveSize,
|
||||||
|
useSelect);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
Limelog("Video Receive: recvUdpSocket() failed: %d\n", (int)LastSocketError());
|
Limelog("Video Receive: recvUdpSocket() failed: %d\n", (int)LastSocketError());
|
||||||
ListenerCallbacks.connectionTerminated(LastSocketFail());
|
ListenerCallbacks.connectionTerminated(LastSocketFail());
|
||||||
@ -153,18 +178,33 @@ static void VideoReceiveThreadProc(void* context) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (err < (int)sizeof(RTP_PACKET)) {
|
if (err < minSize) {
|
||||||
// Runt packet
|
// Runt packet
|
||||||
continue;
|
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
|
// Convert fields to host byte-order
|
||||||
packet = (PRTP_PACKET)&buffer[0];
|
packet = (PRTP_PACKET)&buffer[0];
|
||||||
packet->sequenceNumber = BE16(packet->sequenceNumber);
|
packet->sequenceNumber = BE16(packet->sequenceNumber);
|
||||||
packet->timestamp = BE32(packet->timestamp);
|
packet->timestamp = BE32(packet->timestamp);
|
||||||
packet->ssrc = BE32(packet->ssrc);
|
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) {
|
if (queueStatus == RTPF_RET_QUEUED) {
|
||||||
// The queue owns the buffer
|
// The queue owns the buffer
|
||||||
@ -175,6 +215,10 @@ static void VideoReceiveThreadProc(void* context) {
|
|||||||
if (buffer != NULL) {
|
if (buffer != NULL) {
|
||||||
free(buffer);
|
free(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (encryptedBuffer != NULL) {
|
||||||
|
free(encryptedBuffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void notifyKeyFrameReceived(void) {
|
void notifyKeyFrameReceived(void) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user