Make change and version bump to AV1A.240704.001
Snap for 12047203 from b282ec92b6 to vic-widevine-partner-release
Change-Id: I5b2680f1f99d29c2ab4d24a9b6c80e1a282d97bd
This commit is contained in:
@@ -12,6 +12,7 @@ namespace wvutil {
|
||||
class FileSystem;
|
||||
} // namespace wvutil
|
||||
namespace wvcdm {
|
||||
// Forward declarations.
|
||||
class CryptoSession;
|
||||
class DeviceFiles;
|
||||
|
||||
@@ -20,6 +21,22 @@ class DeviceFiles;
|
||||
// different place.
|
||||
class SystemIdExtractor {
|
||||
public:
|
||||
// The constructor should be provided all the parameters necessary
|
||||
// to find the system ID. Although certain provisioning methods
|
||||
// may not use all parameters, this class must behave in a way which
|
||||
// makes it as easy as possible to obtain the system ID, all
|
||||
// parameters are required.
|
||||
//
|
||||
// Parameters:
|
||||
// |security_level|
|
||||
// - Requested security level, uses the |crypto_session| handle
|
||||
// to convert to a concrete security level.
|
||||
// |crypto_session|
|
||||
// - Handle into the OEMCrypto platform. If handle is open,
|
||||
// then the session's real security level should match
|
||||
// |security_level|.
|
||||
// |fs|
|
||||
// - File system handle to the global file system.
|
||||
SystemIdExtractor(RequestedSecurityLevel security_level,
|
||||
CryptoSession* crypto_session, wvutil::FileSystem* fs);
|
||||
virtual ~SystemIdExtractor() {}
|
||||
@@ -30,12 +47,17 @@ class SystemIdExtractor {
|
||||
SystemIdExtractor& operator=(const SystemIdExtractor&) = delete;
|
||||
SystemIdExtractor& operator=(SystemIdExtractor&&) = delete;
|
||||
|
||||
// Extracts the system ID from the appropriate source.
|
||||
virtual bool ExtractSystemId(uint32_t* system_id);
|
||||
|
||||
// Extracts the system ID from a keybox key data (aka CA token).
|
||||
static bool ExtractSystemIdFromKeyboxData(const std::string& key_data,
|
||||
uint32_t* system_id);
|
||||
// Extracts the system ID from a serialized OEM certificate.
|
||||
// System ID is expected to be in the manufacturer's intermediate
|
||||
// X.509 certificate from the Widevine-defined X.509 v3
|
||||
// Extension found in the TBSCertificate "extensions" attribute.
|
||||
// See RFC 5280 for X.509 certificate structure.
|
||||
static bool ExtractSystemIdFromOemCert(const std::string& oem_cert,
|
||||
uint32_t* system_id);
|
||||
|
||||
@@ -44,13 +66,15 @@ class SystemIdExtractor {
|
||||
}
|
||||
|
||||
private:
|
||||
// Note: All the internal ExtractSystemId*() methods assume
|
||||
// |system_id| is not null.
|
||||
|
||||
// Extracts the system ID from keybox-based OEMCrypto implementations.
|
||||
// System ID is expected to be found in the keybox data. Devices
|
||||
// which require OTA keybox provisioning will return a null system ID.
|
||||
bool ExtractSystemIdProv20(uint32_t* system_id);
|
||||
// Extracts the system ID from OEM certificate-based OEMCrypto
|
||||
// implementations. System ID is expected to be in the manufacturers
|
||||
// intermediate X.509 certificate.
|
||||
// implementations.
|
||||
bool ExtractSystemIdProv30(uint32_t* system_id);
|
||||
// Extracts the system ID from BCC-based OEMCrypto implementations.
|
||||
// System ID is expected to be found in the stored OEM certificate
|
||||
@@ -59,9 +83,20 @@ class SystemIdExtractor {
|
||||
// a null system ID.
|
||||
bool ExtractSystemIdProv40(uint32_t* system_id);
|
||||
|
||||
// Add future extraction methods here.
|
||||
|
||||
// Verifies that if |crypto_session_| is opened, that the
|
||||
// security level is matches the instances |security_level_|.
|
||||
// If unopened, verifies that |security_level_| is a defined
|
||||
// value.
|
||||
// Returns true if security level is valid, false otherwise.
|
||||
bool VerifySecurityLevelExpectations();
|
||||
|
||||
RequestedSecurityLevel security_level_ = kLevelDefault;
|
||||
CryptoSession* crypto_session_ = nullptr;
|
||||
wvutil::FileSystem* fs_ = nullptr;
|
||||
// Test only handle to DeviceFiles. When not null, |fs_| will be
|
||||
// ignored.
|
||||
DeviceFiles* test_device_files_ = nullptr;
|
||||
};
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -18,16 +18,22 @@ constexpr size_t kKeyboxSystemIdOffset = 4;
|
||||
// Index of certificate within cerificate chain which contains the
|
||||
// system ID (0 = leaf/device cert, 1 = intermediate/device family cert).
|
||||
constexpr size_t kOemCertSystemIdIndex = 1;
|
||||
// OID of X.509 certificate extension containing the Widevine system ID.
|
||||
// OID of X.509 TBSCertificate extension containing the Widevine
|
||||
// system ID.
|
||||
const char kWidevineSystemIdExtensionOid[] = "1.3.6.1.4.1.11129.4.1.1";
|
||||
|
||||
constexpr size_t kSystemIdLength = sizeof(uint32_t);
|
||||
|
||||
constexpr bool IsSupportedSecurityLevel(CdmSecurityLevel security_level) {
|
||||
constexpr bool IsSupportedCdmSecurityLevel(CdmSecurityLevel security_level) {
|
||||
return security_level == kSecurityLevelL1 ||
|
||||
security_level == kSecurityLevelL2 ||
|
||||
security_level == kSecurityLevelL3;
|
||||
}
|
||||
|
||||
constexpr bool IsSupportedRequestedSecurityLevel(
|
||||
RequestedSecurityLevel security_level) {
|
||||
return security_level == kLevelDefault || security_level == kLevel3;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
SystemIdExtractor::SystemIdExtractor(RequestedSecurityLevel security_level,
|
||||
@@ -45,15 +51,21 @@ bool SystemIdExtractor::ExtractSystemId(uint32_t* system_id) {
|
||||
LOGE("Output |system_id| is null");
|
||||
return false;
|
||||
}
|
||||
if (crypto_session_->GetCachedSystemId(system_id)) {
|
||||
if (!VerifySecurityLevelExpectations()) {
|
||||
// VerifySecurityLevelExpectations() will log details.
|
||||
return false;
|
||||
}
|
||||
if (crypto_session_->IsOpen() &&
|
||||
crypto_session_->GetCachedSystemId(system_id)) {
|
||||
return true;
|
||||
}
|
||||
CdmClientTokenType type = kClientTokenUninitialized;
|
||||
const CdmResponseType status =
|
||||
crypto_session_->GetProvisioningMethod(security_level_, &type);
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("Failed to get provisioning method: security_level = %s, status = %d",
|
||||
RequestedSecurityLevelToString(security_level_), status.ToInt());
|
||||
LOGE("Failed to get provisioning method: security_level = %s, status = %s",
|
||||
RequestedSecurityLevelToString(security_level_),
|
||||
status.ToString().c_str());
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
@@ -90,7 +102,10 @@ bool SystemIdExtractor::ExtractSystemId(uint32_t* system_id) {
|
||||
// static
|
||||
bool SystemIdExtractor::ExtractSystemIdFromKeyboxData(
|
||||
const std::string& key_data, uint32_t* system_id) {
|
||||
if (system_id == nullptr) return false;
|
||||
if (system_id == nullptr) {
|
||||
LOGE("Output |system_id| is null");
|
||||
return false;
|
||||
}
|
||||
if (key_data.size() < (kKeyboxSystemIdOffset + kSystemIdLength)) {
|
||||
LOGE("Keybox data does not contain system ID: key_data_size = %zu",
|
||||
key_data.size());
|
||||
@@ -106,7 +121,10 @@ bool SystemIdExtractor::ExtractSystemIdFromKeyboxData(
|
||||
// static
|
||||
bool SystemIdExtractor::ExtractSystemIdFromOemCert(const std::string& oem_cert,
|
||||
uint32_t* system_id) {
|
||||
if (system_id == nullptr) return false;
|
||||
if (system_id == nullptr) {
|
||||
LOGE("Output |system_id| is null");
|
||||
return false;
|
||||
}
|
||||
return ExtractExtensionValueFromCertificate(oem_cert,
|
||||
kWidevineSystemIdExtensionOid,
|
||||
kOemCertSystemIdIndex, system_id);
|
||||
@@ -122,8 +140,9 @@ bool SystemIdExtractor::ExtractSystemIdProv20(uint32_t* system_id) {
|
||||
return true;
|
||||
}
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("Failed to get keybox data: security_level = %s, status = %d",
|
||||
RequestedSecurityLevelToString(security_level_), status.ToInt());
|
||||
LOGE("Failed to get keybox data: security_level = %s, status = %s",
|
||||
RequestedSecurityLevelToString(security_level_),
|
||||
status.ToString().c_str());
|
||||
return false;
|
||||
}
|
||||
if (!ExtractSystemIdFromKeyboxData(key_data, system_id)) {
|
||||
@@ -138,8 +157,9 @@ bool SystemIdExtractor::ExtractSystemIdProv30(uint32_t* system_id) {
|
||||
const CdmResponseType status =
|
||||
crypto_session_->GetTokenFromOemCert(security_level_, &oem_cert);
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("Failed to get OEM certificate: security_level = %s, status = %d",
|
||||
RequestedSecurityLevelToString(security_level_), status.ToInt());
|
||||
LOGE("Failed to get OEM certificate: security_level = %s, status = %s",
|
||||
RequestedSecurityLevelToString(security_level_),
|
||||
status.ToString().c_str());
|
||||
return false;
|
||||
}
|
||||
if (!ExtractSystemIdFromOemCert(oem_cert, system_id)) {
|
||||
@@ -152,7 +172,7 @@ bool SystemIdExtractor::ExtractSystemIdProv30(uint32_t* system_id) {
|
||||
bool SystemIdExtractor::ExtractSystemIdProv40(uint32_t* system_id) {
|
||||
const CdmSecurityLevel security_level =
|
||||
crypto_session_->GetSecurityLevel(security_level_);
|
||||
if (!IsSupportedSecurityLevel(security_level)) {
|
||||
if (!IsSupportedCdmSecurityLevel(security_level)) {
|
||||
LOGE("Unsupported security level: %s",
|
||||
CdmSecurityLevelToString(security_level));
|
||||
return false;
|
||||
@@ -160,7 +180,7 @@ bool SystemIdExtractor::ExtractSystemIdProv40(uint32_t* system_id) {
|
||||
DeviceFiles real_device_files(fs_);
|
||||
// Mock DeviceFiles for testing.
|
||||
DeviceFiles& device_files =
|
||||
(test_device_files_ ? *test_device_files_ : real_device_files);
|
||||
(test_device_files_ != nullptr ? *test_device_files_ : real_device_files);
|
||||
if (!device_files.Init(security_level)) {
|
||||
LOGE("Failed to initialize device files: security_level = %s",
|
||||
CdmSecurityLevelToString(security_level));
|
||||
@@ -190,4 +210,48 @@ bool SystemIdExtractor::ExtractSystemIdProv40(uint32_t* system_id) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SystemIdExtractor::VerifySecurityLevelExpectations() {
|
||||
if (!IsSupportedRequestedSecurityLevel(security_level_)) {
|
||||
LOGE("Unsupported requested extractor security level: %d",
|
||||
static_cast<int>(security_level_));
|
||||
return false;
|
||||
}
|
||||
if (!crypto_session_->IsOpen()) {
|
||||
// Any other issues with the security level should be caught and
|
||||
// handled by the respective extractor methods.
|
||||
return true;
|
||||
}
|
||||
// The SystemIdExtractor is intended to work with unopened
|
||||
// CryptoSessions, but does not restrict this.
|
||||
// If the crypto session is open, it is already tied to a
|
||||
// security level; for the extractor work as expected the
|
||||
// session's security level must be the same as extractor's
|
||||
// requested security level.
|
||||
const CdmSecurityLevel session_security_level =
|
||||
crypto_session_->GetSecurityLevel();
|
||||
if (!IsSupportedCdmSecurityLevel(session_security_level)) {
|
||||
LOGE("Failed to get session security level: %s",
|
||||
CdmSecurityLevelToString(session_security_level));
|
||||
return false;
|
||||
}
|
||||
const CdmSecurityLevel extractor_security_level =
|
||||
crypto_session_->GetSecurityLevel(security_level_);
|
||||
if (!IsSupportedCdmSecurityLevel(extractor_security_level)) {
|
||||
LOGE("Failed to get extractor security level: %s",
|
||||
CdmSecurityLevelToString(extractor_security_level));
|
||||
return false;
|
||||
}
|
||||
if (session_security_level != extractor_security_level) {
|
||||
LOGE(
|
||||
"Extractor and session security levels do not match: "
|
||||
"session_security_level = %s, extractor_security_level = %s, "
|
||||
"requested_security_level = %s",
|
||||
CdmSecurityLevelToString(session_security_level),
|
||||
CdmSecurityLevelToString(extractor_security_level),
|
||||
RequestedSecurityLevelToString(security_level_));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -270,11 +270,13 @@ class MockCryptoSession : public CryptoSession {
|
||||
MockCryptoSession(metrics::CryptoMetrics* metrics) : CryptoSession(metrics) {}
|
||||
// ~MockCryptoSession() override {}
|
||||
|
||||
bool IsOpen() override { return true; }
|
||||
CdmSecurityLevel GetSecurityLevel() override { return kSecurityLevelL1; }
|
||||
bool IsOpen() override { return is_open_; }
|
||||
CdmSecurityLevel GetSecurityLevel() override {
|
||||
return is_open_ ? open_security_level_ : kSecurityLevelUninitialized;
|
||||
}
|
||||
CdmSecurityLevel GetSecurityLevel(
|
||||
RequestedSecurityLevel security_level) override {
|
||||
return security_level == kLevelDefault ? kSecurityLevelL1
|
||||
return security_level == kLevelDefault ? default_security_level_
|
||||
: kSecurityLevelL3;
|
||||
}
|
||||
|
||||
@@ -291,6 +293,16 @@ class MockCryptoSession : public CryptoSession {
|
||||
(RequestedSecurityLevel, std::string*), (override));
|
||||
MOCK_METHOD(CdmResponseType, GetTokenFromOemCert,
|
||||
(RequestedSecurityLevel, std::string*), (override));
|
||||
|
||||
// These default values should represent good values of a
|
||||
// CryptoSession used for system ID extractions.
|
||||
// Test cases should modify them if needing to test edge cases.
|
||||
|
||||
bool is_open_ = false;
|
||||
// Security level which is returned if the session is opened.
|
||||
CdmSecurityLevel open_security_level_ = kSecurityLevelL1;
|
||||
// Security level of the underlying default OEMCrypto engine.
|
||||
CdmSecurityLevel default_security_level_ = kSecurityLevelL1;
|
||||
};
|
||||
|
||||
class MockDeviceFiles : public DeviceFiles {
|
||||
@@ -328,13 +340,27 @@ class SystemIdExtractorTest : public WvCdmTestBase {
|
||||
return extractor;
|
||||
}
|
||||
|
||||
void ExpectProvisioningType(CdmClientTokenType type) {
|
||||
EXPECT_CALL(*crypto_session_, GetCachedSystemId).WillOnce(Return(false));
|
||||
void ExpectProvisioningType(CdmClientTokenType type, bool is_open = false) {
|
||||
crypto_session_->is_open_ = is_open;
|
||||
if (is_open) {
|
||||
// Extractor should only call GetCachedSystemId if session
|
||||
// is opened.
|
||||
EXPECT_CALL(*crypto_session_, GetCachedSystemId).WillOnce(Return(false));
|
||||
} else {
|
||||
EXPECT_CALL(*crypto_session_, GetCachedSystemId).Times(0);
|
||||
}
|
||||
|
||||
EXPECT_CALL(*crypto_session_, GetProvisioningMethod(_, NotNull()))
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<1>(type), Return(CdmResponseType(NO_ERROR))));
|
||||
}
|
||||
|
||||
void ExpectFromCached(uint32_t system_id) {
|
||||
crypto_session_->is_open_ = true;
|
||||
EXPECT_CALL(*crypto_session_, GetCachedSystemId(NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(system_id), Return(true)));
|
||||
}
|
||||
|
||||
void ExpectSet(uint32_t system_id) {
|
||||
EXPECT_CALL(*crypto_session_, SetSystemId(system_id));
|
||||
}
|
||||
@@ -361,8 +387,7 @@ TEST_F(SystemIdExtractorTest, ExtractSystemIdFromKeyboxData) {
|
||||
|
||||
TEST_F(SystemIdExtractorTest, CachedSystemId) {
|
||||
const uint32_t kCachedSystemId = 1234;
|
||||
EXPECT_CALL(*crypto_session_, GetCachedSystemId(NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kCachedSystemId), Return(true)));
|
||||
ExpectFromCached(kCachedSystemId);
|
||||
auto extractor = CreateExtractor(kLevelDefault);
|
||||
ASSERT_TRUE(extractor);
|
||||
uint32_t system_id;
|
||||
@@ -372,6 +397,7 @@ TEST_F(SystemIdExtractorTest, CachedSystemId) {
|
||||
|
||||
TEST_F(SystemIdExtractorTest, SetSystemIdMetrics) {
|
||||
const uint32_t kSystemId = 4321;
|
||||
crypto_session_->is_open_ = true; // Must be open to set system ID.
|
||||
crypto_session_->SetSystemIdBase(kSystemId);
|
||||
drm_metrics::WvCdmMetrics::CryptoMetrics metrics_proto;
|
||||
crypto_metrics_.Serialize(&metrics_proto);
|
||||
@@ -380,8 +406,54 @@ TEST_F(SystemIdExtractorTest, SetSystemIdMetrics) {
|
||||
EXPECT_EQ(recorded_system_id, kSystemId);
|
||||
}
|
||||
|
||||
TEST_F(SystemIdExtractorTest,
|
||||
BadSecurityLevelExpectations_InvalidRequestedSecurityLevel) {
|
||||
// Extractor caller is using an invalid requested security level.
|
||||
const RequestedSecurityLevel kBadRequestedSecurityLevel =
|
||||
static_cast<RequestedSecurityLevel>(9999);
|
||||
auto extractor = CreateExtractor(kBadRequestedSecurityLevel);
|
||||
uint32_t system_id;
|
||||
EXPECT_FALSE(extractor->ExtractSystemId(&system_id));
|
||||
}
|
||||
|
||||
TEST_F(SystemIdExtractorTest,
|
||||
BadSecurityLevelExpectations_UnexpectedSessionSecurityLevel) {
|
||||
// CryptoSession is returning an unexpected result for its security
|
||||
// level.
|
||||
crypto_session_->is_open_ = true;
|
||||
crypto_session_->open_security_level_ = kSecurityLevelUnknown;
|
||||
auto extractor = CreateExtractor(kLevelDefault);
|
||||
uint32_t system_id;
|
||||
EXPECT_FALSE(extractor->ExtractSystemId(&system_id));
|
||||
}
|
||||
|
||||
TEST_F(SystemIdExtractorTest,
|
||||
BadSecurityLevelExpectations_UnexpectedExtractorSecurityLevel) {
|
||||
// OEMCrypto (via session-less CryptoSession) is returning an
|
||||
// unexpected result for the default security level.
|
||||
crypto_session_->is_open_ = true;
|
||||
crypto_session_->default_security_level_ = kSecurityLevelUnknown;
|
||||
auto extractor = CreateExtractor(kLevelDefault);
|
||||
uint32_t system_id;
|
||||
EXPECT_FALSE(extractor->ExtractSystemId(&system_id));
|
||||
}
|
||||
|
||||
TEST_F(SystemIdExtractorTest,
|
||||
BadSecurityLevelExpectations_MismatchedSessionSecurityLevel) {
|
||||
// CryptoSession and Extractor are different security levels.
|
||||
crypto_session_->is_open_ = true;
|
||||
// Case 1: Session L3, extractor L1
|
||||
crypto_session_->open_security_level_ = kSecurityLevelL3;
|
||||
auto extractor = CreateExtractor(kLevelDefault);
|
||||
uint32_t system_id;
|
||||
EXPECT_FALSE(extractor->ExtractSystemId(&system_id));
|
||||
// Case 2: Session L1, extractor L3
|
||||
crypto_session_->open_security_level_ = kSecurityLevelL1;
|
||||
extractor = CreateExtractor(kLevel3);
|
||||
EXPECT_FALSE(extractor->ExtractSystemId(&system_id));
|
||||
}
|
||||
|
||||
TEST_F(SystemIdExtractorTest, GetProvisioningMethod_Failed) {
|
||||
EXPECT_CALL(*crypto_session_, GetCachedSystemId).WillOnce(Return(false));
|
||||
EXPECT_CALL(*crypto_session_, GetProvisioningMethod(_, NotNull()))
|
||||
.WillOnce(Return(CdmResponseType(UNKNOWN_ERROR)));
|
||||
auto extractor = CreateExtractor(kLevelDefault);
|
||||
@@ -420,6 +492,19 @@ TEST_F(SystemIdExtractorTest, KeyboxDevice_Success) {
|
||||
EXPECT_EQ(system_id, kKeyboxSystemId);
|
||||
}
|
||||
|
||||
TEST_F(SystemIdExtractorTest, KeyboxDevice_Success_WithCallToGetCached) {
|
||||
ExpectProvisioningType(kClientTokenKeybox, /* is_open = */ true);
|
||||
EXPECT_CALL(*crypto_session_, GetTokenFromKeybox(kLevelDefault, NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(kKeyboxDataStr),
|
||||
Return(CdmResponseType(NO_ERROR))));
|
||||
ExpectSet(kKeyboxSystemId);
|
||||
auto extractor = CreateExtractor(kLevelDefault);
|
||||
ASSERT_TRUE(extractor);
|
||||
uint32_t system_id;
|
||||
EXPECT_TRUE(extractor->ExtractSystemId(&system_id));
|
||||
EXPECT_EQ(system_id, kKeyboxSystemId);
|
||||
}
|
||||
|
||||
TEST_F(SystemIdExtractorTest, KeyboxDevice_NeedsOtaKeyboxProvisioning) {
|
||||
ExpectProvisioningType(kClientTokenKeybox);
|
||||
EXPECT_CALL(*crypto_session_, GetTokenFromKeybox(kLevelDefault, NotNull()))
|
||||
|
||||
@@ -1 +1 @@
|
||||
AV1A.240629.001
|
||||
AV1A.240704.001
|
||||
|
||||
Reference in New Issue
Block a user