FrameEncryption Video End To End Testcase.

There was a suggestion in a previous CL to add an end to end test case to
prevent future regressions. I have enabled this by adding two fakes that
perform fake encryption and enabling an end to end test with VP8 and the
GenericDescriptor.

Bug: webrtc:9927
Change-Id: Icf96eeed541ada1e0579eb81b6f87a46d1c43d96
Reviewed-on: https://webrtc-review.googlesource.com/c/108020
Commit-Queue: Benjamin Wright <benwright@webrtc.org>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25399}
This commit is contained in:
Benjamin Wright 2018-10-26 15:43:06 -07:00 committed by Commit Bot
parent c462a6ef9b
commit 150a907403
6 changed files with 124 additions and 27 deletions

View File

@ -25,19 +25,20 @@ int FakeFrameDecryptor::Decrypt(cricket::MediaType media_type,
rtc::ArrayView<uint8_t> frame,
size_t* bytes_written) {
if (fail_decryption_) {
return 1;
return static_cast<int>(FakeDecryptStatus::FORCED_FAILURE);
}
RTC_CHECK_EQ(frame.size() + 1, encrypted_frame.size());
for (size_t i = 0; i < frame.size(); i++) {
frame[i] ^= fake_key_;
frame[i] = encrypted_frame[i] ^ fake_key_;
}
if (encrypted_frame[frame.size()] != expected_postfix_byte_) {
return 1;
return static_cast<int>(FakeDecryptStatus::INVALID_POSTFIX);
}
return 0;
*bytes_written = frame.size();
return static_cast<int>(FakeDecryptStatus::OK);
}
size_t FakeFrameDecryptor::GetMaxPlaintextByteSize(

View File

@ -22,34 +22,40 @@ namespace webrtc {
// FrameDecryptorInterface. It is constructed with a simple single digit key and
// a fixed postfix byte. This is just to validate that the core code works
// as expected.
class FakeFrameDecryptor
class FakeFrameDecryptor final
: public rtc::RefCountedObject<FrameDecryptorInterface> {
public:
// Provide a key (0,255) and some postfix byte (0,255) this should match the
// byte you expect from the FakeFrameEncryptor.
explicit FakeFrameDecryptor(uint8_t fake_key = 1,
explicit FakeFrameDecryptor(uint8_t fake_key = 0xAA,
uint8_t expected_postfix_byte = 255);
// FrameDecryptorInterface implementation
// Fake decryption that just xors the payload with the 1 byte key and checks
// the postfix byte. This will always fail if fail_decryption_ is set to true.
int Decrypt(cricket::MediaType media_type,
const std::vector<uint32_t>& csrcs,
rtc::ArrayView<const uint8_t> additional_data,
rtc::ArrayView<const uint8_t> encrypted_frame,
rtc::ArrayView<uint8_t> frame,
size_t* bytes_written) override;
// Always returns 1 less than the size of the encrypted frame.
size_t GetMaxPlaintextByteSize(cricket::MediaType media_type,
size_t encrypted_frame_size) override;
// Sets the fake key to use for encryption.
void SetFakeKey(uint8_t fake_key);
// Returns the fake key used for encryption.
uint8_t GetFakeKey() const;
// Set the Postfix byte that is expected in the encrypted payload.
void SetExpectedPostfixByte(uint8_t expected_postfix_byte);
// Returns the postfix byte that will be checked for in the encrypted payload.
uint8_t GetExpectedPostfixByte() const;
// If set to true will force all encryption to fail.
void SetFailDecryption(bool fail_decryption);
// Simple error codes for tests to validate against.
enum class FakeDecryptStatus : int {
OK = 0,
FORCED_FAILURE = 1,
INVALID_POSTFIX = 2
};
private:
uint8_t fake_key_ = 0;

View File

@ -22,18 +22,18 @@ int FakeFrameEncryptor::Encrypt(cricket::MediaType media_type,
rtc::ArrayView<const uint8_t> frame,
rtc::ArrayView<uint8_t> encrypted_frame,
size_t* bytes_written) {
// Useful if you want to test failure cases.
if (fail_encryption_) {
return 1;
return static_cast<int>(FakeEncryptionStatus::FORCED_FAILURE);
}
RTC_CHECK_EQ(frame.size() + 1, encrypted_frame.size());
for (size_t i = 0; i < frame.size(); i++) {
encrypted_frame[i] ^= fake_key_;
encrypted_frame[i] = frame[i] ^ fake_key_;
}
encrypted_frame[frame.size()] = postfix_byte_;
*bytes_written = encrypted_frame.size();
return 0;
return static_cast<int>(FakeEncryptionStatus::OK);
}
size_t FakeFrameEncryptor::GetMaxCiphertextByteSize(

View File

@ -24,29 +24,35 @@ class FakeFrameEncryptor
: public rtc::RefCountedObject<FrameEncryptorInterface> {
public:
// Provide a key (0,255) and some postfix byte (0,255).
explicit FakeFrameEncryptor(uint8_t fake_key = 1, uint8_t postfix_byte = 255);
// FrameEncryptorInterface implementation
explicit FakeFrameEncryptor(uint8_t fake_key = 0xAA,
uint8_t postfix_byte = 255);
// Simply xors each payload with the provided fake key and adds the postfix
// bit to the end. This will always fail if fail_encryption_ is set to true.
int Encrypt(cricket::MediaType media_type,
uint32_t ssrc,
rtc::ArrayView<const uint8_t> additional_data,
rtc::ArrayView<const uint8_t> frame,
rtc::ArrayView<uint8_t> encrypted_frame,
size_t* bytes_written) override;
// Always returns 1 more than the size of the frame.
size_t GetMaxCiphertextByteSize(cricket::MediaType media_type,
size_t frame_size) override;
// Sets the fake key to use during encryption.
void SetFakeKey(uint8_t fake_key);
// Returns the fake key used during encryption.
uint8_t GetFakeKey() const;
// Set the postfix byte to use.
void SetPostfixByte(uint8_t expected_postfix_byte);
// Return a postfix byte added to each outgoing payload.
uint8_t GetPostfixByte() const;
// Force all encryptions to fail.
void SetFailEncryption(bool fail_encryption);
enum class FakeEncryptionStatus : int {
OK = 0,
FORCED_FAILURE = 1,
};
private:
uint8_t fake_key_ = 0;
uint8_t postfix_byte_ = 0;

View File

@ -394,6 +394,7 @@ if (rtc_include_tests) {
"end_to_end_tests/config_tests.cc",
"end_to_end_tests/extended_reports_tests.cc",
"end_to_end_tests/fec_tests.cc",
"end_to_end_tests/frame_encryption_tests.cc",
"end_to_end_tests/histogram_tests.cc",
"end_to_end_tests/multi_codec_receive_tests.cc",
"end_to_end_tests/multi_stream_tester.cc",
@ -426,6 +427,8 @@ if (rtc_include_tests) {
":video",
":video_mocks",
":video_stream_encoder_impl",
"../api:fake_frame_decryptor",
"../api:fake_frame_encryptor",
"../api:simulated_network_api",
"../api/test/video:function_video_factory",
"../api/video:encoded_image",

View File

@ -0,0 +1,81 @@
/*
* Copyright 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "api/test/fake_frame_decryptor.h"
#include "api/test/fake_frame_encryptor.h"
#include "media/engine/internaldecoderfactory.h"
#include "modules/video_coding/codecs/vp8/include/vp8.h"
#include "test/call_test.h"
#include "test/field_trial.h"
#include "test/gtest.h"
namespace webrtc {
class FrameEncryptionEndToEndTest : public test::CallTest {
public:
FrameEncryptionEndToEndTest() = default;
private:
// GenericDescriptor is required for FrameEncryption to work.
test::ScopedFieldTrials field_trials_{"WebRTC-GenericDescriptor/Enabled/"};
};
// Validates that payloads cannot be sent without a frame encryptor and frame
// decryptor attached.
TEST_F(FrameEncryptionEndToEndTest, RequireFrameEncryptionEnforced) {
class DecryptedFrameObserver : public test::EndToEndTest,
public rtc::VideoSinkInterface<VideoFrame> {
public:
DecryptedFrameObserver()
: EndToEndTest(kDefaultTimeoutMs),
encoder_factory_([]() { return VP8Encoder::Create(); }) {}
private:
void ModifyVideoConfigs(
VideoSendStream::Config* send_config,
std::vector<VideoReceiveStream::Config>* receive_configs,
VideoEncoderConfig* encoder_config) override {
// Use VP8 instead of FAKE.
send_config->encoder_settings.encoder_factory = &encoder_factory_;
send_config->rtp.payload_name = "VP8";
send_config->rtp.payload_type = kVideoSendPayloadType;
send_config->frame_encryptor = new FakeFrameEncryptor();
send_config->crypto_options.sframe.require_frame_encryption = true;
encoder_config->codec_type = kVideoCodecVP8;
VideoReceiveStream::Decoder decoder =
test::CreateMatchingDecoder(*send_config);
decoder.decoder_factory = &decoder_factory_;
for (auto& recv_config : *receive_configs) {
recv_config.decoders.clear();
recv_config.decoders.push_back(decoder);
recv_config.renderer = this;
recv_config.frame_decryptor = new FakeFrameDecryptor();
recv_config.crypto_options.sframe.require_frame_encryption = true;
}
}
// Validate that rotation is preserved.
void OnFrame(const VideoFrame& video_frame) override {
observation_complete_.Set();
}
void PerformTest() override {
EXPECT_TRUE(Wait())
<< "Timed out waiting for decrypted frames to be rendered.";
}
std::unique_ptr<VideoEncoder> encoder_;
test::FunctionVideoEncoderFactory encoder_factory_;
InternalDecoderFactory decoder_factory_;
} test;
RunBaseTest(&test);
}
} // namespace webrtc