Automated update of OPK code

Included changes:

  - 676ac7be8548d80c420591fc0b4fb9a11723ef34 Backwards compatibility script for CDM v18 and OPK v19 by Vicky Min <vickymin@google.com>
  - 3cd4f71fda91245ac0b61c4c847950952f3021c0 Change BuildInformation ree fields to optional by Matt Feddersen <mattfedd@google.com>
  - a2259e95dea40c27a4be02ad479aec8f1fc84737 Created a DICE CBOR Cert parser/serializer. by Alex Dale <sigquit@google.com>
  - b8f2c364afeb6279e5aee6488d4527e189ac42ff Don't create invalid enum value by John "Juce" Bruce <juce@google.com>
  - b0aed212a3b2dd8f752d8fc43982848c1aa6c152 Created an HLS Key type. by Alex Dale <sigquit@google.com>
  - f8cfc54b41f124ba849596dbe6438b7f271a72b7 Specify C/C++ standard when running clang-tidy on OPK by John "Juce" Bruce <juce@google.com>

GitOrigin-RevId: 676ac7be8548d80c420591fc0b4fb9a11723ef34
This commit is contained in:
Googler
2025-05-13 21:44:08 +00:00
committed by mattfedd
parent a2b9e085e9
commit 5387878a5b
25 changed files with 3139 additions and 937 deletions

210
util/include/hls_key.h Normal file
View File

@@ -0,0 +1,210 @@
// Copyright 2025 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_UTIL_HLS_KEY_H_
#define WVCDM_UTIL_HLS_KEY_H_
#include <inttypes.h>
#include <ostream>
#include <set>
#include <string>
#include <vector>
#include "wv_class_utils.h"
namespace wvutil {
class HlsAttributeList;
// Data wrapper types for HLS Key fields.
//
// Ensures that the values of each attribute of an
// HLS key is valid. The class does not enforce contextual
// requirements between attributes; however, IsWellFormed()
// can optionally be called to ensure some fields are set
// appropriately based on the values of other fields.
//
// This class is based on RFC 8216 section 4.3.2.4.
class HlsKey {
public:
// HLS Tag for key data.
static constexpr char kTag[] = "EXT-X-KEY";
// Attribute names for HLS keys.
// METHOD is an enumerated string with three known values
// of NONE, AES-128, or SAMPLE-AES.
// METHOD may be a different value so long as it is a
// valid enum string.
// METHOD is required, if set to NONE, then the other
// fields SHOULD NOT be set.
static constexpr char kMethodName[] = "METHOD";
// URI is a quoted string containing be a valid URI which
// identifies where to find the key(s) used for content
// decryption.
// Must be set if METHOD is not NONE.
// Should not be set if METHOD is NONE.
static constexpr char kUriName[] = "URI";
// IV is a hex sequence for a 128-bit initialization vector
// used for decryption of media content.
// Must be set if METHOD is not NONE.
// Should not be set if METHOD is NONE.
static constexpr char kIvName[] = "IV";
// The key format is a quoted string which identifies the
// format of the key acquired by the URI.
// Optionally set if METHOD is not NONE, if not set, then
// it is assumed the URI will directly acquire the key.
// Should not be set if METHOD is NONE.
static constexpr char kKeyFormatName[] = "KEYFORMAT";
// The key format versions is a quoted string containing one
// or more positive integers (of HLS integer format) separated
// by a '/', which indicates the vendor-specific versions of the
// key acquired via the URI.
// Optionally set if METHOD is not NONE, assumed to contain
// a value of 1 if not set but a version is required.
// Should not be set if METHOD is NONE.
static constexpr char kKeyFormatVersionsName[] = "KEYFORMATVERSIONS";
// List of known METHOD values.
static constexpr char kMethodNone[] = "NONE";
static constexpr char kMethodAes128[] = "AES-128";
static constexpr char kMethodSampleAes[] = "SAMPLE-AES";
// HLS keys require an IV length of 128-bits / 16 octets.
static constexpr size_t kIvLength = 16;
// Checks if the provided |method| is one of the known
// values for the enumerated string METHOD.
static bool IsKnownMethod(const std::string& method);
// Checks if the provided |method| is a valid HLS enum
// string value.
static bool IsValidMethod(const std::string& method);
// Simply checks that the provided URI is a valid HLS quoted
// string value. Does not valid URI format.
static bool IsValidUriValue(const std::string& uri);
// Checks that the specified IV length (in octets) is a supported
// length.
static constexpr bool IsValidIvLength(size_t iv_length) {
return iv_length == kIvLength;
}
static bool IsValidIv(const std::string& iv) {
return IsValidIvLength(iv.size());
}
static bool IsValidIv(const std::vector<uint8_t>& iv) {
return IsValidIvLength(iv.size());
}
// Checks that the provided key format conforms to the quoted
// string value requirements of HLS.
static bool IsValidKeyFormat(const std::string& key_format);
// Checks if the specified |version| is a valid key format
// version (must be positive).
static constexpr bool IsValidKeyFormatVersion(uint64_t version) {
return version > 0;
}
// Checks that all the values in the set of |versions| is a valid
// versions.
// The set of versions cannot be empty, and all versions must be a
// positive value.
static bool IsValidKeyFormatVersions(const std::set<uint64_t>& versions);
// Checks that the provided formatted version list is a valid
// representation of key format versions.
// Note: There is no requirement that the versions are unique.
static bool IsValidKeyFormatVersionListRep(
const std::string& version_list_rep);
HlsKey() = default;
WVCDM_DEFAULT_COPY_AND_MOVE(HlsKey);
const std::string& method() const { return method_; }
bool HasMethod() const { return !method_.empty(); }
bool SetMethod(const std::string& method);
void ClearMethod() { method_.clear(); }
// URIs are only validated that they are valid quoted string
// values; not that they are well formed URIs.
const std::string& uri() const { return uri_; }
bool HasUri() const { return !uri_.empty(); }
bool SetUri(const std::string& uri);
void ClearUri() { uri_.clear(); }
const std::vector<uint8_t>& iv() const { return iv_; }
bool HasIv() const { return !iv_.empty(); }
bool SetIv(const std::vector<uint8_t>& iv);
bool SetIv(const std::string& iv);
bool SetIvHex(const std::string& iv_hex);
void ClearIv() { iv_.clear(); }
const std::string& key_format() const { return key_format_; }
bool HasKeyFormat() const { return !key_format_.empty(); }
bool SetKeyFormat(const std::string& key_format);
void ClearKeyFormat() { key_format_.clear(); }
const std::set<uint64_t>& key_format_versions() const {
return key_format_versions_;
}
bool HasKeyFormatVersions() const { return !key_format_versions_.empty(); }
bool HasKeyFormatVersion(uint64_t version) const;
bool SetKeyFormatVersions(const std::set<uint64_t>& versions);
bool SetKeyFormatVersions(const std::vector<uint64_t>& versions);
bool AddKeyFormatVersion(uint64_t version);
void ClearKeyFormatVersions() { key_format_versions_.clear(); }
void Clear() {
ClearMethod();
ClearUri();
ClearIv();
ClearKeyFormat();
ClearKeyFormatVersions();
}
// Checks that the HLS key is well-formed based on the requirements
// of an HLS key. Relies mainly on the requirements of METHOD.
bool IsWellFormed() const;
// == Parsing / Serialization ==
// Initializes the HLS key from a populated instance of
// HlsAttributeList.
//
// Requirements:
// - METHOD must be set, must be one of the well-known methods.
// - URI is optional, must be quoted string if set.
// - IV is optional, must be a 16-byte / 128-bit hex sequence if set.
// - KEYFORMAT is optional, must be a quoted string if set, defaults
// to "identify" is not set.
// - KEYFORMATVERSIONS is optional, must be a quoted string containing
// a valid sequence of '/' separated version if set, defaults to
// "1" if not set.
bool FromAttributeList(const HlsAttributeList& attr_list);
// Initializes the HLS key from the HLS serialized HLS Attribute List.
// Internally uses FromAttributeList(), and follows the same requirements.
bool ParseAttributeList(const std::string& attr_list_rep);
// Packages the HLS key into the HlsAttributeList instance.
// Clears existing data in |attr_list|.
//
// Serializing does not enforce any field requirements other than
// types.
//
// Follows the following rules:
// - METHOD packaged as enum string if set
// - URI packaged as quoted string if set
// - IV packaged as hex sequence if set
// - KEYFORMAT packaged as quoted string if set
// - KEYFORMATVERSIONS packaged as quoted string containing slash
// separated version if any version is set.
bool ToAttributeList(HlsAttributeList* attr_list) const;
// Serializes the HLS key into an HLS attribute string format.
// Internally uses ToAttributeList(), and follows the same packaging
// rules.
std::string Serialize() const;
bool SerializeToStream(std::ostream* out) const;
private:
// Values are either empty or set to valid values.
std::string method_;
std::string uri_;
std::vector<uint8_t> iv_;
std::string key_format_;
std::set<uint64_t> key_format_versions_;
}; // class HlsKey
} // namespace wvutil
#endif // WVCDM_UTIL_HLS_KEY_H_

