Verify DRM certificate validity
[ Merge of http://go/wvgerrit/120123 ] DRM certificate creation and expiration times are now validated. * New DRM (default) certificates will have an expiration time specified by the provisioning service. When stored, the client will include the time the certificate was received. This allows for expiration calculation to occur when client and provisioning service clocks are out of sync. When read out, creation, expiration and acquisition times are validated. The certificate is checked for expiry by making sure that the time at the client since the license was acquired is not greater than the expiration period. The time information stored at the client may be tampered with. The license service will perform an expiration check and reject the license request if tampered with. The expiration time may be set to never expires/unlimited. This is not a valid value for creation or acquisition time. * Pre-existing (legacy) certificates from upgrading devices will not have an expiration time set by the provisioning service. Instead the client will calculate an expiration time 6 months with + or - a random two month period in the future. This is stored along with the certificate. When read out, if no expiration time has been set by the client, one will be calculated and written out. The certificate will be declared as valid. If a client calculated expiration time is present, the certificate will be validated. In case of tampering, the license service can reject license requests and force reprovisioning when appropriate. * ATSC certificates will continue to not have an expiration time. No additional validation is required. Other changes for non-ATSC licenses involve managing both default and legacy certificate co-existance. When checking for DRM certificates, the default certificate is attempted first. This is followed by a check for the legacy certificate, if the default certificate is not present. Bug: 169740403 Test: WV unit/integration tests DeviceFilesTest.StoreCertificateInvalidParams DeviceFilesTest.RetrieveAtscCertificate DeviceFilesTest.RetrieveAtscCertificateNotFound DeviceFilesTest.RetrieveCertificateInvalidParams DeviceFilesTest.RetrieveLegacyCertificateWithoutExpirationTime DeviceFilesTest.RetrieveLegacyCertificateWithClientExpirationTime DeviceFilesTest.RetrieveLegacyExpiredCertificateByClientExpirationTime DeviceFilesTest.RetrieveLegacyCertificateInvalidClientExpirationTime DeviceFilesTest.RetrieveCertificateWithoutKeyType DeviceFilesTest.RetrieveDefaultCertificate DeviceFilesTest.RetrieveDefaultCertificateNeverExpires DeviceFilesTest.HasCertificateAtsc DeviceFilesTest.HasCertificateDefault DeviceFilesTest.HasCertificateLegacy DeviceFilesTest.HasCertificateNone CertificateTest.StoreCertificateTest.DefaultAndLegacy/* CertificateTest.RetrieveLegacyCertificateTest.ErrorScenarios/* CertificateTest.RetrieveDefaultCertificateTest.ErrorScenarios/* Change-Id: I7dbec7555fbd493c1ec61c6bb5d9428a2405b1fd
This commit is contained in:
@@ -116,8 +116,10 @@ class MockDeviceFiles : public DeviceFiles {
|
||||
MockDeviceFiles() : DeviceFiles(nullptr) {}
|
||||
|
||||
MOCK_METHOD1(Init, bool(CdmSecurityLevel));
|
||||
MOCK_METHOD5(RetrieveCertificate, bool(bool, std::string*, CryptoWrappedKey*,
|
||||
std::string*, uint32_t*));
|
||||
MOCK_METHOD5(RetrieveCertificate,
|
||||
DeviceFiles::CertificateState(bool, std::string*,
|
||||
CryptoWrappedKey*, std::string*,
|
||||
uint32_t*));
|
||||
};
|
||||
|
||||
class MockUsageTableHeader : public UsageTableHeader {
|
||||
@@ -221,7 +223,7 @@ TEST_F(CdmSessionTest, InitWithBuiltInCertificate) {
|
||||
EXPECT_CALL(*file_handle_,
|
||||
RetrieveCertificate(false, NotNull(), NotNull(), NotNull(), _))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey),
|
||||
Return(true)));
|
||||
Return(DeviceFiles::kCertificateValid)));
|
||||
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(kWrappedKey))
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(NO_ERROR));
|
||||
@@ -249,7 +251,7 @@ TEST_F(CdmSessionTest, InitWithCertificate) {
|
||||
EXPECT_CALL(*file_handle_,
|
||||
RetrieveCertificate(false, NotNull(), NotNull(), NotNull(), _))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey),
|
||||
Return(true)));
|
||||
Return(DeviceFiles::kCertificateValid)));
|
||||
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(kWrappedKey))
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(NO_ERROR));
|
||||
@@ -276,7 +278,7 @@ TEST_F(CdmSessionTest, ReInitFail) {
|
||||
EXPECT_CALL(*file_handle_,
|
||||
RetrieveCertificate(false, NotNull(), NotNull(), NotNull(), _))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey),
|
||||
Return(true)));
|
||||
Return(DeviceFiles::kCertificateValid)));
|
||||
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(kWrappedKey))
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(NO_ERROR));
|
||||
@@ -310,7 +312,7 @@ TEST_F(CdmSessionTest, InitNeedsProvisioning) {
|
||||
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
|
||||
EXPECT_CALL(*file_handle_,
|
||||
RetrieveCertificate(false, NotNull(), NotNull(), NotNull(), _))
|
||||
.WillOnce(Return(false));
|
||||
.WillOnce(Return(DeviceFiles::kCertificateInvalid));
|
||||
|
||||
ASSERT_EQ(NEED_PROVISIONING, cdm_session_->Init(nullptr));
|
||||
}
|
||||
@@ -331,7 +333,7 @@ TEST_F(CdmSessionTest, UpdateUsageEntry) {
|
||||
EXPECT_CALL(*file_handle_,
|
||||
RetrieveCertificate(false, NotNull(), NotNull(), NotNull(), _))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey),
|
||||
Return(true)));
|
||||
Return(DeviceFiles::kCertificateValid)));
|
||||
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(kWrappedKey))
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(NO_ERROR));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -168,28 +168,28 @@ const size_t kUsageInfoFileArraySize = ArraySize(kUsageInfoFileArray);
|
||||
std::vector<std::string> kUsageInfoFileList;
|
||||
|
||||
const DeviceFiles::CdmUsageData kCdmUsageData1 = {
|
||||
/* provider_session_token = */ "provider_session_token_1",
|
||||
/* license_request = */ "license_request_1",
|
||||
/* license = */ "license_1",
|
||||
/* key_set_id = */ "key_set_id_1",
|
||||
/* usage_entry = */ "usage_entry_1",
|
||||
/* usage_entry_number = */ 0,
|
||||
/* provider_session_token = */ "provider_session_token_1",
|
||||
/* license_request = */ "license_request_1",
|
||||
/* license = */ "license_1",
|
||||
/* key_set_id = */ "key_set_id_1",
|
||||
/* usage_entry = */ "usage_entry_1",
|
||||
/* usage_entry_number = */ 0,
|
||||
};
|
||||
const DeviceFiles::CdmUsageData kCdmUsageData2 = {
|
||||
/* provider_session_token = */ "provider_session_token_2",
|
||||
/* license_request = */ "license_request_2",
|
||||
/* license = */ "license_2",
|
||||
/* key_set_id = */ "key_set_id_2",
|
||||
/* usage_entry = */ "usage_entry_2",
|
||||
/* usage_entry_number = */ 0,
|
||||
/* provider_session_token = */ "provider_session_token_2",
|
||||
/* license_request = */ "license_request_2",
|
||||
/* license = */ "license_2",
|
||||
/* key_set_id = */ "key_set_id_2",
|
||||
/* usage_entry = */ "usage_entry_2",
|
||||
/* usage_entry_number = */ 0,
|
||||
};
|
||||
const DeviceFiles::CdmUsageData kCdmUsageData3 = {
|
||||
/* provider_session_token = */ "provider_session_token_3",
|
||||
/* license_request = */ "license_request_3",
|
||||
/* license = */ "license_3",
|
||||
/* key_set_id = */ "key_set_id_3",
|
||||
/* usage_entry = */ "usage_entry_3",
|
||||
/* usage_entry_number = */ 0,
|
||||
/* provider_session_token = */ "provider_session_token_3",
|
||||
/* license_request = */ "license_request_3",
|
||||
/* license = */ "license_3",
|
||||
/* key_set_id = */ "key_set_id_3",
|
||||
/* usage_entry = */ "usage_entry_3",
|
||||
/* usage_entry_number = */ 0,
|
||||
};
|
||||
const std::vector<DeviceFiles::CdmUsageData> kEmptyUsageInfoUsageDataList;
|
||||
|
||||
@@ -366,8 +366,8 @@ void InitVectorConstants() {
|
||||
}
|
||||
}
|
||||
|
||||
void ToVector(std::vector<CdmUsageEntryInfo>& vec,
|
||||
const CdmUsageEntryInfo* arr, size_t total_size) {
|
||||
void ToVector(std::vector<CdmUsageEntryInfo>& vec, const CdmUsageEntryInfo* arr,
|
||||
size_t total_size) {
|
||||
size_t max = total_size / sizeof(CdmUsageEntryInfo);
|
||||
vec.clear();
|
||||
for (size_t i = 0; i < max; i++) {
|
||||
@@ -415,8 +415,7 @@ class MockDeviceFiles : public DeviceFiles {
|
||||
const std::string&, const CdmUsageEntry&, uint32_t));
|
||||
MOCK_METHOD2(RetrieveUsageInfo,
|
||||
bool(const std::string&, std::vector<CdmUsageData>*));
|
||||
MOCK_METHOD1(ListLicenses,
|
||||
bool(std::vector<std::string>* key_set_ids));
|
||||
MOCK_METHOD1(ListLicenses, bool(std::vector<std::string>* key_set_ids));
|
||||
MOCK_METHOD1(ListUsageInfoFiles,
|
||||
bool(std::vector<std::string>* usage_info_files));
|
||||
|
||||
@@ -468,34 +467,31 @@ class MockCryptoSession : public TestCryptoSession {
|
||||
// Partial mock of the UsageTableHeader. This is to test when dependency
|
||||
// exist on internal methods which would require complex expectations
|
||||
class MockUsageTableHeader : public UsageTableHeader {
|
||||
public:
|
||||
MockUsageTableHeader() : UsageTableHeader() {}
|
||||
MOCK_METHOD4(InvalidateEntry, CdmResponseType(uint32_t, bool, DeviceFiles*,
|
||||
metrics::CryptoMetrics*));
|
||||
MOCK_METHOD6(AddEntry,
|
||||
CdmResponseType(CryptoSession*, bool, const CdmKeySetId&,
|
||||
const std::string&, const CdmKeyResponse&,
|
||||
uint32_t*));
|
||||
public:
|
||||
MockUsageTableHeader() : UsageTableHeader() {}
|
||||
MOCK_METHOD4(InvalidateEntry, CdmResponseType(uint32_t, bool, DeviceFiles*,
|
||||
metrics::CryptoMetrics*));
|
||||
MOCK_METHOD6(AddEntry, CdmResponseType(CryptoSession*, bool,
|
||||
const CdmKeySetId&, const std::string&,
|
||||
const CdmKeyResponse&, uint32_t*));
|
||||
|
||||
CdmResponseType SuperAddEntry(CryptoSession* crypto_session,
|
||||
bool persistent_license,
|
||||
const CdmKeySetId& key_set_id,
|
||||
const std::string& usage_info_filename,
|
||||
const CdmKeyResponse& license_message,
|
||||
uint32_t* usage_entry_number) {
|
||||
return UsageTableHeader::AddEntry(crypto_session, persistent_license,
|
||||
key_set_id, usage_info_filename,
|
||||
license_message, usage_entry_number);
|
||||
}
|
||||
CdmResponseType SuperAddEntry(CryptoSession* crypto_session,
|
||||
bool persistent_license,
|
||||
const CdmKeySetId& key_set_id,
|
||||
const std::string& usage_info_filename,
|
||||
const CdmKeyResponse& license_message,
|
||||
uint32_t* usage_entry_number) {
|
||||
return UsageTableHeader::AddEntry(crypto_session, persistent_license,
|
||||
key_set_id, usage_info_filename,
|
||||
license_message, usage_entry_number);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class UsageTableHeaderTest : public WvCdmTestBase {
|
||||
public:
|
||||
static void SetUpTestCase() {
|
||||
InitVectorConstants();
|
||||
}
|
||||
static void SetUpTestCase() { InitVectorConstants(); }
|
||||
|
||||
// Useful when UsageTableHeader is mocked
|
||||
void InvalidateEntry(uint32_t usage_entry_number, bool, DeviceFiles*,
|
||||
@@ -591,10 +587,7 @@ class UsageTableHeaderInitializationTest
|
||||
: public UsageTableHeaderTest,
|
||||
public ::testing::WithParamInterface<CdmSecurityLevel> {
|
||||
public:
|
||||
static void SetUpTestCase() {
|
||||
InitVectorConstants();
|
||||
}
|
||||
|
||||
static void SetUpTestCase() { InitVectorConstants(); }
|
||||
};
|
||||
|
||||
TEST_P(UsageTableHeaderInitializationTest, CreateUsageTableHeader) {
|
||||
@@ -629,7 +622,7 @@ TEST_P(UsageTableHeaderInitializationTest, Upgrade_UnableToRetrieveLicenses) {
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<1>(kEmptyUsageTableHeader), Return(NO_ERROR)));
|
||||
// TODO: Why not being called?
|
||||
//EXPECT_CALL(*device_files_, DeleteAllLicenses()).WillOnce(Return(true));
|
||||
// EXPECT_CALL(*device_files_, DeleteAllLicenses()).WillOnce(Return(true));
|
||||
EXPECT_CALL(*device_files_, StoreUsageTableInfo(kEmptyUsageTableHeader,
|
||||
kEmptyUsageEntryInfoVector))
|
||||
.WillOnce(Return(true));
|
||||
@@ -763,8 +756,8 @@ TEST_P(UsageTableHeaderInitializationTest,
|
||||
|
||||
const SecurityLevel security_level =
|
||||
(GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault;
|
||||
EXPECT_CALL(*crypto_session_,
|
||||
Open(security_level)).WillOnce(Return(NO_ERROR));
|
||||
EXPECT_CALL(*crypto_session_, Open(security_level))
|
||||
.WillOnce(Return(NO_ERROR));
|
||||
EXPECT_CALL(*crypto_session_,
|
||||
LoadUsageTableHeader(security_level, kUsageTableHeader))
|
||||
.WillOnce(Return(NO_ERROR));
|
||||
@@ -847,8 +840,8 @@ TEST_P(UsageTableHeaderInitializationTest,
|
||||
const uint32_t expect_usage_entry_number =
|
||||
kOverFullUsageEntryInfoVector.size();
|
||||
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(expect_usage_entry_number),
|
||||
Return(NO_ERROR)));
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR)));
|
||||
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
|
||||
|
||||
Reference in New Issue
Block a user