Skip to content

Commit c951691

Browse files
feat: Data packet cryptor. (#191)
TODO: - [x] Core Implementation - [x] Android JNI Wrapper - [x] Objective-C Wrapper --------- Co-authored-by: davidliu <davidliu@deviange.net>
1 parent 9dcbe41 commit c951691

16 files changed

+900
-27
lines changed

BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,7 @@ if (rtc_include_tests && !build_with_chromium) {
630630
deps = [
631631
"api:compile_all_headers",
632632
"api:rtc_api_unittests",
633+
"api/crypto:crypto_unittests",
633634
"api/audio:audio_api_unittests",
634635
"api/audio_codecs/test:audio_codecs_api_unittests",
635636
"api/numerics:numerics_unittests",

api/crypto/BUILD.gn

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,18 @@ rtc_source_set("frame_encryptor_interface") {
6868
"../../rtc_base:refcount",
6969
]
7070
}
71+
72+
rtc_library("crypto_unittests") {
73+
testonly = true
74+
75+
sources = [
76+
"frame_crypto_transformer_unittest.cc",
77+
]
78+
79+
deps = [
80+
":crypto",
81+
":frame_crypto_transformer",
82+
"//testing/gtest",
83+
"../../test:test_support",
84+
]
85+
}

api/crypto/frame_crypto_transformer.cc

Lines changed: 177 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
#include "frame_crypto_transformer.h"
17+
#include "api/crypto/frame_crypto_transformer.h"
1818

1919
#include <openssl/aes.h>
2020
#include <openssl/err.h>
@@ -35,7 +35,9 @@
3535
#include "common_video/h265/h265_common.h"
3636
#include "modules/rtp_rtcp/source/rtp_format_h264.h"
3737
#include "rtc_base/byte_buffer.h"
38+
#include "rtc_base/crypto_random.h"
3839
#include "rtc_base/logging.h"
40+
#include "rtc_base/time_utils.h"
3941

4042
enum class EncryptOrDecrypt { kEncrypt = 0, kDecrypt };
4143

@@ -210,12 +212,16 @@ uint8_t get_unencrypted_bytes(webrtc::TransformableFrameInterface* frame,
210212
webrtc::H265::NaluType nalu_type =
211213
webrtc::H265::ParseNaluType(slice[0]);
212214
if (IsH265SliceNalu(nalu_type)) {
213-
// H.265 has a 2-byte NALU header, so unencrypted bytes = offset + header size
214-
unencrypted_bytes = index.payload_start_offset + webrtc::H265::kNaluHeaderSize;
215+
// H.265 has a 2-byte NALU header, so unencrypted bytes = offset +
216+
// header size
217+
unencrypted_bytes =
218+
index.payload_start_offset + webrtc::H265::kNaluHeaderSize;
215219
RTC_LOG(LS_INFO)
216-
<< "H265 NonParameterSetNalu::payload_size: " << index.payload_size
217-
<< ", nalu_type " << static_cast<int>(nalu_type) << ", NaluIndex [" << idx++
218-
<< "] offset: " << index.payload_start_offset << ", unencrypted_bytes: " << unencrypted_bytes;
220+
<< "H265 NonParameterSetNalu::payload_size: "
221+
<< index.payload_size << ", nalu_type "
222+
<< static_cast<int>(nalu_type) << ", NaluIndex [" << idx++
223+
<< "] offset: " << index.payload_start_offset
224+
<< ", unencrypted_bytes: " << unencrypted_bytes;
219225
return unencrypted_bytes;
220226
}
221227
}
@@ -321,8 +327,8 @@ int AesEncryptDecrypt(EncryptOrDecrypt mode,
321327
RTC_LOG(LS_ERROR) << "Invalid AES-GCM key size.";
322328
return ErrorUnexpected;
323329
}
324-
return AesGcmEncryptDecrypt(
325-
mode, raw_key, data, tag_length_bits / 8, iv, additional_data, cipher, buffer);
330+
return AesGcmEncryptDecrypt(mode, raw_key, data, tag_length_bits / 8, iv,
331+
additional_data, cipher, buffer);
326332
}
327333
default:
328334
RTC_LOG(LS_ERROR) << "Unsupported algorithm.";
@@ -385,7 +391,8 @@ void FrameCryptorTransformer::Transform(
385391
void FrameCryptorTransformer::encryptFrame(
386392
std::unique_ptr<webrtc::TransformableFrameInterface> frame) {
387393
bool enabled_cryption = false;
388-
webrtc::scoped_refptr<webrtc::TransformedFrameCallback> sink_callback = nullptr;
394+
webrtc::scoped_refptr<webrtc::TransformedFrameCallback> sink_callback =
395+
nullptr;
389396
{
390397
webrtc::MutexLock lock(&mutex_);
391398
enabled_cryption = enabled_cryption_;
@@ -410,7 +417,7 @@ void FrameCryptorTransformer::encryptFrame(
410417
if (data_in.size() == 0 || !enabled_cryption) {
411418
RTC_LOG(LS_WARNING) << "FrameCryptorTransformer::encryptFrame() "
412419
"data_in.size() == 0 || enabled_cryption == false";
413-
if(key_provider_->options().discard_frame_when_cryptor_not_ready) {
420+
if (key_provider_->options().discard_frame_when_cryptor_not_ready) {
414421
return;
415422
}
416423
sink_callback->OnTransformedFrame(std::move(frame));
@@ -498,7 +505,8 @@ void FrameCryptorTransformer::encryptFrame(
498505
void FrameCryptorTransformer::decryptFrame(
499506
std::unique_ptr<webrtc::TransformableFrameInterface> frame) {
500507
bool enabled_cryption = false;
501-
webrtc::scoped_refptr<webrtc::TransformedFrameCallback> sink_callback = nullptr;
508+
webrtc::scoped_refptr<webrtc::TransformedFrameCallback> sink_callback =
509+
nullptr;
502510
{
503511
webrtc::MutexLock lock(&mutex_);
504512
enabled_cryption = enabled_cryption_;
@@ -524,7 +532,7 @@ void FrameCryptorTransformer::decryptFrame(
524532
if (data_in.size() == 0 || !enabled_cryption) {
525533
RTC_LOG(LS_WARNING) << "FrameCryptorTransformer::decryptFrame() "
526534
"data_in.size() == 0 || enabled_cryption == false";
527-
if(key_provider_->options().discard_frame_when_cryptor_not_ready) {
535+
if (key_provider_->options().discard_frame_when_cryptor_not_ready) {
528536
return;
529537
}
530538

@@ -585,8 +593,8 @@ void FrameCryptorTransformer::decryptFrame(
585593
? key_provider_->GetSharedKey(participant_id_)
586594
: key_provider_->GetKey(participant_id_);
587595

588-
if (0 > key_index || key_index >= key_provider_->options().key_ring_size || key_handler == nullptr ||
589-
key_handler->GetKeySet(key_index) == nullptr) {
596+
if (0 > key_index || key_index >= key_provider_->options().key_ring_size ||
597+
key_handler == nullptr || key_handler->GetKeySet(key_index) == nullptr) {
590598
RTC_LOG(LS_INFO) << "FrameCryptorTransformer::decryptFrame() no keys, or "
591599
"key_index["
592600
<< key_index << "] out of range for participant "
@@ -621,7 +629,8 @@ void FrameCryptorTransformer::decryptFrame(
621629
encrypted_buffer.SetData(
622630
H264::ParseRbsp(encrypted_buffer.data(), encrypted_buffer.size()));
623631
} else if (FrameIsH265(frame.get(), type_) &&
624-
NeedsRbspUnescaping(encrypted_buffer.data(), encrypted_buffer.size())) {
632+
NeedsRbspUnescaping(encrypted_buffer.data(),
633+
encrypted_buffer.size())) {
625634
encrypted_buffer.SetData(
626635
H265::ParseRbsp(encrypted_buffer.data(), encrypted_buffer.size()));
627636
}
@@ -728,8 +737,7 @@ void FrameCryptorTransformer::onFrameCryptionStateChanged(
728737
rtc::Buffer FrameCryptorTransformer::makeIv(uint32_t ssrc, uint32_t timestamp) {
729738
uint32_t send_count = 0;
730739
if (send_counts_.find(ssrc) == send_counts_.end()) {
731-
srand((unsigned)time(NULL));
732-
send_counts_[ssrc] = floor(rand() * 0xFFFF);
740+
send_counts_[ssrc] = floor(CreateRandomNonZeroId() * 0xFFFF);
733741
} else {
734742
send_count = send_counts_[ssrc];
735743
}
@@ -753,4 +761,156 @@ uint8_t FrameCryptorTransformer::getIvSize() {
753761
}
754762
}
755763

764+
DataPacketCryptor::DataPacketCryptor(
765+
FrameCryptorTransformer::Algorithm algorithm,
766+
webrtc::scoped_refptr<KeyProvider> key_provider)
767+
: algorithm_(algorithm), key_provider_(key_provider) {
768+
RTC_DCHECK(key_provider_ != nullptr);
769+
}
770+
771+
DataPacketCryptor::~DataPacketCryptor() {}
772+
773+
RTCErrorOr<webrtc::scoped_refptr<EncryptedPacket>> DataPacketCryptor::Encrypt(
774+
const std::string participant_id,
775+
int key_index,
776+
const std::vector<uint8_t>& data) {
777+
auto key_handler = key_provider_->options().shared_key
778+
? key_provider_->GetSharedKey(participant_id)
779+
: key_provider_->GetKey(participant_id);
780+
781+
if (key_handler == nullptr || key_handler->GetKeySet(key_index) == nullptr) {
782+
RTC_LOG(LS_INFO) << "DataPacketCryptor::Encrypt() no keys, or "
783+
"key_index["
784+
<< key_index << "] out of range for participant "
785+
<< participant_id;
786+
return RTCError(RTCErrorType::INVALID_PARAMETER,
787+
"DataPacketCryptor::Encrypt() no keys, or key_index[" +
788+
std::to_string(key_index) +
789+
"] out of range for participant " + participant_id);
790+
}
791+
792+
auto key_set = key_handler->GetKeySet(key_index);
793+
auto timestamp = Timestamp::Millis(rtc::TimeMillis())
794+
.ms(); // use current time millis as timestamp
795+
auto iv = makeIv(timestamp); // for data packets, ssrc is always 0
796+
797+
std::vector<uint8_t> buffer;
798+
rtc::Buffer payload(data.data(), data.size());
799+
auto frame_header = rtc::Buffer(0); // no frame header for data packets
800+
if (AesEncryptDecrypt(EncryptOrDecrypt::kEncrypt, algorithm_,
801+
key_set->encryption_key, iv, frame_header, payload,
802+
&buffer) == Success) {
803+
webrtc::scoped_refptr<EncryptedPacket> encryptedPacket =
804+
webrtc::make_ref_counted<EncryptedPacket>(
805+
buffer, std::vector<uint8_t>(iv.begin(), iv.end()), key_index);
806+
return encryptedPacket;
807+
}
808+
809+
return RTCError(RTCErrorType::INTERNAL_ERROR,
810+
"DataPacketCryptor::Encrypt() failed");
811+
}
812+
813+
RTCErrorOr<std::vector<uint8_t>> DataPacketCryptor::Decrypt(
814+
const std::string participant_id,
815+
const webrtc::scoped_refptr<EncryptedPacket> encryptedPacket) {
816+
auto key_handler = key_provider_->options().shared_key
817+
? key_provider_->GetSharedKey(participant_id)
818+
: key_provider_->GetKey(participant_id);
819+
int key_index = encryptedPacket->key_index;
820+
if (key_handler == nullptr || key_handler->GetKeySet(key_index) == nullptr) {
821+
RTC_LOG(LS_INFO) << "DataPacketCryptor::Decrypt() no keys, or "
822+
"key_index["
823+
<< key_index << "] out of range for participant "
824+
<< participant_id;
825+
return RTCError(RTCErrorType::INVALID_PARAMETER,
826+
"DataPacketCryptor::Decrypt() no keys, or key_index[" +
827+
std::to_string(key_index) +
828+
"] out of range for participant " + participant_id);
829+
}
830+
831+
std::vector<uint8_t> buffer;
832+
rtc::Buffer encrypted_payload(encryptedPacket->data.data(),
833+
encryptedPacket->data.size());
834+
rtc::Buffer iv(encryptedPacket->iv.data(), encryptedPacket->iv.size());
835+
auto frame_header = rtc::Buffer(0); // no frame header for data packets
836+
837+
auto key_set = key_handler->GetKeySet(key_index);
838+
auto initialKeyMaterial = key_set->material;
839+
bool decryption_success = false;
840+
841+
if (AesEncryptDecrypt(EncryptOrDecrypt::kDecrypt, algorithm_,
842+
key_set->encryption_key, iv, frame_header,
843+
encrypted_payload, &buffer) == Success) {
844+
decryption_success = true;
845+
} else {
846+
RTC_LOG(LS_WARNING) << "DataPacketCryptor::Decrypt() failed with key_index "
847+
<< static_cast<int>(key_index);
848+
webrtc::scoped_refptr<ParticipantKeyHandler::KeySet> ratcheted_key_set;
849+
auto currentKeyMaterial = key_set->material;
850+
int ratchet_count = 0;
851+
if (key_provider_->options().ratchet_window_size > 0) {
852+
while (ratchet_count < key_provider_->options().ratchet_window_size) {
853+
ratchet_count++;
854+
855+
RTC_LOG(LS_INFO) << "ratcheting key attempt " << ratchet_count << " of "
856+
<< key_provider_->options().ratchet_window_size;
857+
858+
auto new_material = key_handler->RatchetKeyMaterial(currentKeyMaterial);
859+
ratcheted_key_set = key_handler->DeriveKeys(
860+
new_material, key_provider_->options().ratchet_salt, 128);
861+
862+
if (AesEncryptDecrypt(EncryptOrDecrypt::kDecrypt, algorithm_,
863+
ratcheted_key_set->encryption_key, iv,
864+
frame_header, encrypted_payload,
865+
&buffer) == Success) {
866+
RTC_LOG(LS_INFO) << "DataPacketCryptor::Decrypt() successfully "
867+
"ratcheted to key_index="
868+
<< static_cast<int>(key_index);
869+
decryption_success = true;
870+
// success, so we set the new key
871+
key_handler->SetKeyFromMaterial(new_material, key_index);
872+
key_handler->SetHasValidKey();
873+
break;
874+
}
875+
// for the next ratchet attempt
876+
currentKeyMaterial = new_material;
877+
}
878+
879+
/* Since the key it is first send and only afterwards actually used for
880+
encrypting, there were situations when the decrypting failed due to the
881+
fact that the received frame was not encrypted yet and ratcheting, of
882+
course, did not solve the problem. So if we fail RATCHET_WINDOW_SIZE
883+
times, we come back to the initial key.
884+
*/
885+
if (!decryption_success ||
886+
ratchet_count >= key_provider_->options().ratchet_window_size) {
887+
key_handler->SetKeyFromMaterial(initialKeyMaterial, key_index);
888+
}
889+
}
890+
}
891+
892+
if (decryption_success) {
893+
return buffer;
894+
}
895+
896+
return RTCError(RTCErrorType::INTERNAL_ERROR,
897+
"DataPacketCryptor::Decrypt() failed");
898+
}
899+
900+
rtc::Buffer DataPacketCryptor::makeIv(uint32_t timestamp) {
901+
if (send_count_ == 0) {
902+
send_count_ = floor(CreateRandomNonZeroId() * 0xFFFF);
903+
}
904+
rtc::ByteBufferWriter buf;
905+
uint32_t random_u32 = CreateRandomId();
906+
buf.WriteUInt32(random_u32);
907+
buf.WriteUInt32(timestamp);
908+
buf.WriteUInt32(timestamp - (send_count_ % 0xFFFF));
909+
send_count_ += 1;
910+
911+
RTC_CHECK_EQ(buf.Length(), 12);
912+
913+
return rtc::Buffer(buf.Data(), buf.Length());
914+
}
915+
756916
} // namespace webrtc

0 commit comments

Comments
 (0)