469
util/src/hls_key.cpp Normal file
View File

@@ -0,0 +1,469 @@
// Copyright 2025 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "hls_key.h"
#include <inttypes.h>
#include <algorithm>
#include <ostream>
#include <set>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "hls_attribute_list.h"
#include "log.h"
#include "string_conversions.h"
#include "string_utils.h"
namespace wvutil {
namespace {
// If KEYFORMATVERSIONS specifies multiple versions, then each
// version is separated by a '/' character.
constexpr char kVersionSeparatorChar = '/';
constexpr char kZeroChar = '0';
// RFC 8216 specifies that KEYFORMAT is optional, but when
// METHOD is not NONE and KEYFORMAT absent, then KEYFORMAT is assumed
// to be "identity".
constexpr char kDefaultKeyFormat[] = "identity";
// RFC 8216 specifies that KEYFORMATVERSIONS is optional, but when
// METHOD is not NONE, and KEYFORMATVERSIONS absent, then
// KEYFORMATVERSIONS is assumed to be "1".
constexpr uint64_t kDefaultKeyFormatVersion = 1;
// Checks each version in the |versions| container are valid
// values.
template <class Container>
bool IsValidKeyFormatVersionsInternal(const Container& versions) {
// Container should be of uint64_t.
static_assert(std::is_same_v<typename Container::value_type, uint64_t>,
"Container must have values of uint64_t");
if (versions.empty()) return false;
// Note: Duplicate values are technically not invalid.
return std::all_of(versions.begin(), versions.end(),
HlsKey::IsValidKeyFormatVersion);
}
bool IsValidKeyFormatVersionRep(const std::string& version_rep) {
// Must be a valid HLS integer.
if (!HlsAttributeList::IsValidIntegerRep(version_rep)) return false;
// Version cannot be zero.
// Note: HLS integer strings may contain leading zeros, so long
// as not all the digits are zero, the |version_rep| is valid.
const size_t zero_count =
std::count(version_rep.begin(), version_rep.end(), kZeroChar);
return zero_count < version_rep.size();
}
bool ParseKeyFormatVersionList(const std::string& version_list_rep,
std::set<uint64_t>* versions) {
versions->clear();
const std::vector<std::string> version_reps =
StringSplit(version_list_rep, kVersionSeparatorChar);
if (version_reps.empty()) {
LOGV("Invalid key format version list rep");
return false;
}
for (const auto& version_rep : version_reps) {
uint64_t version = 0;
if (!HlsAttributeList::ParseInteger(version_rep, &version)) {
LOGV("Failed to parse key format version: %s", version_rep.c_str());
versions->clear();
return false;
}
if (!HlsKey::IsValidKeyFormatVersion(version)) {
LOGV("Found invalid key format version: %" PRIu64, version);
versions->clear();
return false;
}
versions->insert(version);
}
return true;
}
std::string KeyFormatVersionsToString(const std::vector<uint64_t>& versions) {
std::string result;
for (const uint64_t& version : versions) {
// Ensure each version is separated by a '/' character.
if (!result.empty()) result.push_back(kVersionSeparatorChar);
result.append(std::to_string(version));
}
return result;
}
std::string KeyFormatVersionsToString(const std::set<uint64_t>& versions) {
// std::set are not necessarily sorted. Converting to a vector
// to allow formatting in ascending order.
std::vector<uint64_t> versions_vec(versions.begin(), versions.end());
std::sort(versions_vec.begin(), versions_vec.end());
return KeyFormatVersionsToString(versions_vec);
}
} // namespace
// static
bool HlsKey::IsKnownMethod(const std::string& method) {
return method == kMethodNone || method == kMethodAes128 ||
method == kMethodSampleAes;
}
// static
bool HlsKey::IsValidMethod(const std::string& method) {
return !method.empty() && HlsAttributeList::IsValidEnumStringValue(method);
}
// static
bool HlsKey::IsValidUriValue(const std::string& uri) {
return !uri.empty() && HlsAttributeList::IsValidQuotedStringValue(uri);
}
// static
bool HlsKey::IsValidKeyFormat(const std::string& key_format) {
return !key_format.empty() &&
HlsAttributeList::IsValidQuotedStringValue(key_format);
}
// static
bool HlsKey::IsValidKeyFormatVersions(const std::set<uint64_t>& versions) {
return IsValidKeyFormatVersionsInternal(versions);
}
// static
bool HlsKey::IsValidKeyFormatVersionListRep(
const std::string& version_list_rep) {
const std::vector<std::string> version_reps =
StringSplit(version_list_rep, kVersionSeparatorChar);
if (version_reps.empty()) return false;
return std::all_of(version_reps.begin(), version_reps.end(),
IsValidKeyFormatVersionRep);
}
bool HlsKey::SetMethod(const std::string& method) {
if (!IsValidMethod(method)) {
LOGV("Invalid method: %s", SafeByteIdToString(method).c_str());
return false;
}
method_ = method;
return true;
}
bool HlsKey::SetUri(const std::string& uri) {
if (!IsValidUriValue(uri)) {
LOGV("Invalid URI: %s", SafeByteIdToString(uri).c_str());
return false;
}
uri_ = uri;
return true;
}
bool HlsKey::SetIv(const std::vector<uint8_t>& iv) {
if (!IsValidIv(iv)) {
LOGV("Invalid IV: 0x%s (length = %zu)", b2a_hex(iv).c_str(), iv.size());
return false;
}
iv_ = iv;
return true;
}
bool HlsKey::SetIv(const std::string& iv) {
if (!IsValidIv(iv)) {
LOGV("Invalid IV: 0x%s (length = %zu)", b2a_hex(iv).c_str(), iv.size());
return false;
}
iv_.assign(iv.begin(), iv.end());
return true;
}
bool HlsKey::SetIvHex(const std::string& iv_hex) {
std::vector<uint8_t> iv = a2b_hex(iv_hex);
if (iv.empty() && !iv_hex.empty()) {
LOGV("Invalid IV hex: %s", SafeByteIdToString(iv_hex).c_str());
return false;
}
if (!IsValidIv(iv)) {
LOGV("Invalid IV: 0x%s (length = %zu)", b2a_hex(iv).c_str(), iv.size());
return false;
}
iv_ = std::move(iv);
return true;
}
bool HlsKey::SetKeyFormat(const std::string& key_format) {
if (!IsValidKeyFormat(key_format)) {
LOGV("Invalid key format: %s", SafeByteIdToString(key_format).c_str());
return false;
}
key_format_ = key_format;
return true;
}
bool HlsKey::HasKeyFormatVersion(uint64_t version) const {
return key_format_versions_.find(version) != key_format_versions_.end();
}
bool HlsKey::SetKeyFormatVersions(const std::set<uint64_t>& versions) {
if (!IsValidKeyFormatVersionsInternal(versions)) {
LOGV("Invalid key format versions: %s",
KeyFormatVersionsToString(versions).c_str());
return false;
}
key_format_versions_ = versions;
return true;
}
bool HlsKey::SetKeyFormatVersions(const std::vector<uint64_t>& versions) {
if (!IsValidKeyFormatVersionsInternal(versions)) {
LOGV("Invalid key format versions: %s",
KeyFormatVersionsToString(versions).c_str());
return false;
}
key_format_versions_.clear();
key_format_versions_.insert(versions.begin(), versions.end());
return true;
}
bool HlsKey::AddKeyFormatVersion(uint64_t version) {
if (!IsValidKeyFormatVersion(version)) {
LOGV("Invalid key format version: %" PRIu64, version);
return false;
}
key_format_versions_.insert(version);
return true;
}
bool HlsKey::IsWellFormed() const {
// METHOD is always required.
if (!HasMethod()) {
LOGV("Missing method");
return false;
}
if (method() == kMethodNone) {
// From RFC 8216, section 4.3.2.4:
// If the encryption method is NONE, other attributes
// MUST NOT be present.
if (HasUri()) {
LOGV("URI not allowed if method is NONE: uri = %s", uri_.c_str());
return false;
}
if (HasIv()) {
LOGV("IV not allowed if method is NONE: iv = 0x%s", b2a_hex(iv_).c_str());
return false;
}
if (HasKeyFormat()) {
LOGV("Key format not allowed if method is NONE: key_format = %s",
key_format_.c_str());
return false;
}
if (HasKeyFormatVersions()) {
LOGV(
"Key format versions are not allowed if method is NONE: "
"key_format_versions = %s",
KeyFormatVersionsToString(key_format_versions_).c_str());
return false;
}
return true;
}
// METHOD != NONE
// URI is the only required field if METHOD is not NONE.
if (!HasUri()) {
LOGV("Missing URI");
return false;
}
// IV, key format and key format versions are optional fields.
return true;
}
bool HlsKey::FromAttributeList(const HlsAttributeList& list) {
if (list.IsEmpty()) {
LOGV("HLS attribute list is empty");
return false;
}
if (!list.IsType(kMethodName, HlsAttributeList::kEnumStringType)) {
LOGV("HLS attribute list is missing METHOD");
return false;
}
std::string method;
if (!list.GetEnumString(kMethodName, &method)) {
// Definitely an error as the check above should
// ensure method is available.
LOGE("Failed to get METHOD");
return false;
}
if (!IsValidMethod(method)) {
LOGV("Invalid method: %s", method.c_str());
return false;
}
if (method == kMethodNone) {
// Ignore all other fields.
Clear();
method_ = std::move(method);
return true;
}
// URI is the only required field.
if (!list.IsType(kUriName, HlsAttributeList::kQuotedStringType)) {
LOGV("HLS attribute list is missing URI");
return false;
}
std::string uri;
if (!list.GetQuotedString(kUriName, &uri)) {
LOGE("Failed to get URI");
return false;
}
if (!IsValidUriValue(uri)) {
LOGV("Invalid URI: \"%s\"", uri.c_str());
return false;
}
std::vector<uint8_t> iv;
if (list.IsType(kIvName, HlsAttributeList::kHexSequenceType)) {
if (!list.GetHexSequence(kIvName, &iv)) {
LOGE("Failed to get IV");
return false;
}
if (!IsValidIv(iv)) {
// Note: hex sequences are never empty.
LOGV("Invalid IV: 0x%s", b2a_hex(iv).c_str());
return false;
}
} else if (list.Contains(kIvName)) {
LOGV("HLS attribute list contains an invalid IV");
return false;
}
std::string key_format = kDefaultKeyFormat;
if (list.IsType(kKeyFormatName, HlsAttributeList::kQuotedStringType)) {
if (!list.GetQuotedString(kKeyFormatName, &key_format)) {
LOGE("Failed to get KEYFORMAT");
return false;
}
if (!IsValidKeyFormat(key_format)) {
LOGV("Invalid key format: \"%s\"", key_format.c_str());
return false;
}
} else if (list.Contains(kKeyFormatName)) {
LOGV("HLS attribute list contains an invalid KEYFORMAT");
return false;
}
std::set<uint64_t> versions = {kDefaultKeyFormatVersion};
if (list.IsType(kKeyFormatVersionsName,
HlsAttributeList::kQuotedStringType)) {
versions.clear();
std::string version_list_rep;
if (!list.GetQuotedString(kKeyFormatVersionsName, &version_list_rep)) {
LOGE("Failed to get KEYFORMATVERSIONS");
return false;
}
if (!IsValidKeyFormatVersionListRep(version_list_rep)) {
LOGV("Invalid key format versions: \"%s\"", version_list_rep.c_str());
return false;
}
if (!ParseKeyFormatVersionList(version_list_rep, &versions)) {
LOGE("Failed to parse key format versions: \"%s\"",
version_list_rep.c_str());
return false;
}
} else if (list.Contains(kKeyFormatVersionsName)) {
LOGV("HLS attribute list contains an invalid KEYFORMATVERSIONS");
return false;
}
// Ignore all other fields as they might be from a future HLS version.
method_ = std::move(method);
uri_ = std::move(uri);
iv_ = std::move(iv);
key_format_ = std::move(key_format);
key_format_versions_ = std::move(versions);
return true;
}
bool HlsKey::ParseAttributeList(const std::string& list_rep) {
if (list_rep.empty()) {
LOGV("HLS attribute list rep is empty");
return false;
}
HlsAttributeList list;
if (!list.Parse(list_rep)) {
LOGV("Failed to parse HLS attribute list");
return false;
}
return FromAttributeList(list);
}
// Note: Any value that is set is assumed to able to
// produce a valid HLS attribute value, and therefore, setting
// the attribute should be successful. Failure to do so is
// considered an internal error.
bool HlsKey::ToAttributeList(HlsAttributeList* list) const {
if (list == nullptr) {
LOGE("Output |list| is null");
return false;
}
if (!IsWellFormed()) {
// Serialize anyways.
LOGW("Current HLS key is incomplete");
}
list->Clear();
if (HasMethod() && !list->SetEnumString(kMethodName, method_)) {
LOGE("Failed to set method: %s=%s", kMethodName, method_.c_str());
list->Clear();
return false;
}
if (HasUri() && !list->SetQuotedString(kUriName, uri_)) {
LOGE("Failed to set URI: %s=\"%s\"", kUriName, uri_.c_str());
list->Clear();
return false;
}
if (HasIv() && !list->SetHexSequence(kIvName, iv_)) {
LOGE("Failed to set IV: %s=0x%s", kIvName, b2a_hex(iv_).c_str());
list->Clear();
return false;
}
if (HasKeyFormat() && !list->SetQuotedString(kKeyFormatName, key_format_)) {
LOGE("Failed to set key format: %s=\"%s\"", kKeyFormatName,
key_format_.c_str());
list->Clear();
return false;
}
if (HasKeyFormatVersions()) {
const std::string formatted_versions =
KeyFormatVersionsToString(key_format_versions_);
if (!list->SetQuotedString(kKeyFormatVersionsName, formatted_versions)) {
LOGE("Failed to set key format versions: %s=\"%s\"",
kKeyFormatVersionsName, formatted_versions.c_str());
list->Clear();
return false;
}
}
return true;
}
std::string HlsKey::Serialize() const {
HlsAttributeList list;
if (!ToAttributeList(&list)) {
LOGV("Failed to convert HLS key to HLS attribute list");
return std::string();
}
return list.Serialize();
}
bool HlsKey::SerializeToStream(std::ostream* out) const {
if (out == nullptr) {
LOGE("Output stream is null");
return false;
}
HlsAttributeList list;
if (!ToAttributeList(&list)) {
LOGV("Failed to convert HLS key to HLS attribute list");
return false;
}
return list.SerializeToStream(out);
}
} // namespace wvutil

