mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2025-08-18 09:25:49 +00:00
Add support for audio stream encryption
Clients must opt-in using the new encryptionFlags field
This commit is contained in:
parent
55cf1f8d30
commit
db81f1e512
@ -13,6 +13,8 @@ static PLT_THREAD udpPingThread;
|
|||||||
static PLT_THREAD receiveThread;
|
static PLT_THREAD receiveThread;
|
||||||
static PLT_THREAD decoderThread;
|
static PLT_THREAD decoderThread;
|
||||||
|
|
||||||
|
static PPLT_CRYPTO_CONTEXT audioDecryptionCtx;
|
||||||
|
|
||||||
static unsigned short lastSeq;
|
static unsigned short lastSeq;
|
||||||
|
|
||||||
static bool receivedDataFromPeer;
|
static bool receivedDataFromPeer;
|
||||||
@ -65,6 +67,7 @@ int initializeAudioStream(void) {
|
|||||||
RtpqInitializeQueue(&rtpReorderQueue, RTPQ_DEFAULT_MAX_SIZE, RTPQ_DEFAULT_QUEUE_TIME);
|
RtpqInitializeQueue(&rtpReorderQueue, RTPQ_DEFAULT_MAX_SIZE, RTPQ_DEFAULT_QUEUE_TIME);
|
||||||
lastSeq = 0;
|
lastSeq = 0;
|
||||||
receivedDataFromPeer = false;
|
receivedDataFromPeer = false;
|
||||||
|
audioDecryptionCtx = PltCreateCryptoContext();
|
||||||
|
|
||||||
// For GFE 3.22 compatibility, we must start the audio ping thread before the RTSP handshake.
|
// For GFE 3.22 compatibility, we must start the audio ping thread before the RTSP handshake.
|
||||||
// It will not reply to our RTSP PLAY request until the audio ping has been received.
|
// It will not reply to our RTSP PLAY request until the audio ping has been received.
|
||||||
@ -109,6 +112,7 @@ void destroyAudioStream(void) {
|
|||||||
rtpSocket = INVALID_SOCKET;
|
rtpSocket = INVALID_SOCKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PltDestroyCryptoContext(audioDecryptionCtx);
|
||||||
freePacketList(LbqDestroyLinkedBlockingQueue(&packetQueue));
|
freePacketList(LbqDestroyLinkedBlockingQueue(&packetQueue));
|
||||||
RtpqCleanupQueue(&rtpReorderQueue);
|
RtpqCleanupQueue(&rtpReorderQueue);
|
||||||
}
|
}
|
||||||
@ -144,7 +148,36 @@ static void decodeInputData(PQUEUED_AUDIO_PACKET packet) {
|
|||||||
|
|
||||||
lastSeq = rtp->sequenceNumber;
|
lastSeq = rtp->sequenceNumber;
|
||||||
|
|
||||||
AudioCallbacks.decodeAndPlaySample((char*)(rtp + 1), packet->size - sizeof(*rtp));
|
if (AudioEncryptionEnabled) {
|
||||||
|
// We must have room for the AES padding which may be written to the buffer
|
||||||
|
unsigned char decryptedOpusData[MAX_PACKET_SIZE+16];
|
||||||
|
unsigned char iv[16] = {};
|
||||||
|
int dataLength = packet->size - sizeof(*rtp);
|
||||||
|
|
||||||
|
LC_ASSERT(dataLength <= MAX_PACKET_SIZE);
|
||||||
|
|
||||||
|
// The IV is the avkeyid (equivalent to the rikeyid) +
|
||||||
|
// the RTP sequence number, in big endian.
|
||||||
|
uint32_t ivSeq = BE32(*(uint32_t*)StreamConfig.remoteInputAesIv);
|
||||||
|
ivSeq += rtp->sequenceNumber;
|
||||||
|
ivSeq = BE32(ivSeq);
|
||||||
|
|
||||||
|
memcpy(iv, &ivSeq, sizeof(ivSeq));
|
||||||
|
|
||||||
|
if (!PltDecryptMessage(audioDecryptionCtx, ALGORITHM_AES_CBC, CIPHER_FLAG_RESET_IV | CIPHER_FLAG_FINISH,
|
||||||
|
(unsigned char*)StreamConfig.remoteInputAesKey, sizeof(StreamConfig.remoteInputAesKey),
|
||||||
|
iv, sizeof(iv),
|
||||||
|
NULL, 0,
|
||||||
|
(unsigned char*)(rtp + 1), dataLength,
|
||||||
|
decryptedOpusData, &dataLength)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioCallbacks.decodeAndPlaySample((char*)decryptedOpusData, dataLength);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
AudioCallbacks.decodeAndPlaySample((char*)(rtp + 1), packet->size - sizeof(*rtp));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ReceiveThreadProc(void* context) {
|
static void ReceiveThreadProc(void* context) {
|
||||||
|
@ -24,6 +24,7 @@ OPUS_MULTISTREAM_CONFIGURATION NormalQualityOpusConfig;
|
|||||||
OPUS_MULTISTREAM_CONFIGURATION HighQualityOpusConfig;
|
OPUS_MULTISTREAM_CONFIGURATION HighQualityOpusConfig;
|
||||||
int OriginalVideoBitrate;
|
int OriginalVideoBitrate;
|
||||||
int AudioPacketDuration;
|
int AudioPacketDuration;
|
||||||
|
bool AudioEncryptionEnabled;
|
||||||
|
|
||||||
// Connection stages
|
// Connection stages
|
||||||
static const char* stageNames[STAGE_MAX] = {
|
static const char* stageNames[STAGE_MAX] = {
|
||||||
|
@ -27,6 +27,7 @@ extern OPUS_MULTISTREAM_CONFIGURATION NormalQualityOpusConfig;
|
|||||||
extern OPUS_MULTISTREAM_CONFIGURATION HighQualityOpusConfig;
|
extern OPUS_MULTISTREAM_CONFIGURATION HighQualityOpusConfig;
|
||||||
extern int OriginalVideoBitrate;
|
extern int OriginalVideoBitrate;
|
||||||
extern int AudioPacketDuration;
|
extern int AudioPacketDuration;
|
||||||
|
extern bool AudioEncryptionEnabled;
|
||||||
|
|
||||||
#ifndef UINT24_MAX
|
#ifndef UINT24_MAX
|
||||||
#define UINT24_MAX 0xFFFFFF
|
#define UINT24_MAX 0xFFFFFF
|
||||||
|
@ -29,6 +29,11 @@ extern "C" {
|
|||||||
#define COLOR_RANGE_LIMITED 0
|
#define COLOR_RANGE_LIMITED 0
|
||||||
#define COLOR_RANGE_FULL 1
|
#define COLOR_RANGE_FULL 1
|
||||||
|
|
||||||
|
// Values for 'encryptionFlags' field below
|
||||||
|
#define ENCFLG_NONE 0x00000000
|
||||||
|
#define ENCFLG_AUDIO 0x00000001
|
||||||
|
#define ENCFLG_ALL 0xFFFFFFFF
|
||||||
|
|
||||||
typedef struct _STREAM_CONFIGURATION {
|
typedef struct _STREAM_CONFIGURATION {
|
||||||
// Dimensions in pixels of the desired video stream
|
// Dimensions in pixels of the desired video stream
|
||||||
int width;
|
int width;
|
||||||
@ -87,6 +92,14 @@ typedef struct _STREAM_CONFIGURATION {
|
|||||||
// option (listed above). If not set, the encoder will default to Limited.
|
// option (listed above). If not set, the encoder will default to Limited.
|
||||||
int colorRange;
|
int colorRange;
|
||||||
|
|
||||||
|
// Specifies the data streams where encryption may be enabled if supported
|
||||||
|
// by the host PC. Ideally, you would pass ENCFLG_ALL to encrypt everything
|
||||||
|
// that we support encrypting. However, lower performance hardware may not
|
||||||
|
// be able to support encrypting heavy stuff like video or audio data, so
|
||||||
|
// that encryption may be disabled here. Remote input encryption is always
|
||||||
|
// enabled.
|
||||||
|
int encryptionFlags;
|
||||||
|
|
||||||
// AES encryption data for the remote input stream. This must be
|
// AES encryption data for the remote input stream. This must be
|
||||||
// the same as what was passed as rikey and rikeyid
|
// the same as what was passed as rikey and rikeyid
|
||||||
// in /launch and /resume requests.
|
// in /launch and /resume requests.
|
||||||
|
@ -607,6 +607,7 @@ int performRtspHandshake(void) {
|
|||||||
currentSeqNumber = 1;
|
currentSeqNumber = 1;
|
||||||
hasSessionId = false;
|
hasSessionId = false;
|
||||||
controlStreamId = APP_VERSION_AT_LEAST(7, 1, 431) ? "streamid=control/13/0" : "streamid=control/1/0";
|
controlStreamId = APP_VERSION_AT_LEAST(7, 1, 431) ? "streamid=control/13/0" : "streamid=control/1/0";
|
||||||
|
AudioEncryptionEnabled = false;
|
||||||
|
|
||||||
switch (AppVersionQuad[0]) {
|
switch (AppVersionQuad[0]) {
|
||||||
case 3:
|
case 3:
|
||||||
|
@ -139,13 +139,31 @@ static int addGen4Options(PSDP_OPTION* head, char* addrStr) {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define NVFF_BASE 0x07
|
||||||
|
#define NVFF_AUDIO_ENCRYPTION 0x20
|
||||||
|
#define NVFF_RI_ENCRYPTION 0x80
|
||||||
|
|
||||||
static int addGen5Options(PSDP_OPTION* head) {
|
static int addGen5Options(PSDP_OPTION* head) {
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
char payloadStr[32];
|
||||||
|
|
||||||
|
// This must be initialized to false already
|
||||||
|
LC_ASSERT(!AudioEncryptionEnabled);
|
||||||
|
|
||||||
if (APP_VERSION_AT_LEAST(7, 1, 431)) {
|
if (APP_VERSION_AT_LEAST(7, 1, 431)) {
|
||||||
// 0x20 enables audio encryption (which we don't support yet)
|
unsigned int featureFlags;
|
||||||
// 0x80 enables remote input encryption (which we do want)
|
|
||||||
err |= addAttributeString(head, "x-nv-general.featureFlags", "135");
|
// RI encryption is always enabled
|
||||||
|
featureFlags = NVFF_BASE | NVFF_RI_ENCRYPTION;
|
||||||
|
|
||||||
|
// Enable audio encryption if the client opted in
|
||||||
|
if (StreamConfig.encryptionFlags & ENCFLG_AUDIO) {
|
||||||
|
featureFlags |= NVFF_AUDIO_ENCRYPTION;
|
||||||
|
AudioEncryptionEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(payloadStr, "%u", featureFlags);
|
||||||
|
err |= addAttributeString(head, "x-nv-general.featureFlags", payloadStr);
|
||||||
|
|
||||||
// Ask for the encrypted control protocol to ensure remote input will be encrypted.
|
// Ask for the encrypted control protocol to ensure remote input will be encrypted.
|
||||||
// This used to be done via separate RI encryption, but now it is all or nothing.
|
// This used to be done via separate RI encryption, but now it is all or nothing.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user