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 decoderThread;
|
||||
|
||||
static PPLT_CRYPTO_CONTEXT audioDecryptionCtx;
|
||||
|
||||
static unsigned short lastSeq;
|
||||
|
||||
static bool receivedDataFromPeer;
|
||||
@ -65,6 +67,7 @@ int initializeAudioStream(void) {
|
||||
RtpqInitializeQueue(&rtpReorderQueue, RTPQ_DEFAULT_MAX_SIZE, RTPQ_DEFAULT_QUEUE_TIME);
|
||||
lastSeq = 0;
|
||||
receivedDataFromPeer = false;
|
||||
audioDecryptionCtx = PltCreateCryptoContext();
|
||||
|
||||
// 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.
|
||||
@ -109,6 +112,7 @@ void destroyAudioStream(void) {
|
||||
rtpSocket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
PltDestroyCryptoContext(audioDecryptionCtx);
|
||||
freePacketList(LbqDestroyLinkedBlockingQueue(&packetQueue));
|
||||
RtpqCleanupQueue(&rtpReorderQueue);
|
||||
}
|
||||
@ -144,8 +148,37 @@ static void decodeInputData(PQUEUED_AUDIO_PACKET packet) {
|
||||
|
||||
lastSeq = rtp->sequenceNumber;
|
||||
|
||||
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) {
|
||||
PRTP_PACKET rtp;
|
||||
|
@ -24,6 +24,7 @@ OPUS_MULTISTREAM_CONFIGURATION NormalQualityOpusConfig;
|
||||
OPUS_MULTISTREAM_CONFIGURATION HighQualityOpusConfig;
|
||||
int OriginalVideoBitrate;
|
||||
int AudioPacketDuration;
|
||||
bool AudioEncryptionEnabled;
|
||||
|
||||
// Connection stages
|
||||
static const char* stageNames[STAGE_MAX] = {
|
||||
|
@ -27,6 +27,7 @@ extern OPUS_MULTISTREAM_CONFIGURATION NormalQualityOpusConfig;
|
||||
extern OPUS_MULTISTREAM_CONFIGURATION HighQualityOpusConfig;
|
||||
extern int OriginalVideoBitrate;
|
||||
extern int AudioPacketDuration;
|
||||
extern bool AudioEncryptionEnabled;
|
||||
|
||||
#ifndef UINT24_MAX
|
||||
#define UINT24_MAX 0xFFFFFF
|
||||
|
@ -29,6 +29,11 @@ extern "C" {
|
||||
#define COLOR_RANGE_LIMITED 0
|
||||
#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 {
|
||||
// Dimensions in pixels of the desired video stream
|
||||
int width;
|
||||
@ -87,6 +92,14 @@ typedef struct _STREAM_CONFIGURATION {
|
||||
// option (listed above). If not set, the encoder will default to Limited.
|
||||
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
|
||||
// the same as what was passed as rikey and rikeyid
|
||||
// in /launch and /resume requests.
|
||||
|
@ -607,6 +607,7 @@ int performRtspHandshake(void) {
|
||||
currentSeqNumber = 1;
|
||||
hasSessionId = false;
|
||||
controlStreamId = APP_VERSION_AT_LEAST(7, 1, 431) ? "streamid=control/13/0" : "streamid=control/1/0";
|
||||
AudioEncryptionEnabled = false;
|
||||
|
||||
switch (AppVersionQuad[0]) {
|
||||
case 3:
|
||||
|
@ -139,13 +139,31 @@ static int addGen4Options(PSDP_OPTION* head, char* addrStr) {
|
||||
return err;
|
||||
}
|
||||
|
||||
#define NVFF_BASE 0x07
|
||||
#define NVFF_AUDIO_ENCRYPTION 0x20
|
||||
#define NVFF_RI_ENCRYPTION 0x80
|
||||
|
||||
static int addGen5Options(PSDP_OPTION* head) {
|
||||
int err = 0;
|
||||
char payloadStr[32];
|
||||
|
||||
// This must be initialized to false already
|
||||
LC_ASSERT(!AudioEncryptionEnabled);
|
||||
|
||||
if (APP_VERSION_AT_LEAST(7, 1, 431)) {
|
||||
// 0x20 enables audio encryption (which we don't support yet)
|
||||
// 0x80 enables remote input encryption (which we do want)
|
||||
err |= addAttributeString(head, "x-nv-general.featureFlags", "135");
|
||||
unsigned int featureFlags;
|
||||
|
||||
// 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.
|
||||
// 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