Skip to content

Commit

Permalink
The actual encryption is assumed to have been carried out
Browse files Browse the repository at this point in the history
according to the SAMPLE-AES encryption method cited by the
HLS spec (informational RFC called draft-pantos-http-live-
streaming).

This encryption method is being incorporated in the Common
Encryption specification ISO/IEC 23001-7 3rd Edition as the
'cbcs' scheme.

Parsing of the stream requires it to be prefixed with extra
TS packets that carry encryption metadata. These packets
are formatted according to ISO/IEC 23001-9 "Common
encryption of MPEG-2 transport streams".

This CL is dependent on a prior CL (1490613005) that allows
conveyance of the extended encryption metadata through the
pipeline.

BUG=568326

Review-Url: https://codereview.chromium.org/1517473002
Cr-Commit-Position: refs/heads/master@{#436058}
  • Loading branch information
dougsteed authored and Commit bot committed Dec 2, 2016
1 parent 7dd3af9 commit a66a6e4
Show file tree
Hide file tree
Showing 29 changed files with 1,379 additions and 62 deletions.
17 changes: 17 additions & 0 deletions media/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ buildflag_header("media_features") {
"ENABLE_AC3_EAC3_AUDIO_DEMUXING=$enable_ac3_eac3_audio_demuxing",
"ENABLE_CBCS_ENCRYPTION_SCHEME=$enable_cbcs_encryption_scheme",
"ENABLE_HEVC_DEMUXING=$enable_hevc_demuxing",
"ENABLE_HLS_SAMPLE_AES=$enable_hls_sample_aes",
"ENABLE_MSE_MPEG2TS_STREAM_PARSER=$enable_mse_mpeg2ts_stream_parser",
"ENABLE_MEDIA_REMOTING=$enable_media_remoting",
"ENABLE_WEBRTC=$enable_webrtc",
Expand Down Expand Up @@ -434,6 +435,8 @@ component("media") {
]
if (enable_mse_mpeg2ts_stream_parser) {
sources += [
"formats/mp2t/descriptors.cc",
"formats/mp2t/descriptors.h",
"formats/mp2t/es_adapter_video.cc",
"formats/mp2t/es_adapter_video.h",
"formats/mp2t/es_parser.cc",
Expand Down Expand Up @@ -461,6 +464,16 @@ component("media") {
"formats/mp2t/ts_section_psi.cc",
"formats/mp2t/ts_section_psi.h",
]
if (enable_hls_sample_aes) {
sources += [
"formats/mp2t/ts_section_cat.cc",
"formats/mp2t/ts_section_cat.h",
"formats/mp2t/ts_section_cets_ecm.cc",
"formats/mp2t/ts_section_cets_ecm.h",
"formats/mp2t/ts_section_cets_pssh.cc",
"formats/mp2t/ts_section_cets_pssh.h",
]
}
}
}

Expand Down Expand Up @@ -600,6 +613,7 @@ source_set("unit_tests") {
":media",
":test_support",
"//base/test:test_support",
"//crypto",
"//gpu:test_support",
"//gpu/command_buffer/common",
"//media/audio:test_support",
Expand Down Expand Up @@ -693,6 +707,9 @@ source_set("unit_tests") {
if (enable_hevc_demuxing) {
sources += [ "filters/h265_parser_unittest.cc" ]
}
if (enable_hls_sample_aes) {
deps += [ "//third_party/boringssl" ]
}
}

if (is_mac || is_ios) {
Expand Down
14 changes: 14 additions & 0 deletions media/base/bit_reader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ BitReader::BitReader(const uint8_t* data, int size)

BitReader::~BitReader() {}

bool BitReader::ReadString(int num_bits, std::string* str) {
DCHECK_EQ(num_bits % 8, 0);
DCHECK_GT(num_bits, 0);
DCHECK(str);
int num_bytes = num_bits / 8;
str->resize(num_bytes);
char* ptr = &str->front();
while (num_bytes--) {
if (!ReadBits(8, ptr++))
return false;
}
return true;
}

int BitReader::GetBytes(int max_nbytes, const uint8_t** out) {
DCHECK_GE(max_nbytes, 0);
DCHECK(out);
Expand Down
6 changes: 6 additions & 0 deletions media/base/bit_reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#define MEDIA_BASE_BIT_READER_H_

#include <stdint.h>
#include <string>

#include "base/compiler_specific.h"
#include "base/macros.h"
Expand All @@ -30,6 +31,11 @@ class MEDIA_EXPORT BitReader
return bit_reader_core_.ReadFlag(flag);
}

// Read |num_bits| of binary data into |str|. |num_bits| must be a positive
// multiple of 8. This is not efficient for extracting large strings.
// If false is returned, |str| may not be valid.
bool ReadString(int num_bits, std::string* str);

bool SkipBits(int num_bits) {
return bit_reader_core_.SkipBits(num_bits);
}
Expand Down
40 changes: 40 additions & 0 deletions media/base/test_data_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@

namespace media {

namespace {

// Key used to encrypt test files.
const uint8_t kSecretKey[] = {0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c};

// The key ID for all encrypted files.
const uint8_t kKeyId[] = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35};
}

const base::FilePath::CharType kTestDataPath[] =
FILE_PATH_LITERAL("media/test/data");

Expand Down Expand Up @@ -56,4 +67,33 @@ scoped_refptr<DecoderBuffer> ReadTestDataFile(const std::string& name) {
return buffer;
}

bool LookupTestKeyVector(const std::vector<uint8_t>& key_id,
bool allow_rotation,
std::vector<uint8_t>* key) {
std::vector<uint8_t> starting_key_id(kKeyId, kKeyId + arraysize(kKeyId));
size_t rotate_limit = allow_rotation ? starting_key_id.size() : 1;
for (size_t pos = 0; pos < rotate_limit; ++pos) {
std::rotate(starting_key_id.begin(), starting_key_id.begin() + pos,
starting_key_id.end());
if (key_id == starting_key_id) {
key->assign(kSecretKey, kSecretKey + arraysize(kSecretKey));
std::rotate(key->begin(), key->begin() + pos, key->end());
return true;
}
}
return false;
}

bool LookupTestKeyString(const std::string& key_id,
bool allow_rotation,
std::string* key) {
std::vector<uint8_t> key_vector;
bool result =
LookupTestKeyVector(std::vector<uint8_t>(key_id.begin(), key_id.end()),
allow_rotation, &key_vector);
if (result)
*key = std::string(key_vector.begin(), key_vector.end());
return result;
}

} // namespace media
12 changes: 12 additions & 0 deletions media/base/test_data_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ std::string GetURLQueryString(const base::StringPairs& query_params);
// |buffer| - The contents of the file.
scoped_refptr<DecoderBuffer> ReadTestDataFile(const std::string& name);

// If the provided |key_id| is that of a test key, returns true and fills the
// |key|, otherwise returns false. If |allowRotation| is true, then other valid
// values are obtained by rotating the original key_id and key. Two overloads
// are provided, one using vectors and one using strings.
bool LookupTestKeyVector(const std::vector<uint8_t>& key_id,
bool allowRotation,
std::vector<uint8_t>* key);

bool LookupTestKeyString(const std::string& key_id,
bool allowRotation,
std::string* key);

} // namespace media

#endif // MEDIA_BASE_TEST_DATA_UTIL_H_
156 changes: 156 additions & 0 deletions media/formats/mp2t/descriptors.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/formats/mp2t/descriptors.h"

#include <vector>

#include "base/logging.h"
#include "media/base/bit_reader.h"
#include "media/formats/mp2t/mp2t_common.h"

namespace media {
namespace mp2t {

namespace {

// Tag values for various kinds of descriptors for which there is specific
// parsing support herein.
enum DescriptorTag {
DESCRIPTOR_TAG_REGISTRATION = 5,
DESCRIPTOR_TAG_CA = 9,
DESCRIPTOR_TAG_PRIVATE_DATA_INDICATOR = 15,
};

const int kCASystemIdCenc = 0x6365; // 'ce'

class StringBitReader : public BitReader {
public:
StringBitReader(const std::string& input);
~StringBitReader() override;
};

StringBitReader::StringBitReader(const std::string& input)
: BitReader(reinterpret_cast<const uint8_t*>(input.data()), input.size()) {}

StringBitReader::~StringBitReader() {}

} // namespace

Descriptors::Descriptors() {}

Descriptors::Descriptors(const Descriptors& other) = default;

Descriptors::~Descriptors() {}

bool Descriptors::Read(BitReader* reader, int size) {
DCHECK(reader);
DCHECK(size >= 0);
descriptors_.clear();
if (size == 0)
return true;
int initial_bits_read = reader->bits_read();
int bits_read = 0;
int bits_available = reader->bits_available();
int size_in_bits = 8 * size;
if (size_in_bits > bits_available)
return false;
bits_available = size_in_bits;
do {
int tag;
size_t length;
RCHECK(reader->ReadBits(8, &tag));
RCHECK(reader->ReadBits(8, &length));
char data[256];
for (size_t i = 0; i < length; i++) {
RCHECK(reader->ReadBits(8, &data[i]));
}
descriptors_.insert(Descriptor(tag, std::string(data, length)));
bits_read = reader->bits_read() - initial_bits_read;
} while (bits_read < bits_available);
return bits_read == bits_available;
}

bool Descriptors::HasRegistrationDescriptor(
int64_t* format_identifier,
std::string* additional_info) const {
DCHECK(format_identifier);
DCHECK(additional_info);
auto search = descriptors_.find(DESCRIPTOR_TAG_REGISTRATION);
if (search == descriptors_.end())
return false;
const std::string& data = search->second;
StringBitReader reader(data);
RCHECK(reader.ReadBits(32, format_identifier));
size_t extra_bits = reader.bits_available();
RCHECK(extra_bits % 8 == 0);
RCHECK(extra_bits > 0);
RCHECK(reader.ReadString(extra_bits, additional_info));
return true;
}

bool Descriptors::HasCADescriptor(int* system_id,
int* pid,
std::string* private_data) const {
DCHECK(system_id);
DCHECK(pid);
DCHECK(private_data);
auto search = descriptors_.find(DESCRIPTOR_TAG_CA);
if (search == descriptors_.end())
return false;
const std::string& data = search->second;
StringBitReader reader(data);
RCHECK(reader.ReadBits(16, system_id));
RCHECK(reader.SkipBits(3));
RCHECK(reader.ReadBits(13, pid));
size_t extra_bits = reader.bits_available();
RCHECK(extra_bits % 8 == 0);
RCHECK(reader.ReadString(extra_bits, private_data));
return true;
}

bool Descriptors::HasCADescriptorCenc(int* ca_pid, int* pssh_pid) const {
DCHECK(ca_pid);
DCHECK(pssh_pid);
int system_id;
std::string private_data;
if (!HasCADescriptor(&system_id, ca_pid, &private_data))
return false;
if (system_id != kCASystemIdCenc)
return false;
StringBitReader reader(private_data);
uint32_t scheme_type;
uint32_t scheme_version;
int num_systems;
int encryption_algorithm;
char pssh_system_id[16];
// TODO(dougsteed). Currently we don't check many of the following values.
// When we flesh out this implementation to cover all of ISO/IEC 23001-9 we
// will need to use and check these values.
RCHECK(reader.ReadBits(32, &scheme_type));
RCHECK(reader.ReadBits(32, &scheme_version));
RCHECK(reader.ReadBits(8, &num_systems));
RCHECK(num_systems == 1);
RCHECK(reader.ReadBits(24, &encryption_algorithm));
for (size_t i = 0; i < 16; i++) {
RCHECK(reader.ReadBits(8, &pssh_system_id[i]));
}
RCHECK(reader.ReadBits(13, pssh_pid));
return true;
}

bool Descriptors::HasPrivateDataIndicator(int64_t value) const {
int64_t private_data_indicator;
auto search = descriptors_.find(DESCRIPTOR_TAG_PRIVATE_DATA_INDICATOR);
if (search == descriptors_.end())
return false;
const std::string& data = search->second;
StringBitReader reader(data);
RCHECK(reader.ReadBits(32, &private_data_indicator));
RCHECK(reader.bits_available() == 0);
return private_data_indicator == value;
}

} // namespace mp2t
} // namespace media
67 changes: 67 additions & 0 deletions media/formats/mp2t/descriptors.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_FORMATS_MP2T_DESCRIPTORS_H_
#define MEDIA_FORMATS_MP2T_DESCRIPTORS_H_

#include <stdint.h>

#include <map>
#include <string>

namespace media {

class BitReader;

namespace mp2t {

// Representation of a list of descriptors, used in the MPEG-2 Systems standard
// to extend the definitions of programs or program elements. While the standard
// appears to permit multiple descriptors in such a list to have the same tag
// value, the implementation herein will not support this.
class Descriptors {
public:
Descriptors();
Descriptors(const Descriptors& other);
~Descriptors();

// Attempts to read a (possibly empty) list of descriptors from the |reader|.
// If |size| > 0, the descriptors must occupy exactly |size| bytes, Otherwise,
// the descriptors should use all available bits from the reader.
bool Read(BitReader* reader, int size);

// Indicates whether a Registration descriptor is present. If so, the
// |format_identifier| and |additional_info| values are populated with the
// contents of the descriptor.
bool HasRegistrationDescriptor(int64_t* format_identifier,
std::string* additional_info) const;

// Indicates whether a CA descriptor is present. If so, the |system_id|,
// |pid|, and |private_data| values are populated with the contents of the
// descriptor.
bool HasCADescriptor(int* system_id,
int* pid,
std::string* private_data) const;

// Indicates whether a CA descriptor is present, and if so, whether it is
// of the type defined by ISO/IEC 23001-9:2014 (i.e. with a specific
// system_id value and layout of the private_data). If so, the |ca_pid| and
// |pssh_pid| are populated with the contents of the descriptor.
bool HasCADescriptorCenc(int* ca_pid, int* pssh_pid) const;

// Indicates whether a Private Data Indicator descriptor is present with a
// particular |value|.
bool HasPrivateDataIndicator(int64_t value) const;

private:
using Descriptor = std::pair<int, std::string>;
std::map<int, std::string> descriptors_;

// Allow copy and assign so that it can be used in a std C++ container.
};

} // namespace mp2t
} // namespace media

#endif // MEDIA_FORMATS_MP2T_DESCRIPTOR_LIST_H_
Loading

0 comments on commit a66a6e4

Please sign in to comment.