From efea2ddba414e54461617769d9bfbf973beb1762 Mon Sep 17 00:00:00 2001 From: "John \"Juce\" Bruce" Date: Thu, 9 Apr 2015 19:07:51 -0700 Subject: [PATCH] Allow Unprovisioning of Origins (This is a merge of http://go/wvgerrit/14051) Adds support for passing a special provisioning response ("delete") to the provisioning API in order to unprovision the current origin. Note that the origin MUST be set or else this will fail. The existing, system-only unprovisionDevice() method is unaffected. Bug: 12247651 Change-Id: I16d296397d8e9e73c8f43e36c86838873318a398 --- libwvdrmengine/include/WVErrors.h | 3 +- libwvdrmengine/mediadrm/include/WVDrmPlugin.h | 2 + libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp | 65 ++++++++++------- .../mediadrm/test/WVDrmPlugin_test.cpp | 72 +++++++++++++++++++ 4 files changed, 114 insertions(+), 28 deletions(-) diff --git a/libwvdrmengine/include/WVErrors.h b/libwvdrmengine/include/WVErrors.h index e23e5176..b05b2720 100644 --- a/libwvdrmengine/include/WVErrors.h +++ b/libwvdrmengine/include/WVErrors.h @@ -20,7 +20,8 @@ enum { kErrorSessionIsOpen = ERROR_DRM_VENDOR_MIN + 4, kErrorTooManySessions = ERROR_DRM_VENDOR_MIN + 5, kErrorInvalidKey = ERROR_DRM_VENDOR_MIN + 6, - kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 6, + kErrorNoOriginSpecified = ERROR_DRM_VENDOR_MIN + 7, + kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 7, // Used by crypto test mode kErrorTestMode = ERROR_DRM_VENDOR_MAX, diff --git a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h index ec340c83..9d6d7da0 100644 --- a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h +++ b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h @@ -276,6 +276,8 @@ class WVDrmPlugin : public android::DrmPlugin, bool InitDataResemblesPSSH(const Vector& initData); + status_t unprovision(const std::string& origin); + const char* determineOrigin() const; }; diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index bd50218d..7365d1f5 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -24,6 +24,7 @@ namespace { static const char* const kEnable = "enable"; static const char* const kDisable = "disable"; static const std::string kPsshTag = "pssh"; + static const char* const kSpecialUnprovisionResponse = "unprovision"; } namespace wvdrm { @@ -385,38 +386,35 @@ status_t WVDrmPlugin::provideProvisionResponse( Vector& certificate, Vector& wrapped_key) { CdmProvisioningResponse cdmResponse(response.begin(), response.end()); - string cdmCertificate; - string cdmWrappedKey; - CdmResponseType res = mCDM->HandleProvisioningResponse(determineOrigin(), - cdmResponse, - &cdmCertificate, - &cdmWrappedKey); - if (isCdmResponseTypeSuccess(res)) { - certificate.clear(); - certificate.appendArray( - reinterpret_cast(cdmCertificate.data()), - cdmCertificate.size()); + if (cdmResponse == kSpecialUnprovisionResponse) { + const std::string origin = determineOrigin(); + if (origin == EMPTY_ORIGIN) return kErrorNoOriginSpecified; + return unprovision(origin); + } else { + string cdmCertificate; + string cdmWrappedKey; + CdmResponseType res = mCDM->HandleProvisioningResponse(determineOrigin(), + cdmResponse, + &cdmCertificate, + &cdmWrappedKey); + if (isCdmResponseTypeSuccess(res)) { + certificate.clear(); + certificate.appendArray( + reinterpret_cast(cdmCertificate.data()), + cdmCertificate.size()); - wrapped_key.clear(); - wrapped_key.appendArray( - reinterpret_cast(cdmWrappedKey.data()), - cdmWrappedKey.size()); + wrapped_key.clear(); + wrapped_key.appendArray( + reinterpret_cast(cdmWrappedKey.data()), + cdmWrappedKey.size()); + } + + return mapCdmResponseType(res); } - - return mapCdmResponseType(res); } status_t WVDrmPlugin::unprovisionDevice() { - CdmResponseType res1 = mCDM->Unprovision(kSecurityLevelL1, determineOrigin()); - CdmResponseType res3 = mCDM->Unprovision(kSecurityLevelL3, determineOrigin()); - if (!isCdmResponseTypeSuccess(res1)) - { - return mapCdmResponseType(res1); - } - else - { - return mapCdmResponseType(res3); - } + return unprovision(EMPTY_ORIGIN); } status_t WVDrmPlugin::getSecureStop(const Vector& ssid, @@ -1027,6 +1025,19 @@ bool WVDrmPlugin::InitDataResemblesPSSH(const Vector& initData) { return id == kPsshTag; } +status_t WVDrmPlugin::unprovision(const std::string& origin) { + CdmResponseType res1 = mCDM->Unprovision(kSecurityLevelL1, origin); + CdmResponseType res3 = mCDM->Unprovision(kSecurityLevelL3, origin); + if (!isCdmResponseTypeSuccess(res1)) + { + return mapCdmResponseType(res1); + } + else + { + return mapCdmResponseType(res3); + } +} + const char* WVDrmPlugin::determineOrigin() const { return mOrigin.empty() ? EMPTY_ORIGIN : mOrigin.c_str(); } diff --git a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp index 3eeb037b..629a7f6c 100644 --- a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp +++ b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp @@ -29,6 +29,9 @@ namespace { const String8 kEmptyString; const String8 kOrigin("widevine.com"); const String8 kAppId("com.unittest.mock.app.id"); +const uint8_t* const kUnprovisionResponse = + reinterpret_cast("unprovision"); +const size_t kUnprovisionResponseSize = 11; } class MockCDM : public WvContentDecryptionModule { @@ -627,6 +630,75 @@ TEST_F(WVDrmPluginTest, MuxesUnprovisioningErrors) { ASSERT_NE(OK, res); } +TEST_F(WVDrmPluginTest, UnprovisionsOrigin) { + StrictMock cdm; + StrictMock crypto; + WVDrmPlugin plugin(&cdm, &crypto); + + Vector cert; + Vector key; + Vector specialResponse; + specialResponse.appendArray(kUnprovisionResponse, kUnprovisionResponseSize); + + EXPECT_CALL(cdm, Unprovision(kSecurityLevelL1, StrEq(kOrigin.string()))) + .Times(1); + EXPECT_CALL(cdm, Unprovision(kSecurityLevelL3, StrEq(kOrigin.string()))) + .Times(1); + + status_t res = plugin.setPropertyString(String8("origin"), kOrigin); + ASSERT_EQ(OK, res); + res = plugin.provideProvisionResponse(specialResponse, cert, key); + EXPECT_EQ(OK, res); +} + +TEST_F(WVDrmPluginTest, WillNotUnprovisionWithoutOrigin) { + StrictMock cdm; + StrictMock crypto; + WVDrmPlugin plugin(&cdm, &crypto); + + Vector cert; + Vector key; + Vector specialResponse; + specialResponse.appendArray(kUnprovisionResponse, kUnprovisionResponseSize); + + EXPECT_CALL(cdm, Unprovision(_, _)) + .Times(0); + + status_t res = plugin.provideProvisionResponse(specialResponse, cert, key); + EXPECT_NE(OK, res); +} + +TEST_F(WVDrmPluginTest, MuxesOriginUnprovisioningErrors) { + StrictMock cdm; + StrictMock crypto; + WVDrmPlugin plugin(&cdm, &crypto); + + Vector cert; + Vector key; + Vector specialResponse; + specialResponse.appendArray(kUnprovisionResponse, kUnprovisionResponseSize); + + // Tests that both Unprovisions are called even if one fails. Also tests that + // no matter which fails, the function always propagates the error. + EXPECT_CALL(cdm, Unprovision(kSecurityLevelL1, StrEq(kOrigin.string()))) + .WillOnce(Return(wvcdm::UNKNOWN_ERROR)) + .WillOnce(Return(wvcdm::NO_ERROR)) + .WillOnce(Return(wvcdm::UNKNOWN_ERROR)); + EXPECT_CALL(cdm, Unprovision(kSecurityLevelL3, StrEq(kOrigin.string()))) + .WillOnce(Return(wvcdm::NO_ERROR)) + .WillOnce(Return(wvcdm::UNKNOWN_ERROR)) + .WillOnce(Return(wvcdm::UNKNOWN_ERROR)); + + status_t res = plugin.setPropertyString(String8("origin"), kOrigin); + ASSERT_EQ(OK, res); + res = plugin.provideProvisionResponse(specialResponse, cert, key); + EXPECT_NE(OK, res); + res = plugin.provideProvisionResponse(specialResponse, cert, key); + EXPECT_NE(OK, res); + res = plugin.provideProvisionResponse(specialResponse, cert, key); + EXPECT_NE(OK, res); +} + TEST_F(WVDrmPluginTest, GetsSecureStops) { StrictMock cdm; StrictMock crypto;