View File

@@ -0,0 +1,866 @@
// Copyright 2025 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "hls_key.h"
#include <inttypes.h>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include <gtest/gtest.h>
#include "hls_attribute_list.h"
#include "string_conversions.h"
namespace wvutil {
namespace test {
TEST(HlsKeyTest, ClassConstants) {
// Validity checks on the class constants.
// Verify names.
EXPECT_TRUE(HlsAttributeList::IsValidName(HlsKey::kMethodName))
<< "METHOD key invalid: " << HlsKey::kMethodName;
EXPECT_TRUE(HlsAttributeList::IsValidName(HlsKey::kUriName))
<< "URI key invalid: " << HlsKey::kUriName;
EXPECT_TRUE(HlsAttributeList::IsValidName(HlsKey::kIvName))
<< "IV key invalid: " << HlsKey::kIvName;
EXPECT_TRUE(HlsAttributeList::IsValidName(HlsKey::kKeyFormatName))
<< "KEYFORMAT key invalid: " << HlsKey::kKeyFormatName;
EXPECT_TRUE(HlsAttributeList::IsValidName(HlsKey::kKeyFormatVersionsName))
<< "KEYFORMATVERSIONS key invalid: " << HlsKey::kKeyFormatVersionsName;
// Well-known methods.
EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue(HlsKey::kMethodNone))
<< "METHOD=NONE value invalid: " << HlsKey::kMethodNone;
EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue(HlsKey::kMethodAes128))
<< "METHOD=AES-128 value invalid: " << HlsKey::kMethodAes128;
EXPECT_TRUE(
HlsAttributeList::IsValidEnumStringValue(HlsKey::kMethodSampleAes))
<< "METHOD=SAMPLE-AES value invalid: " << HlsKey::kMethodSampleAes;
}
TEST(HlsKeyTest, IsValidKeyFormatVersion) {
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersion(1));
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersion(2));
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersion(1337));
EXPECT_TRUE(
HlsKey::IsValidKeyFormatVersion(std::numeric_limits<uint64_t>::max()));
}
TEST(HlsKeyTest, IsValidKeyFormatVersion_Invalid) {
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersion(0));
}
TEST(HlsKeyTest, IsValidKeyFormatVersions) {
const std::set<uint64_t> kSingleVersion = {2};
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersions(kSingleVersion));
const std::set<uint64_t> kFewVersions = {1, 2, 5};
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersions(kFewVersions));
const std::set<uint64_t> kLargeVersion = {
1, 2, std::numeric_limits<uint64_t>::max()};
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersions(kLargeVersion));
}
TEST(HlsKeyTest, IsValidKeyFormatVersions_Invalid) {
const std::set<uint64_t> kEmptySet;
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersions(kEmptySet));
const std::set<uint64_t> kZeroOnly = {0};
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersions(kZeroOnly));
const std::set<uint64_t> kWithZero = {0, 1, 2, 5};
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersions(kWithZero));
}
TEST(HlsKeyTest, IsValidKeyFormatVersionListRep) {
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersionListRep("1"));
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersionListRep("1/2"));
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersionListRep("1/2/5"));
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersionListRep("1337"));
}
TEST(HlsKeyTest, IsValidKeyFormatVersionListRep_Invalid) {
// Empty not allowed.
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep(""));
// Zero is not allowed.
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("0"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("0/2/5"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/0/5"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/2/0"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/2/000000"));
// All must integers.
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("NONE"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/NONE/4"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/1.2/2"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/0xDEADBEAF"));
// Cannot have empty values.
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("/2/5"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1//5"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/2/"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("/"));
// Must be a slash separated list.
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1,0,5"));
// Cannot have spaces.
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep(" 1/2/5"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1 /2/5"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/ 2/5"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/2 /5"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/2/ 5"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/2/5 "));
// Must fit in the 64-bit integer range.
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("100000000000000000000"));
}
TEST(HlsKeyTest, Empty) {
const HlsKey key;
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
EXPECT_FALSE(key.HasKeyFormat());
EXPECT_FALSE(key.HasKeyFormatVersions());
EXPECT_FALSE(key.HasKeyFormatVersion(1));
}
TEST(HlsKeyTest, SetMethod) {
HlsKey key;
EXPECT_TRUE(key.SetMethod("NONE"));
EXPECT_TRUE(key.HasMethod());
EXPECT_EQ(key.method(), "NONE");
key.ClearMethod();
EXPECT_FALSE(key.HasMethod());
}
TEST(HlsKeyTest, SetMethod_Invalid) {
HlsKey key;
// Check that an invalid cannot be set.
EXPECT_FALSE(key.SetMethod("NOT VALID"));
EXPECT_FALSE(key.HasMethod());
EXPECT_TRUE(key.SetMethod("SAMPLE-AES"));
// Check that setting an invalid method does not
// overwrite the existing method.
EXPECT_FALSE(key.SetMethod("NOT VALID"));
EXPECT_EQ(key.method(), "SAMPLE-AES");
}
TEST(HlsKeyTest, SetUri) {
const std::string kSampleUri = "http://media.src/some-key";
HlsKey key;
EXPECT_TRUE(key.SetUri(kSampleUri));
EXPECT_TRUE(key.HasUri());
EXPECT_EQ(key.uri(), kSampleUri);
key.ClearUri();
EXPECT_FALSE(key.HasUri());
}
TEST(HlsKeyTest, SetUri_Invalid) {
const std::string kBadUri = "No\"quotes";
const std::string kGoodUri = "http://media.src/some-key";
HlsKey key;
EXPECT_FALSE(key.SetUri(kBadUri));
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.SetUri(""));
EXPECT_FALSE(key.HasUri());
EXPECT_TRUE(key.SetUri(kGoodUri));
EXPECT_FALSE(key.SetUri(kBadUri));
EXPECT_EQ(key.uri(), kGoodUri);
}
TEST(HlsKeyTest, SetIv_Vec) {
const std::vector<uint8_t> kGoodIv(HlsKey::kIvLength, 42);
HlsKey key;
EXPECT_TRUE(key.SetIv(kGoodIv));
EXPECT_TRUE(key.HasIv());
EXPECT_EQ(key.iv(), kGoodIv);
key.ClearIv();
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, SetIv_Vec_Invalid) {
const std::vector<uint8_t> kGoodIv(HlsKey::kIvLength, 42);
const std::vector<uint8_t> kEmptyIv;
const std::vector<uint8_t> kShortIv(HlsKey::kIvLength / 2, 22);
const std::vector<uint8_t> kLargeIv(HlsKey::kIvLength * 2, 22);
HlsKey key;
EXPECT_FALSE(key.SetIv(kEmptyIv));
EXPECT_FALSE(key.SetIv(kShortIv));
EXPECT_FALSE(key.SetIv(kLargeIv));
EXPECT_FALSE(key.HasIv());
EXPECT_TRUE(key.SetIv(kGoodIv));
EXPECT_FALSE(key.SetIv(kShortIv));
EXPECT_EQ(key.iv(), kGoodIv);
}
TEST(HlsKeyTest, SetIv_String) {
const std::string kGoodIv(HlsKey::kIvLength, 'x');
HlsKey key;
EXPECT_TRUE(key.SetIv(kGoodIv));
EXPECT_TRUE(key.HasIv());
const std::vector<uint8_t> kGoodIvVec(kGoodIv.begin(), kGoodIv.end());
EXPECT_EQ(key.iv(), kGoodIvVec);
key.ClearIv();
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, SetIv_String_Invalid) {
const std::vector<uint8_t> kGoodIv(HlsKey::kIvLength, 42);
const std::string kEmptyIv;
const std::string kShortIv(HlsKey::kIvLength / 2, 'y');
const std::string kLargeIv(HlsKey::kIvLength * 2, 'z');
HlsKey key;
EXPECT_FALSE(key.SetIv(kEmptyIv));
EXPECT_FALSE(key.SetIv(kShortIv));
EXPECT_FALSE(key.SetIv(kLargeIv));
EXPECT_FALSE(key.HasIv());
EXPECT_TRUE(key.SetIv(kGoodIv));
EXPECT_FALSE(key.SetIv(kShortIv));
EXPECT_EQ(key.iv(), kGoodIv);
}
TEST(HlsKeyTest, SetIvHex) {
const std::vector<uint8_t> kGoodIv(HlsKey::kIvLength, 42);
const std::string kGoodIvHex = b2a_hex(kGoodIv);
HlsKey key;
EXPECT_TRUE(key.SetIvHex(kGoodIvHex));
EXPECT_TRUE(key.HasIv());
EXPECT_EQ(key.iv(), kGoodIv);
key.ClearIv();
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, SetIvHex_Invalid) {
const std::vector<uint8_t> kGoodIv(HlsKey::kIvLength, 42);
const std::string kEmptyIvHex;
const std::string kShortIvHex(HlsKey::kIvLength, 'e');
const std::string kLargeIvHex(HlsKey::kIvLength * 4, 'e');
const std::string kNonHexIv(HlsKey::kIvLength * 2, 'x');
HlsKey key;
EXPECT_FALSE(key.SetIvHex(kEmptyIvHex));
EXPECT_FALSE(key.SetIvHex(kShortIvHex));
EXPECT_FALSE(key.SetIvHex(kLargeIvHex));
EXPECT_FALSE(key.SetIvHex(kNonHexIv));
EXPECT_TRUE(key.SetIv(kGoodIv));
EXPECT_FALSE(key.SetIvHex(kShortIvHex));
EXPECT_EQ(key.iv(), kGoodIv);
}
TEST(HlsKeyTest, SetKeyFormat) {
const std::string kGoodKeyFormat = "com.widevine";
HlsKey key;
EXPECT_TRUE(key.SetKeyFormat(kGoodKeyFormat));
EXPECT_TRUE(key.HasKeyFormat());
EXPECT_EQ(key.key_format(), kGoodKeyFormat);
key.ClearKeyFormat();
EXPECT_FALSE(key.HasKeyFormat());
}
TEST(HlsKeyTest, SetKeyFormat_Invalid) {
const std::string kGoodKeyFormat = "com.widevine";
const std::string kEmptyKeyFormat;
const std::string kInvalidKeyFormat = "com\"widevine";
HlsKey key;
EXPECT_FALSE(key.SetKeyFormat(kEmptyKeyFormat));
EXPECT_FALSE(key.SetKeyFormat(kInvalidKeyFormat));
EXPECT_TRUE(key.SetKeyFormat(kGoodKeyFormat));
EXPECT_FALSE(key.SetKeyFormat(kInvalidKeyFormat));
EXPECT_EQ(key.key_format(), kGoodKeyFormat);
}
TEST(HlsKeyTest, SetKeyFormatVersions_Set) {
const std::set<uint64_t> kSingleVersion = {3};
const std::set<uint64_t> kFewVersions = {1, 2, 5};
HlsKey key;
EXPECT_TRUE(key.SetKeyFormatVersions(kSingleVersion));
EXPECT_TRUE(key.HasKeyFormatVersions());
EXPECT_EQ(key.key_format_versions(), kSingleVersion);
key.ClearKeyFormatVersions();
EXPECT_FALSE(key.HasKeyFormatVersions());
EXPECT_TRUE(key.SetKeyFormatVersions(kFewVersions));
EXPECT_TRUE(key.HasKeyFormatVersions());
EXPECT_EQ(key.key_format_versions(), kFewVersions);
// Ensure that set is not merging.
EXPECT_TRUE(key.SetKeyFormatVersions(kSingleVersion));
EXPECT_EQ(key.key_format_versions(), kSingleVersion);
}
TEST(HlsKeyTest, SetKeyFormatVersions_Set_Invalid) {
const std::set<uint64_t> kSingleVersion = {3};
const std::set<uint64_t> kBadVersions = {0, 2, 5};
HlsKey key;
EXPECT_FALSE(key.SetKeyFormatVersions(kBadVersions));
EXPECT_FALSE(key.HasKeyFormatVersions());
EXPECT_TRUE(key.SetKeyFormatVersions(kSingleVersion));
EXPECT_FALSE(key.SetKeyFormatVersions(kBadVersions));
EXPECT_EQ(key.key_format_versions(), kSingleVersion);
}
TEST(HlsKeyTest, SetKeyFormatVersions_Vec) {
const std::vector<uint64_t> kSingleVersionVec = {3};
const std::vector<uint64_t> kFewVersionsVec = {1, 2, 5};
const std::vector<uint64_t> kRepeatedVersionsVec = {1, 1, 2, 5, 5, 5, 5, 5};
const std::set<uint64_t> kSingleVersion = {3};
const std::set<uint64_t> kFewVersions = {1, 2, 5};
HlsKey key;
EXPECT_TRUE(key.SetKeyFormatVersions(kSingleVersionVec));
EXPECT_TRUE(key.HasKeyFormatVersions());
EXPECT_EQ(key.key_format_versions(), kSingleVersion);
key.ClearKeyFormatVersions();
EXPECT_FALSE(key.HasKeyFormatVersions());
EXPECT_TRUE(key.SetKeyFormatVersions(kFewVersionsVec));
EXPECT_TRUE(key.HasKeyFormatVersions());
EXPECT_EQ(key.key_format_versions(), kFewVersions);
// Ensure that set is not merging.
EXPECT_TRUE(key.SetKeyFormatVersions(kSingleVersionVec));
EXPECT_EQ(key.key_format_versions(), kSingleVersion);
// Ensure that repeated values are not stored.
EXPECT_TRUE(key.SetKeyFormatVersions(kRepeatedVersionsVec));
EXPECT_EQ(key.key_format_versions(), kFewVersions);
}
TEST(HlsKeyTest, SetKeyFormatVersions_Vec_Invalid) {
const std::set<uint64_t> kSingleVersion = {3};
const std::vector<uint64_t> kBadVersionsVec = {1, 2, 0, 5};
HlsKey key;
EXPECT_FALSE(key.SetKeyFormatVersions(kBadVersionsVec));
EXPECT_FALSE(key.HasKeyFormatVersions());
EXPECT_TRUE(key.SetKeyFormatVersions(kSingleVersion));
EXPECT_FALSE(key.SetKeyFormatVersions(kBadVersionsVec));
EXPECT_EQ(key.key_format_versions(), kSingleVersion);
}
TEST(HlsKeyTest, HasKeyFormatVersion) {
const std::set<uint64_t> kFewVersions = {1, 2, 5};
HlsKey key;
EXPECT_FALSE(key.HasKeyFormatVersion(1));
EXPECT_FALSE(key.HasKeyFormatVersion(std::numeric_limits<uint64_t>::max()));
EXPECT_TRUE(key.SetKeyFormatVersions(kFewVersions));
EXPECT_FALSE(key.HasKeyFormatVersion(0));
EXPECT_TRUE(key.HasKeyFormatVersion(1));
EXPECT_TRUE(key.HasKeyFormatVersion(2));
EXPECT_FALSE(key.HasKeyFormatVersion(3));
EXPECT_FALSE(key.HasKeyFormatVersion(4));
EXPECT_TRUE(key.HasKeyFormatVersion(5));
EXPECT_FALSE(key.HasKeyFormatVersion(std::numeric_limits<uint64_t>::max()));
}
TEST(HlsKeyTest, AddKeyFormatVersion) {
const std::set<uint64_t> kFewVersions = {1, 2, 5};
HlsKey key;
EXPECT_TRUE(key.AddKeyFormatVersion(1));
EXPECT_TRUE(key.AddKeyFormatVersion(2));
EXPECT_TRUE(key.AddKeyFormatVersion(5));
EXPECT_EQ(key.key_format_versions(), kFewVersions);
// Repeated adds doesn't count as error.
EXPECT_TRUE(key.AddKeyFormatVersion(5));
// Cannot add invalid.
EXPECT_FALSE(key.AddKeyFormatVersion(0));
EXPECT_EQ(key.key_format_versions(), kFewVersions);
}
TEST(HlsKeyTest, IsWellFormed_Empty) {
const HlsKey key;
EXPECT_FALSE(key.IsWellFormed());
}
TEST(HlsKeyTest, IsWellFormed_MethodNone) {
HlsKey key;
EXPECT_TRUE(key.SetMethod("NONE"));
EXPECT_TRUE(key.IsWellFormed());
}
// When method is none, all other fields must be empty.
TEST(HlsKeyTest, IsWellFormed_MethodNone_Invalid) {
HlsKey key;
EXPECT_TRUE(key.SetMethod("NONE"));
// With URI.
EXPECT_TRUE(key.SetUri("http://domain.tld/key"));
EXPECT_FALSE(key.IsWellFormed());
key.ClearUri();
// With IV.
EXPECT_TRUE(key.SetIv(std::vector<uint8_t>(HlsKey::kIvLength, 42)));
EXPECT_FALSE(key.IsWellFormed());
key.ClearIv();
// With key format.
EXPECT_TRUE(key.SetKeyFormat("com.widevine"));
EXPECT_FALSE(key.IsWellFormed());
key.ClearKeyFormat();
// With key format versions.
EXPECT_TRUE(key.AddKeyFormatVersion(1));
EXPECT_FALSE(key.IsWellFormed());
key.ClearKeyFormatVersions();
// Should be well-formed again.
EXPECT_TRUE(key.IsWellFormed());
}
TEST(HlsKeyTest, IsWellFormed_MethodNonNone) {
HlsKey key;
EXPECT_TRUE(key.SetMethod("AES-128"));
// Required fields.
EXPECT_TRUE(key.SetUri("http://domain.tld/key"));
EXPECT_TRUE(key.SetIv(std::vector<uint8_t>(HlsKey::kIvLength, 42)));
EXPECT_TRUE(key.IsWellFormed());
// Optional fields.
EXPECT_TRUE(key.SetKeyFormat("com.widevine"));
EXPECT_TRUE(key.IsWellFormed());
EXPECT_TRUE(key.AddKeyFormatVersion(1));
EXPECT_TRUE(key.IsWellFormed());
}
TEST(HlsKeyTest, IsWellFormed_MethodNonNone_Invalid) {
HlsKey key;
EXPECT_TRUE(key.SetMethod("AES-128"));
// Missing required all fields.
EXPECT_FALSE(key.IsWellFormed());
// URI is the only required field.
EXPECT_TRUE(key.SetUri("http://domain.tld/key"));
EXPECT_TRUE(key.IsWellFormed());
// Missing URI.
key.ClearUri();
EXPECT_FALSE(key.IsWellFormed());
}
TEST(HlsKeyTest, FromAttributeList_MethodNone) {
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "NONE"));
HlsKey key;
EXPECT_TRUE(key.FromAttributeList(list));
EXPECT_TRUE(key.IsWellFormed());
EXPECT_EQ(key.method(), "NONE");
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
EXPECT_FALSE(key.HasKeyFormat());
EXPECT_FALSE(key.HasKeyFormatVersions());
}
TEST(HlsKeyTest, FromAttributeList_MethodNone_IgnoreOtherFields) {
// The HLS specification states that if METHOD is NONE, then
// there should not be any other fields. However, this parser
// is less strict, and will simply ignore the other fields
// when reading from an attribute list.
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "NONE"));
EXPECT_TRUE(
list.SetHexSequence("IV", std::vector<uint8_t>(HlsKey::kIvLength, 42)));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetQuotedString("KEYFORMAT", "com.example"));
EXPECT_TRUE(list.SetQuotedString("KEYFORMATVERSIONS", "1337"));
HlsKey key;
EXPECT_TRUE(key.FromAttributeList(list));
EXPECT_TRUE(key.IsWellFormed());
EXPECT_EQ(key.method(), "NONE");
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
EXPECT_FALSE(key.HasKeyFormat());
EXPECT_FALSE(key.HasKeyFormatVersions());
}
TEST(HlsKeyTest, FromAttributeList_MethodNonNone_OnlyRequiredFields) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
const std::string kUri = "http://domain.tld/key";
// HLS standard says to use the following defaults if not
// present in the HLS list.
const std::string kDefaultKeyFormat = "identity";
const std::set<uint64_t> kDefaultKeyFormatVersions = {1};
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
EXPECT_TRUE(list.SetQuotedString("URI", kUri));
HlsKey key;
EXPECT_TRUE(key.FromAttributeList(list));
EXPECT_TRUE(key.IsWellFormed());
EXPECT_EQ(key.method(), "AES-128");
EXPECT_EQ(key.uri(), kUri);
EXPECT_EQ(key.iv(), kIv);
EXPECT_EQ(key.key_format(), kDefaultKeyFormat);
EXPECT_EQ(key.key_format_versions(), kDefaultKeyFormatVersions);
}
TEST(HlsKeyTest, FromAttributeList_MethodNonNone_AllFields) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
const std::string kUri = "http://domain.tld/key";
const std::string kKeyFormat = "com.widevine";
const std::set<uint64_t> kKeyFormatVersion = {1, 2, 5};
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "SAMPLE-AES"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
EXPECT_TRUE(list.SetQuotedString("URI", kUri));
EXPECT_TRUE(list.SetQuotedString("KEYFORMAT", kKeyFormat));
EXPECT_TRUE(list.SetQuotedString("KEYFORMATVERSIONS", "1/2/5"));
HlsKey key;
EXPECT_TRUE(key.FromAttributeList(list));
EXPECT_TRUE(key.IsWellFormed());
EXPECT_EQ(key.method(), "SAMPLE-AES");
EXPECT_EQ(key.uri(), kUri);
EXPECT_EQ(key.iv(), kIv);
EXPECT_EQ(key.key_format(), kKeyFormat);
EXPECT_EQ(key.key_format_versions(), kKeyFormatVersion);
}
TEST(HlsKeyTest, FromAttributeList_MissingFields_All) {
HlsAttributeList list;
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
}
TEST(HlsKeyTest, FromAttributeList_MissingFields_Method) {
const std::string kUri = "http://domain.tld/key";
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetQuotedString("URI", kUri));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, FromAttributeList_MissingFields_MissingUri) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_BadMethodType) {
HlsAttributeList list;
// Must be enum string not quoted string.
EXPECT_TRUE(list.SetQuotedString("METHOD", "NONE"));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
EXPECT_FALSE(key.HasMethod());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_EmptyUri) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", ""));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_BadUriType) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetEnumString("URI", "DATA"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_ShortIv) {
const std::vector<uint8_t> kShortIv(HlsKey::kIvLength / 2, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetHexSequence("IV", kShortIv));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_LargeIv) {
const std::vector<uint8_t> kLargeIv(HlsKey::kIvLength * 2, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetHexSequence("IV", kLargeIv));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_BadIvType) {
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetQuotedString("IV", "42 repeated 16 times"));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_EmptyKeyFormat) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
EXPECT_TRUE(list.SetQuotedString("KEYFORMAT", ""));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_BadKeyFormatType) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
EXPECT_TRUE(list.SetEnumString("KEYFORMAT", "COM-EXAMPLE"));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
EXPECT_FALSE(key.HasKeyFormat());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_EmptyKeyFormatVersions) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
EXPECT_TRUE(list.SetQuotedString("KEYFORMAT", "com.example"));
EXPECT_TRUE(list.SetQuotedString("KEYFORMATVERSIONS", ""));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
EXPECT_FALSE(key.HasKeyFormat());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_ZeroKeyFormatVersions) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
EXPECT_TRUE(list.SetQuotedString("KEYFORMAT", "com.example"));
EXPECT_TRUE(list.SetQuotedString("KEYFORMATVERSIONS", "0/1/5"));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
EXPECT_FALSE(key.HasKeyFormat());
EXPECT_FALSE(key.HasKeyFormatVersions());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_BadKeyFormatVersionsFormat) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
EXPECT_TRUE(list.SetQuotedString("KEYFORMAT", "com.example"));
EXPECT_TRUE(list.SetQuotedString("KEYFORMATVERSIONS", "1,2,5"));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
EXPECT_FALSE(key.HasKeyFormat());
EXPECT_FALSE(key.HasKeyFormatVersions());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_BadKeyFormatVersionsType) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
EXPECT_TRUE(list.SetQuotedString("KEYFORMAT", "com.example"));
EXPECT_TRUE(list.SetInteger("KEYFORMATVERSIONS", 1));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
EXPECT_FALSE(key.HasKeyFormat());
EXPECT_FALSE(key.HasKeyFormatVersions());
}
TEST(HlsKeyTest, FromAttributeList_InvalidList) {
// Populate HLS Attribute List as if it is a different message.
// Using EXT-X-MEDIA.
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("TYPE", "AUDIO"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/video-data"));
EXPECT_TRUE(list.SetQuotedString("GROUP-ID", "Video Group"));
EXPECT_TRUE(list.SetQuotedString("LANGUAGE", "en"));
EXPECT_TRUE(list.SetQuotedString("NAME", "A nice video"));
EXPECT_TRUE(list.SetEnumString("DEFAULT", "YES"));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
}
TEST(HlsKeyTest, ParseAttributeList_MethodNone) {
HlsKey key;
EXPECT_TRUE(key.ParseAttributeList("METHOD=NONE"));
EXPECT_TRUE(key.IsWellFormed());
EXPECT_EQ(key.method(), "NONE");
}
TEST(HlsKeyTest, ParseAttributeList_MethodNonNone_OnlyRequiredFields) {
const std::string kUri = "http://domain.tld/key";
const std::vector<uint8_t> kEmptyIv;
const std::string kDefaultKeyFormat = "identity";
const std::set<uint64_t> kDefaultKeyFormatVersions = {1};
std::ostringstream hls_stream;
hls_stream << "METHOD=AES-128,";
hls_stream << "URI=\"" << kUri << "\"";
const std::string key_rep = hls_stream.str();
HlsKey key;
EXPECT_TRUE(key.ParseAttributeList(key_rep));
EXPECT_TRUE(key.IsWellFormed());
EXPECT_EQ(key.method(), "AES-128");
EXPECT_EQ(key.uri(), kUri);
EXPECT_EQ(key.iv(), kEmptyIv);
EXPECT_EQ(key.key_format(), kDefaultKeyFormat);
EXPECT_EQ(key.key_format_versions(), kDefaultKeyFormatVersions);
}
TEST(HlsKeyTest, ParseAttributeList_MethodNonNone_AllFields) {
const std::string kIvHex(HlsKey::kIvLength * 2, 'A');
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 0xaa);
const std::string kUri = "http://domain.tld/key";
const std::string kKeyFormat = "com.widevine";
const std::set<uint64_t> kKeyFormatVersion = {1, 2, 5};
std::ostringstream hls_stream;
hls_stream << "METHOD=SAMPLE-AES,";
hls_stream << "URI=\"" << kUri << "\",";
hls_stream << "IV=0x" << kIvHex << ',';
hls_stream << "KEYFORMAT=\"" << kKeyFormat << "\",";
hls_stream << "KEYFORMATVERSIONS=\"1/2/5\"";
const std::string key_rep = hls_stream.str();
HlsKey key;
EXPECT_TRUE(key.ParseAttributeList(key_rep));
EXPECT_TRUE(key.IsWellFormed());
EXPECT_EQ(key.method(), "SAMPLE-AES");
EXPECT_EQ(key.uri(), kUri);
EXPECT_EQ(key.iv(), kIv);
EXPECT_EQ(key.key_format(), kKeyFormat);
EXPECT_EQ(key.key_format_versions(), kKeyFormatVersion);
}
TEST(HlsKeyTest, ParseAttributeList_Empty) {
HlsKey key;
EXPECT_FALSE(key.ParseAttributeList(""));
}
TEST(HlsKeyTest, ParseAttributeList_InvalidAttributeList) {
HlsKey key;
EXPECT_FALSE(key.ParseAttributeList("Not an Attribute List"));
}
} // namespace test
} // namespace wvutil