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:
Android Build Coastguard Worker
2024-07-04 09:19:05 +00:00
4 changed files with 208 additions and 24 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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()))

View File

@@ -1 +1 @@
AV1A.240629.001
AV1A.240704.001