Skip to content

Commit

Permalink
Implement control stream v2 encryption
Browse files Browse the repository at this point in the history
  • Loading branch information
cgutman committed Jan 20, 2024
1 parent 2f80145 commit 23fb07d
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 13 deletions.
18 changes: 13 additions & 5 deletions src/rtsp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -518,14 +518,22 @@ namespace rtsp_stream {
std::stringstream ss;

// Tell the client about our supported features
ss << "a=x-ss-general.featureFlags: " << (uint32_t) platf::get_capabilities() << std::endl;
ss << "a=x-ss-general.featureFlags:" << (uint32_t) platf::get_capabilities() << std::endl;

if (video::active_hevc_mode != 1) {
ss << "sprop-parameter-sets=AAAAAU"sv << std::endl;
}
// Always request new control stream encryption if the client supports it
uint32_t encryption_flags_supported = SS_ENC_CONTROL_V2 | SS_ENC_AUDIO;
uint32_t encryption_flags_requested = SS_ENC_CONTROL_V2;

// Report supported and required encryption flags
ss << "a=x-ss-general.encryptionSupported:" << encryption_flags_supported << std::endl;
ss << "a=x-ss-general.encryptionRequested:" << encryption_flags_requested << std::endl;

if (video::last_encoder_probe_supported_ref_frames_invalidation) {
ss << "x-nv-video[0].refPicInvalidation=1"sv << std::endl;
ss << "a=x-nv-video[0].refPicInvalidation:1"sv << std::endl;
}

if (video::active_hevc_mode != 1) {
ss << "sprop-parameter-sets=AAAAAU"sv << std::endl;
}

if (video::active_av1_mode != 1) {
Expand Down
52 changes: 44 additions & 8 deletions src/stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -381,11 +381,11 @@ namespace stream {
crypto::cipher::gcm_t cipher;
crypto::aes_t legacy_input_enc_iv; // Only used when the client doesn't support full control stream encryption

uint32_t connect_data; // Used for new clients with ML_FF_SESSION_ID_V1
std::uint32_t connect_data; // Used for new clients with ML_FF_SESSION_ID_V1
std::string expected_peer_address; // Only used for legacy clients without ML_FF_SESSION_ID_V1

net::peer_t peer;
std::uint8_t seq;
std::uint32_t seq;

platf::feedback_queue_t feedback_queue;
safe::mail_raw_t::event_t<video::hdr_info_t> hdr_queue;
Expand Down Expand Up @@ -414,9 +414,29 @@ namespace stream {
return plaintext;
}

crypto::aes_t iv(16);
auto seq = session->control.seq++;
iv[0] = seq;

crypto::aes_t iv;
if (session->config.encryptionFlagsEnabled & SS_ENC_CONTROL_V2) {
// We use the deterministic IV construction algorithm specified in NIST SP 800-38D
// Section 8.2.1. The sequence number is our "invocation" field and the 'CH' in the
// high bytes is the "fixed" field. Because each client provides their own unique
// key, our values in the fixed field need only uniquely identify each independent
// use of the client's key with AES-GCM in our code.
//
// The sequence number is 32 bits long which allows for 2^32 control stream messages
// to be sent to each client before the IV repeats.
iv.resize(12);
std::copy_n((uint8_t *) &seq, sizeof(seq), std::begin(iv));
iv[10] = 'H'; // Host originated
iv[11] = 'C'; // Control stream
}
else {
// Nvidia's old style encryption uses a 16-byte IV
iv.resize(16);

iv[0] = (std::uint8_t) seq;
}

auto packet = (control_encrypted_p) tagged_cipher.data();

Expand Down Expand Up @@ -915,11 +935,27 @@ namespace stream {
std::string_view tagged_cipher { (char *) header->payload(), (size_t) tagged_cipher_length };

auto &cipher = session->control.cipher;
crypto::aes_t iv(16);
iv[0] = (std::uint8_t) seq;
crypto::aes_t iv;
if (session->config.encryptionFlagsEnabled & SS_ENC_CONTROL_V2) {
// We use the deterministic IV construction algorithm specified in NIST SP 800-38D
// Section 8.2.1. The sequence number is our "invocation" field and the 'CC' in the
// high bytes is the "fixed" field. Because each client provides their own unique
// key, our values in the fixed field need only uniquely identify each independent
// use of the client's key with AES-GCM in our code.
//
// The sequence number is 32 bits long which allows for 2^32 control stream messages
// to be received from each client before the IV repeats.
iv.resize(12);
std::copy_n((uint8_t *) &seq, sizeof(seq), std::begin(iv));
iv[10] = 'C'; // Client originated
iv[11] = 'C'; // Control stream
}
else {
// Nvidia's old style encryption uses a 16-byte IV
iv.resize(16);

// update control sequence
++session->control.seq;
iv[0] = (std::uint8_t) seq;
}

std::vector<uint8_t> plaintext;
if (cipher.decrypt(tagged_cipher, plaintext, &iv)) {
Expand Down

0 comments on commit 23fb07d

Please sign in to comment.