Support CAST V2 authentication
bug: 12702350 Squashed commit of these CLs from the widevine cdm repo: Cast V2 cdm support https://widevine-internal-review.googlesource.com/#/c/9190/ Add CASTv2 Support to DrmPlugin https://widevine-internal-review.googlesource.com/#/c/9228/ Test for CastV2 authentication APIs https://widevine-internal-review.googlesource.com/9550 Change-Id: I6d66bc1bbd653db5542c68687b30b441dd20617f
This commit is contained in:
@@ -80,11 +80,15 @@ class CdmEngine : public TimerHandler {
|
|||||||
|
|
||||||
// Provisioning related methods
|
// Provisioning related methods
|
||||||
virtual CdmResponseType GetProvisioningRequest(
|
virtual CdmResponseType GetProvisioningRequest(
|
||||||
|
CdmCertificateType cert_type,
|
||||||
|
const std::string& cert_authority,
|
||||||
CdmProvisioningRequest* request,
|
CdmProvisioningRequest* request,
|
||||||
std::string* default_url);
|
std::string* default_url);
|
||||||
|
|
||||||
virtual CdmResponseType HandleProvisioningResponse(
|
virtual CdmResponseType HandleProvisioningResponse(
|
||||||
CdmProvisioningResponse& response);
|
CdmProvisioningResponse& response,
|
||||||
|
std::string* cert,
|
||||||
|
std::string* wrapped_key);
|
||||||
|
|
||||||
// Secure stop related methods
|
// Secure stop related methods
|
||||||
virtual CdmResponseType GetSecureStops(CdmSecureStops* secure_stops);
|
virtual CdmResponseType GetSecureStops(CdmSecureStops* secure_stops);
|
||||||
|
|||||||
@@ -13,14 +13,18 @@ class CdmSession;
|
|||||||
|
|
||||||
class CertificateProvisioning {
|
class CertificateProvisioning {
|
||||||
public:
|
public:
|
||||||
CertificateProvisioning() {};
|
CertificateProvisioning() : cert_type_(kCertificateWidevine) {};
|
||||||
~CertificateProvisioning() {};
|
~CertificateProvisioning() {};
|
||||||
|
|
||||||
// Provisioning related methods
|
// Provisioning related methods
|
||||||
CdmResponseType GetProvisioningRequest(SecurityLevel requested_security_level,
|
CdmResponseType GetProvisioningRequest(SecurityLevel requested_security_level,
|
||||||
|
CdmCertificateType cert_type,
|
||||||
|
const std::string& cert_authority,
|
||||||
CdmProvisioningRequest* request,
|
CdmProvisioningRequest* request,
|
||||||
std::string* default_url);
|
std::string* default_url);
|
||||||
CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response);
|
CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response,
|
||||||
|
std::string* cert,
|
||||||
|
std::string* wrapped_key);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ComposeJsonRequestAsQueryString(const std::string& message,
|
void ComposeJsonRequestAsQueryString(const std::string& message,
|
||||||
@@ -30,6 +34,7 @@ class CertificateProvisioning {
|
|||||||
const std::string& end_substr,
|
const std::string& end_substr,
|
||||||
std::string* result);
|
std::string* result);
|
||||||
CryptoSession crypto_session_;
|
CryptoSession crypto_session_;
|
||||||
|
CdmCertificateType cert_type_;
|
||||||
|
|
||||||
CORE_DISALLOW_COPY_AND_ASSIGN(CertificateProvisioning);
|
CORE_DISALLOW_COPY_AND_ASSIGN(CertificateProvisioning);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -64,6 +64,11 @@ enum CdmSecurityLevel {
|
|||||||
kSecurityLevelUnknown
|
kSecurityLevelUnknown
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum CdmCertificateType {
|
||||||
|
kCertificateWidevine,
|
||||||
|
kCertificateX509,
|
||||||
|
};
|
||||||
|
|
||||||
struct CdmDecryptionParameters {
|
struct CdmDecryptionParameters {
|
||||||
bool is_encrypted;
|
bool is_encrypted;
|
||||||
bool is_secure;
|
bool is_secure;
|
||||||
|
|||||||
@@ -446,6 +446,8 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(
|
|||||||
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
||||||
*/
|
*/
|
||||||
CdmResponseType CdmEngine::GetProvisioningRequest(
|
CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||||
|
CdmCertificateType cert_type,
|
||||||
|
const std::string& cert_authority,
|
||||||
CdmProvisioningRequest* request,
|
CdmProvisioningRequest* request,
|
||||||
std::string* default_url) {
|
std::string* default_url) {
|
||||||
if (!request || !default_url) {
|
if (!request || !default_url) {
|
||||||
@@ -454,6 +456,8 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
|||||||
}
|
}
|
||||||
return cert_provisioning_.GetProvisioningRequest(
|
return cert_provisioning_.GetProvisioningRequest(
|
||||||
cert_provisioning_requested_security_level_,
|
cert_provisioning_requested_security_level_,
|
||||||
|
cert_type,
|
||||||
|
cert_authority,
|
||||||
request,
|
request,
|
||||||
default_url);
|
default_url);
|
||||||
}
|
}
|
||||||
@@ -466,12 +470,25 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
|||||||
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
||||||
*/
|
*/
|
||||||
CdmResponseType CdmEngine::HandleProvisioningResponse(
|
CdmResponseType CdmEngine::HandleProvisioningResponse(
|
||||||
CdmProvisioningResponse& response) {
|
CdmProvisioningResponse& response,
|
||||||
|
std::string* cert,
|
||||||
|
std::string* wrapped_key) {
|
||||||
if (response.empty()) {
|
if (response.empty()) {
|
||||||
LOGE("CdmEngine::HandleProvisioningResponse: Empty provisioning response.");
|
LOGE("CdmEngine::HandleProvisioningResponse: Empty provisioning response.");
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
return cert_provisioning_.HandleProvisioningResponse(response);
|
if (NULL == cert) {
|
||||||
|
LOGE("CdmEngine::HandleProvisioningResponse: invalid certificate "
|
||||||
|
"destination");
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
if (NULL == wrapped_key) {
|
||||||
|
LOGE("CdmEngine::HandleProvisioningResponse: invalid wrapped key "
|
||||||
|
"destination");
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
return cert_provisioning_.HandleProvisioningResponse(response, cert,
|
||||||
|
wrapped_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
CdmResponseType CdmEngine::GetSecureStops(
|
CdmResponseType CdmEngine::GetSecureStops(
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "license_protocol.pb.h"
|
#include "license_protocol.pb.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "string_conversions.h"
|
#include "string_conversions.h"
|
||||||
|
#include "wv_cdm_constants.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const std::string kDefaultProvisioningServerUrl =
|
const std::string kDefaultProvisioningServerUrl =
|
||||||
@@ -17,6 +18,7 @@ const std::string kDefaultProvisioningServerUrl =
|
|||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
// Protobuf generated classes.
|
// Protobuf generated classes.
|
||||||
using video_widevine_server::sdk::ClientIdentification;
|
using video_widevine_server::sdk::ClientIdentification;
|
||||||
|
using video_widevine_server::sdk::ProvisioningOptions;
|
||||||
using video_widevine_server::sdk::ProvisioningRequest;
|
using video_widevine_server::sdk::ProvisioningRequest;
|
||||||
using video_widevine_server::sdk::ProvisioningResponse;
|
using video_widevine_server::sdk::ProvisioningResponse;
|
||||||
using video_widevine_server::sdk::SignedProvisioningMessage;
|
using video_widevine_server::sdk::SignedProvisioningMessage;
|
||||||
@@ -54,6 +56,8 @@ void CertificateProvisioning::ComposeJsonRequestAsQueryString(
|
|||||||
*/
|
*/
|
||||||
CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||||
SecurityLevel requested_security_level,
|
SecurityLevel requested_security_level,
|
||||||
|
CdmCertificateType cert_type,
|
||||||
|
const std::string& cert_authority,
|
||||||
CdmProvisioningRequest* request,
|
CdmProvisioningRequest* request,
|
||||||
std::string* default_url) {
|
std::string* default_url) {
|
||||||
default_url->assign(kDefaultProvisioningServerUrl);
|
default_url->assign(kDefaultProvisioningServerUrl);
|
||||||
@@ -86,6 +90,24 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
|||||||
std::string the_nonce(reinterpret_cast<char*>(&nonce), sizeof(nonce));
|
std::string the_nonce(reinterpret_cast<char*>(&nonce), sizeof(nonce));
|
||||||
provisioning_request.set_nonce(the_nonce);
|
provisioning_request.set_nonce(the_nonce);
|
||||||
|
|
||||||
|
ProvisioningOptions* options = provisioning_request.mutable_options();
|
||||||
|
switch (cert_type) {
|
||||||
|
case kCertificateWidevine:
|
||||||
|
options->set_certificate_type(
|
||||||
|
video_widevine_server::sdk::ProvisioningOptions_CertificateType_RSA_WIDEVINE);
|
||||||
|
break;
|
||||||
|
case kCertificateX509:
|
||||||
|
options->set_certificate_type(
|
||||||
|
video_widevine_server::sdk::ProvisioningOptions_CertificateType_X509);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOGE("GetProvisioningRequest: unknown certificate type %ld", cert_type);
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
cert_type_ = cert_type;
|
||||||
|
options->set_certificate_authority(cert_authority);
|
||||||
|
|
||||||
std::string serialized_message;
|
std::string serialized_message;
|
||||||
provisioning_request.SerializeToString(&serialized_message);
|
provisioning_request.SerializeToString(&serialized_message);
|
||||||
|
|
||||||
@@ -155,7 +177,9 @@ bool CertificateProvisioning::ParseJsonResponse(
|
|||||||
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
||||||
*/
|
*/
|
||||||
CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||||
CdmProvisioningResponse& response) {
|
CdmProvisioningResponse& response,
|
||||||
|
std::string* cert,
|
||||||
|
std::string* wrapped_key) {
|
||||||
|
|
||||||
// Extracts signed response from JSON string, decodes base64 signed response
|
// Extracts signed response from JSON string, decodes base64 signed response
|
||||||
const std::string kMessageStart = "\"signedResponse\": \"";
|
const std::string kMessageStart = "\"signedResponse\": \"";
|
||||||
@@ -211,6 +235,12 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
|||||||
|
|
||||||
crypto_session_.Close();
|
crypto_session_.Close();
|
||||||
|
|
||||||
|
if (cert_type_ == kCertificateX509) {
|
||||||
|
*cert = provisioning_response.device_certificate();
|
||||||
|
*wrapped_key = wrapped_rsa_key;
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string& device_certificate =
|
const std::string& device_certificate =
|
||||||
provisioning_response.device_certificate();
|
provisioning_response.device_certificate();
|
||||||
|
|
||||||
|
|||||||
@@ -292,15 +292,38 @@ message SessionState {
|
|||||||
// Public protocol buffer definitions for Widevine Device Certificate
|
// Public protocol buffer definitions for Widevine Device Certificate
|
||||||
// Provisioning protocol.
|
// Provisioning protocol.
|
||||||
|
|
||||||
|
// PROPOSED message for customizing provisioning request.
|
||||||
|
// This could support requesting specificy types of certificates.
|
||||||
|
// E.g. Cast X.509 certs.
|
||||||
|
message ProvisioningOptions {
|
||||||
|
// PROPOSED enum identifying the certificate type.
|
||||||
|
enum CertificateType {
|
||||||
|
RSA_WIDEVINE = 0; // Default. The original certificate type.
|
||||||
|
X509 = 1; // X.509 certificate.
|
||||||
|
}
|
||||||
|
|
||||||
|
optional CertificateType certificate_type = 1;
|
||||||
|
|
||||||
|
// OPEN QUESTION: How does the client specify the cert root authority?
|
||||||
|
// Should this be the cert authority's domain? E.g. foo.com?
|
||||||
|
optional string certificate_authority = 2;
|
||||||
|
}
|
||||||
|
|
||||||
// Provisioning request sent by client devices to provisioning service.
|
// Provisioning request sent by client devices to provisioning service.
|
||||||
message ProvisioningRequest {
|
message ProvisioningRequest {
|
||||||
// Device root of trust and other client identification. Required.
|
// Device root of trust and other client identification. Required.
|
||||||
optional ClientIdentification client_id = 1;
|
optional ClientIdentification client_id = 1;
|
||||||
// Nonce value used to prevent replay attacks. Required.
|
// Nonce value used to prevent replay attacks. Required.
|
||||||
optional bytes nonce = 2;
|
optional bytes nonce = 2;
|
||||||
|
// Options for type of certificate to generate. Optional.
|
||||||
|
optional ProvisioningOptions options = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provisioning response sent by the provisioning server to client devices.
|
// Provisioning response sent by the provisioning server to client devices.
|
||||||
|
//
|
||||||
|
// PROPOSAL: The contents of this message vary depending upon the value of
|
||||||
|
// CertificateType in options. TODO(blueeyes): Determine the right way to
|
||||||
|
// transfer X.509 certs.
|
||||||
message ProvisioningResponse {
|
message ProvisioningResponse {
|
||||||
// AES-128 encrypted device private RSA key. PKCS#1 ASN.1 DER-encoded.
|
// AES-128 encrypted device private RSA key. PKCS#1 ASN.1 DER-encoded.
|
||||||
// Required.
|
// Required.
|
||||||
|
|||||||
@@ -188,9 +188,14 @@ TEST(WvCdmProvisioningTest, ProvisioningTest) {
|
|||||||
CdmEngine cdm_engine;
|
CdmEngine cdm_engine;
|
||||||
CdmProvisioningRequest prov_request;
|
CdmProvisioningRequest prov_request;
|
||||||
std::string provisioning_server_url;
|
std::string provisioning_server_url;
|
||||||
|
CdmCertificateType cert_type = kCertificateWidevine;
|
||||||
|
std::string cert_authority;
|
||||||
|
std::string cert, wrapped_key;
|
||||||
|
|
||||||
cdm_engine.GetProvisioningRequest(&prov_request, &provisioning_server_url);
|
cdm_engine.GetProvisioningRequest(cert_type, cert_authority,
|
||||||
cdm_engine.HandleProvisioningResponse(kValidJsonProvisioningResponse);
|
&prov_request, &provisioning_server_url);
|
||||||
|
cdm_engine.HandleProvisioningResponse(kValidJsonProvisioningResponse,
|
||||||
|
&cert, &wrapped_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvCdmEngineTest, BaseIsoBmffMessageTest) {
|
TEST_F(WvCdmEngineTest, BaseIsoBmffMessageTest) {
|
||||||
|
|||||||
@@ -64,10 +64,15 @@ class WvContentDecryptionModule {
|
|||||||
|
|
||||||
// Provisioning related methods
|
// Provisioning related methods
|
||||||
virtual CdmResponseType GetProvisioningRequest(
|
virtual CdmResponseType GetProvisioningRequest(
|
||||||
CdmProvisioningRequest* request, std::string* default_url);
|
CdmCertificateType cert_type,
|
||||||
|
const std::string& cert_authority,
|
||||||
|
CdmProvisioningRequest* request,
|
||||||
|
std::string* default_url);
|
||||||
|
|
||||||
virtual CdmResponseType HandleProvisioningResponse(
|
virtual CdmResponseType HandleProvisioningResponse(
|
||||||
CdmProvisioningResponse& response);
|
CdmProvisioningResponse& response,
|
||||||
|
std::string* cert,
|
||||||
|
std::string* wrapped_key);
|
||||||
|
|
||||||
// Secure stop related methods
|
// Secure stop related methods
|
||||||
virtual CdmResponseType GetSecureStops(CdmSecureStops* secure_stops);
|
virtual CdmResponseType GetSecureStops(CdmSecureStops* secure_stops);
|
||||||
|
|||||||
@@ -102,13 +102,19 @@ CdmResponseType WvContentDecryptionModule::QueryKeyControlInfo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
CdmResponseType WvContentDecryptionModule::GetProvisioningRequest(
|
CdmResponseType WvContentDecryptionModule::GetProvisioningRequest(
|
||||||
CdmProvisioningRequest* request, std::string* default_url) {
|
CdmCertificateType cert_type,
|
||||||
return cdm_engine_->GetProvisioningRequest(request, default_url);
|
const std::string& cert_authority,
|
||||||
|
CdmProvisioningRequest* request,
|
||||||
|
std::string* default_url) {
|
||||||
|
return cdm_engine_->GetProvisioningRequest(cert_type, cert_authority,
|
||||||
|
request, default_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
CdmResponseType WvContentDecryptionModule::HandleProvisioningResponse(
|
CdmResponseType WvContentDecryptionModule::HandleProvisioningResponse(
|
||||||
CdmProvisioningResponse& response) {
|
CdmProvisioningResponse& response,
|
||||||
return cdm_engine_->HandleProvisioningResponse(response);
|
std::string* cert,
|
||||||
|
std::string* wrapped_key) {
|
||||||
|
return cdm_engine_->HandleProvisioningResponse(response, cert, wrapped_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
CdmResponseType WvContentDecryptionModule::GetSecureStops(
|
CdmResponseType WvContentDecryptionModule::GetSecureStops(
|
||||||
|
|||||||
@@ -423,40 +423,82 @@ class WvCdmSessionSharingTest
|
|||||||
TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
|
TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
|
||||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||||
std::string provisioning_server_url;
|
std::string provisioning_server_url;
|
||||||
|
CdmCertificateType cert_type = kCertificateWidevine;
|
||||||
|
std::string cert_authority, cert, wrapped_key;
|
||||||
|
|
||||||
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
|
||||||
|
cert_type, cert_authority,
|
||||||
&key_msg_, &provisioning_server_url));
|
&key_msg_, &provisioning_server_url));
|
||||||
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
|
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
|
||||||
|
|
||||||
std::string response =
|
std::string response =
|
||||||
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
||||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||||
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response));
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
||||||
|
decryptor_.HandleProvisioningResponse(response, &cert,
|
||||||
|
&wrapped_key));
|
||||||
|
EXPECT_EQ(0, static_cast<int>(cert.size()));
|
||||||
|
EXPECT_EQ(0, static_cast<int>(wrapped_key.size()));
|
||||||
decryptor_.CloseSession(session_id_);
|
decryptor_.CloseSession(session_id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) {
|
TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) {
|
||||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||||
std::string provisioning_server_url;
|
std::string provisioning_server_url;
|
||||||
|
CdmCertificateType cert_type = kCertificateWidevine;
|
||||||
|
std::string cert_authority, cert, wrapped_key;
|
||||||
|
|
||||||
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
|
||||||
|
cert_type, cert_authority,
|
||||||
&key_msg_, &provisioning_server_url));
|
&key_msg_, &provisioning_server_url));
|
||||||
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
|
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
|
||||||
|
|
||||||
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
|
||||||
|
cert_type, cert_authority,
|
||||||
&key_msg_, &provisioning_server_url));
|
&key_msg_, &provisioning_server_url));
|
||||||
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
|
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
|
||||||
|
|
||||||
std::string response =
|
std::string response =
|
||||||
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
||||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||||
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response));
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
||||||
|
decryptor_.HandleProvisioningResponse(response, &cert,
|
||||||
|
&wrapped_key));
|
||||||
|
EXPECT_EQ(0, static_cast<int>(cert.size()));
|
||||||
|
EXPECT_EQ(0, static_cast<int>(wrapped_key.size()));
|
||||||
|
|
||||||
response =
|
response =
|
||||||
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
||||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||||
EXPECT_EQ(wvcdm::UNKNOWN_ERROR,
|
EXPECT_EQ(wvcdm::UNKNOWN_ERROR,
|
||||||
decryptor_.HandleProvisioningResponse(response));
|
decryptor_.HandleProvisioningResponse(response, &cert,
|
||||||
|
&wrapped_key));
|
||||||
|
EXPECT_EQ(0, static_cast<int>(cert.size()));
|
||||||
|
EXPECT_EQ(0, static_cast<int>(wrapped_key.size()));
|
||||||
|
decryptor_.CloseSession(session_id_);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WvCdmRequestLicenseTest, DISABLED_X509ProvisioningTest) {
|
||||||
|
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||||
|
std::string provisioning_server_url;
|
||||||
|
CdmCertificateType cert_type = kCertificateX509;
|
||||||
|
// TODO(rfrias): insert appropriate CA here
|
||||||
|
std::string cert_authority = "cast.google.com";
|
||||||
|
std::string cert, wrapped_key;
|
||||||
|
|
||||||
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
|
||||||
|
cert_type, cert_authority,
|
||||||
|
&key_msg_, &provisioning_server_url));
|
||||||
|
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
|
||||||
|
|
||||||
|
std::string response =
|
||||||
|
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
||||||
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||||
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
||||||
|
decryptor_.HandleProvisioningResponse(response, &cert,
|
||||||
|
&wrapped_key));
|
||||||
|
EXPECT_NE(0, static_cast<int>(cert.size()));
|
||||||
|
EXPECT_NE(0, static_cast<int>(wrapped_key.size()));
|
||||||
decryptor_.CloseSession(session_id_);
|
decryptor_.CloseSession(session_id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,14 +521,19 @@ TEST_F(WvCdmRequestLicenseTest, PropertySetTest) {
|
|||||||
|
|
||||||
if (NEED_PROVISIONING == sts) {
|
if (NEED_PROVISIONING == sts) {
|
||||||
std::string provisioning_server_url;
|
std::string provisioning_server_url;
|
||||||
|
CdmCertificateType cert_type = kCertificateWidevine;
|
||||||
|
std::string cert_authority, cert, wrapped_key;
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
NO_ERROR,
|
NO_ERROR,
|
||||||
decryptor_.GetProvisioningRequest(&key_msg_, &provisioning_server_url));
|
decryptor_.GetProvisioningRequest(cert_type, cert_authority,
|
||||||
|
&key_msg_, &provisioning_server_url));
|
||||||
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
|
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
|
||||||
std::string response =
|
std::string response =
|
||||||
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
||||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||||
EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response));
|
EXPECT_EQ(NO_ERROR,
|
||||||
|
decryptor_.HandleProvisioningResponse(response, &cert,
|
||||||
|
&wrapped_key));
|
||||||
EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set_L3,
|
EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set_L3,
|
||||||
&session_id_L3));
|
&session_id_L3));
|
||||||
} else {
|
} else {
|
||||||
@@ -523,14 +570,19 @@ TEST_F(WvCdmRequestLicenseTest, ForceL3Test) {
|
|||||||
EXPECT_EQ(NEED_PROVISIONING,
|
EXPECT_EQ(NEED_PROVISIONING,
|
||||||
decryptor_.OpenSession(g_key_system, &property_set, &session_id_));
|
decryptor_.OpenSession(g_key_system, &property_set, &session_id_));
|
||||||
std::string provisioning_server_url;
|
std::string provisioning_server_url;
|
||||||
|
CdmCertificateType cert_type = kCertificateWidevine;
|
||||||
|
std::string cert_authority, cert, wrapped_key;
|
||||||
EXPECT_EQ(NO_ERROR,
|
EXPECT_EQ(NO_ERROR,
|
||||||
decryptor_.GetProvisioningRequest(&key_msg_,
|
decryptor_.GetProvisioningRequest(cert_type, cert_authority,
|
||||||
|
&key_msg_,
|
||||||
&provisioning_server_url));
|
&provisioning_server_url));
|
||||||
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
|
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
|
||||||
std::string response =
|
std::string response =
|
||||||
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
||||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||||
EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response));
|
EXPECT_EQ(NO_ERROR,
|
||||||
|
decryptor_.HandleProvisioningResponse(response, &cert,
|
||||||
|
&wrapped_key));
|
||||||
|
|
||||||
EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set,
|
EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set,
|
||||||
&session_id_));
|
&session_id_));
|
||||||
@@ -912,13 +964,18 @@ TEST_F(WvCdmRequestLicenseTest, SecurityLevelPathBackwardCompatibility) {
|
|||||||
|
|
||||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||||
std::string provisioning_server_url;
|
std::string provisioning_server_url;
|
||||||
|
CdmCertificateType cert_type = kCertificateWidevine;
|
||||||
|
std::string cert_authority, cert, wrapped_key;
|
||||||
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
|
||||||
|
cert_type, cert_authority,
|
||||||
&key_msg_, &provisioning_server_url));
|
&key_msg_, &provisioning_server_url));
|
||||||
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
|
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
|
||||||
std::string response =
|
std::string response =
|
||||||
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
||||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||||
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response));
|
EXPECT_EQ(wvcdm::NO_ERROR,
|
||||||
|
decryptor_.HandleProvisioningResponse(response, &cert,
|
||||||
|
&wrapped_key));
|
||||||
decryptor_.CloseSession(session_id_);
|
decryptor_.CloseSession(session_id_);
|
||||||
|
|
||||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||||
@@ -969,13 +1026,16 @@ TEST_F(WvCdmRequestLicenseTest, SecurityLevelPathBackwardCompatibility) {
|
|||||||
app_parameters, &key_msg_,
|
app_parameters, &key_msg_,
|
||||||
&server_url));
|
&server_url));
|
||||||
EXPECT_EQ(NO_ERROR,
|
EXPECT_EQ(NO_ERROR,
|
||||||
decryptor_.GetProvisioningRequest(&key_msg_,
|
decryptor_.GetProvisioningRequest(cert_type, cert_authority,
|
||||||
|
&key_msg_,
|
||||||
&provisioning_server_url));
|
&provisioning_server_url));
|
||||||
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
|
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
|
||||||
response =
|
response =
|
||||||
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
||||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||||
EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response));
|
EXPECT_EQ(NO_ERROR,
|
||||||
|
decryptor_.HandleProvisioningResponse(response, &cert,
|
||||||
|
&wrapped_key));
|
||||||
|
|
||||||
EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set,
|
EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set,
|
||||||
&session_id_));
|
&session_id_));
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ enum {
|
|||||||
kErrorUnsupportedCrypto = ERROR_DRM_VENDOR_MIN + 2,
|
kErrorUnsupportedCrypto = ERROR_DRM_VENDOR_MIN + 2,
|
||||||
kErrorExpectedUnencrypted = ERROR_DRM_VENDOR_MIN + 3,
|
kErrorExpectedUnencrypted = ERROR_DRM_VENDOR_MIN + 3,
|
||||||
kErrorSessionIsOpen = ERROR_DRM_VENDOR_MIN + 4,
|
kErrorSessionIsOpen = ERROR_DRM_VENDOR_MIN + 4,
|
||||||
kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 4,
|
kErrorTooManySessions = ERROR_DRM_VENDOR_MIN + 5,
|
||||||
|
kErrorInvalidKey = ERROR_DRM_VENDOR_MIN + 6,
|
||||||
|
kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 6,
|
||||||
|
|
||||||
// Used by crypto test mode
|
// Used by crypto test mode
|
||||||
kErrorTestMode = ERROR_DRM_VENDOR_MAX,
|
kErrorTestMode = ERROR_DRM_VENDOR_MAX,
|
||||||
|
|||||||
@@ -72,10 +72,14 @@ class WVDrmPlugin : public android::DrmPlugin,
|
|||||||
const Vector<uint8_t>& sessionId,
|
const Vector<uint8_t>& sessionId,
|
||||||
KeyedVector<String8, String8>& infoMap) const;
|
KeyedVector<String8, String8>& infoMap) const;
|
||||||
|
|
||||||
virtual status_t getProvisionRequest(Vector<uint8_t>& request,
|
virtual status_t getProvisionRequest(const String8& cert_type,
|
||||||
|
const String8& cert_authority,
|
||||||
|
Vector<uint8_t>& request,
|
||||||
String8& defaultUrl);
|
String8& defaultUrl);
|
||||||
|
|
||||||
virtual status_t provideProvisionResponse(const Vector<uint8_t>& response);
|
virtual status_t provideProvisionResponse(const Vector<uint8_t>& response,
|
||||||
|
Vector<uint8_t>& certificate,
|
||||||
|
Vector<uint8_t>& wrapped_key);
|
||||||
|
|
||||||
virtual status_t getSecureStops(List<Vector<uint8_t> >& secureStops);
|
virtual status_t getSecureStops(List<Vector<uint8_t> >& secureStops);
|
||||||
|
|
||||||
@@ -120,6 +124,12 @@ class WVDrmPlugin : public android::DrmPlugin,
|
|||||||
const Vector<uint8_t>& signature,
|
const Vector<uint8_t>& signature,
|
||||||
bool& match);
|
bool& match);
|
||||||
|
|
||||||
|
virtual status_t signRSA(const Vector<uint8_t>& sessionId,
|
||||||
|
const String8& algorithm,
|
||||||
|
const Vector<uint8_t>& message,
|
||||||
|
const Vector<uint8_t>& wrappedKey,
|
||||||
|
Vector<uint8_t>& signature);
|
||||||
|
|
||||||
virtual void onEvent(const CdmSessionId& cdmSessionId,
|
virtual void onEvent(const CdmSessionId& cdmSessionId,
|
||||||
CdmEventType cdmEventType);
|
CdmEventType cdmEventType);
|
||||||
|
|
||||||
@@ -224,6 +234,8 @@ class WVDrmPlugin : public android::DrmPlugin,
|
|||||||
|
|
||||||
status_t mapAndNotifyOfOEMCryptoResult(const Vector<uint8_t>& sessionId,
|
status_t mapAndNotifyOfOEMCryptoResult(const Vector<uint8_t>& sessionId,
|
||||||
OEMCryptoResult res);
|
OEMCryptoResult res);
|
||||||
|
|
||||||
|
status_t mapOEMCryptoResult(OEMCryptoResult res);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace wvdrm
|
} // namespace wvdrm
|
||||||
|
|||||||
@@ -57,6 +57,33 @@ class WVGenericCryptoInterface {
|
|||||||
algorithm, signature, signature_length);
|
algorithm, signature, signature_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual OEMCryptoResult openSession(OEMCrypto_SESSION *session) {
|
||||||
|
return OEMCrypto_OpenSession(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual OEMCryptoResult closeSession(OEMCrypto_SESSION session) {
|
||||||
|
return OEMCrypto_CloseSession(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual OEMCryptoResult loadDeviceRSAKey(OEMCrypto_SESSION session,
|
||||||
|
const uint8_t* wrapped_rsa_key,
|
||||||
|
size_t wrapped_rsa_key_length) {
|
||||||
|
return OEMCrypto_LoadDeviceRSAKey(session, wrapped_rsa_key,
|
||||||
|
wrapped_rsa_key_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual OEMCryptoResult generateRSASignature(
|
||||||
|
OEMCrypto_SESSION session,
|
||||||
|
const uint8_t* message,
|
||||||
|
size_t message_length,
|
||||||
|
uint8_t* signature,
|
||||||
|
size_t* signature_length,
|
||||||
|
RSA_Padding_Scheme padding_scheme) {
|
||||||
|
return OEMCrypto_GenerateRSASignature(session, message, message_length,
|
||||||
|
signature, signature_length,
|
||||||
|
padding_scheme);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_EVIL_CONSTRUCTORS(WVGenericCryptoInterface);
|
DISALLOW_EVIL_CONSTRUCTORS(WVGenericCryptoInterface);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -320,12 +320,23 @@ status_t WVDrmPlugin::queryKeyStatus(
|
|||||||
return mapCdmResponseType(res);
|
return mapCdmResponseType(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t WVDrmPlugin::getProvisionRequest(Vector<uint8_t>& request,
|
status_t WVDrmPlugin::getProvisionRequest(const String8& cert_type,
|
||||||
|
const String8& cert_authority,
|
||||||
|
Vector<uint8_t>& request,
|
||||||
String8& defaultUrl) {
|
String8& defaultUrl) {
|
||||||
CdmProvisioningRequest cdmProvisionRequest;
|
CdmProvisioningRequest cdmProvisionRequest;
|
||||||
string cdmDefaultUrl;
|
string cdmDefaultUrl;
|
||||||
|
|
||||||
CdmResponseType res = mCDM->GetProvisioningRequest(&cdmProvisionRequest,
|
CdmCertificateType cdmCertType = kCertificateWidevine;
|
||||||
|
if (cert_type == "X.509") {
|
||||||
|
cdmCertType = kCertificateX509;
|
||||||
|
}
|
||||||
|
|
||||||
|
string cdmCertAuthority = cert_authority.string();
|
||||||
|
|
||||||
|
CdmResponseType res = mCDM->GetProvisioningRequest(cdmCertType,
|
||||||
|
cdmCertAuthority,
|
||||||
|
&cdmProvisionRequest,
|
||||||
&cdmDefaultUrl);
|
&cdmDefaultUrl);
|
||||||
|
|
||||||
if (isCdmResponseTypeSuccess(res)) {
|
if (isCdmResponseTypeSuccess(res)) {
|
||||||
@@ -342,9 +353,27 @@ status_t WVDrmPlugin::getProvisionRequest(Vector<uint8_t>& request,
|
|||||||
}
|
}
|
||||||
|
|
||||||
status_t WVDrmPlugin::provideProvisionResponse(
|
status_t WVDrmPlugin::provideProvisionResponse(
|
||||||
const Vector<uint8_t>& response) {
|
const Vector<uint8_t>& response,
|
||||||
|
Vector<uint8_t>& certificate,
|
||||||
|
Vector<uint8_t>& wrapped_key) {
|
||||||
CdmProvisioningResponse cdmResponse(response.begin(), response.end());
|
CdmProvisioningResponse cdmResponse(response.begin(), response.end());
|
||||||
CdmResponseType res = mCDM->HandleProvisioningResponse(cdmResponse);
|
string cdmCertificate;
|
||||||
|
string cdmWrappedKey;
|
||||||
|
CdmResponseType res = mCDM->HandleProvisioningResponse(cdmResponse,
|
||||||
|
&cdmCertificate,
|
||||||
|
&cdmWrappedKey);
|
||||||
|
if (isCdmResponseTypeSuccess(res)) {
|
||||||
|
certificate.clear();
|
||||||
|
certificate.appendArray(
|
||||||
|
reinterpret_cast<const uint8_t*>(cdmCertificate.data()),
|
||||||
|
cdmCertificate.size());
|
||||||
|
|
||||||
|
wrapped_key.clear();
|
||||||
|
wrapped_key.appendArray(
|
||||||
|
reinterpret_cast<const uint8_t*>(cdmWrappedKey.data()),
|
||||||
|
cdmWrappedKey.size());
|
||||||
|
}
|
||||||
|
|
||||||
return mapCdmResponseType(res);
|
return mapCdmResponseType(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -688,7 +717,7 @@ status_t WVDrmPlugin::sign(const Vector<uint8_t>& sessionId,
|
|||||||
|
|
||||||
res = mCrypto->sign(cryptoSession.oecSessionId(), message.array(),
|
res = mCrypto->sign(cryptoSession.oecSessionId(), message.array(),
|
||||||
message.size(), cryptoSession.macAlgorithm(),
|
message.size(), cryptoSession.macAlgorithm(),
|
||||||
signature.editArray(), &signatureSize);
|
NULL, &signatureSize);
|
||||||
|
|
||||||
if (res != OEMCrypto_ERROR_SHORT_BUFFER) {
|
if (res != OEMCrypto_ERROR_SHORT_BUFFER) {
|
||||||
ALOGE("OEMCrypto_Generic_Sign failed with %u when requesting signature "
|
ALOGE("OEMCrypto_Generic_Sign failed with %u when requesting signature "
|
||||||
@@ -755,6 +784,68 @@ status_t WVDrmPlugin::verify(const Vector<uint8_t>& sessionId,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status_t WVDrmPlugin::signRSA(const Vector<uint8_t>& sessionId,
|
||||||
|
const String8& algorithm,
|
||||||
|
const Vector<uint8_t>& message,
|
||||||
|
const Vector<uint8_t>& wrappedKey,
|
||||||
|
Vector<uint8_t>& signature) {
|
||||||
|
CdmSessionId cdmSessionId(sessionId.begin(), sessionId.end());
|
||||||
|
|
||||||
|
if (!mCryptoSessions.count(cdmSessionId)) {
|
||||||
|
return android::ERROR_DRM_SESSION_NOT_OPENED;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
|
||||||
|
|
||||||
|
RSA_Padding_Scheme padding_scheme;
|
||||||
|
if (algorithm == "RSASSA-PSS-SHA1") {
|
||||||
|
padding_scheme = kSign_RSASSA_PSS;
|
||||||
|
} else if (algorithm == "PKCS1-BlockType1") {
|
||||||
|
padding_scheme = kSign_PKCS1_Block1;
|
||||||
|
} else {
|
||||||
|
ALOGE("Unknown RSA Algorithm %s", algorithm.string());
|
||||||
|
return android::ERROR_DRM_CANNOT_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
OEMCryptoResult res = mCrypto->loadDeviceRSAKey(cryptoSession.oecSessionId(),
|
||||||
|
wrappedKey.array(),
|
||||||
|
wrappedKey.size());
|
||||||
|
if (res != OEMCrypto_SUCCESS) {
|
||||||
|
ALOGE("OEMCrypto_LoadDeviceRSAKey failed with %u", res);
|
||||||
|
return mapOEMCryptoResult(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t signatureSize = 0;
|
||||||
|
|
||||||
|
res = mCrypto->generateRSASignature(cryptoSession.oecSessionId(),
|
||||||
|
message.array(), message.size(),
|
||||||
|
NULL, &signatureSize, padding_scheme);
|
||||||
|
|
||||||
|
if (res != OEMCrypto_ERROR_SHORT_BUFFER) {
|
||||||
|
ALOGE("OEMCrypto_GenerateRSASignature failed with %u when requesting "
|
||||||
|
"signature size", res);
|
||||||
|
if (res != OEMCrypto_SUCCESS) {
|
||||||
|
return mapOEMCryptoResult(res);
|
||||||
|
} else {
|
||||||
|
return android::ERROR_DRM_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signature.resize(signatureSize);
|
||||||
|
|
||||||
|
res = mCrypto->generateRSASignature(cryptoSession.oecSessionId(),
|
||||||
|
message.array(), message.size(),
|
||||||
|
signature.editArray(), &signatureSize,
|
||||||
|
padding_scheme);
|
||||||
|
|
||||||
|
if (res != OEMCrypto_SUCCESS) {
|
||||||
|
ALOGE("OEMCrypto_GenerateRSASignature failed with %u", res);
|
||||||
|
return mapOEMCryptoResult(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return android::OK;
|
||||||
|
}
|
||||||
|
|
||||||
void WVDrmPlugin::onEvent(const CdmSessionId& cdmSessionId,
|
void WVDrmPlugin::onEvent(const CdmSessionId& cdmSessionId,
|
||||||
CdmEventType cdmEventType) {
|
CdmEventType cdmEventType) {
|
||||||
Vector<uint8_t> sessionId;
|
Vector<uint8_t> sessionId;
|
||||||
@@ -791,8 +882,13 @@ status_t WVDrmPlugin::mapAndNotifyOfCdmResponseType(
|
|||||||
status_t WVDrmPlugin::mapAndNotifyOfOEMCryptoResult(
|
status_t WVDrmPlugin::mapAndNotifyOfOEMCryptoResult(
|
||||||
const Vector<uint8_t>& sessionId,
|
const Vector<uint8_t>& sessionId,
|
||||||
OEMCryptoResult res) {
|
OEMCryptoResult res) {
|
||||||
// Note that we only cover those errors that OEMCryptoCENC.h states may be
|
if (res == OEMCrypto_ERROR_NO_DEVICE_KEY) {
|
||||||
// returned by the generic crypto methods.
|
sendEvent(kDrmPluginEventProvisionRequired, 0, &sessionId, NULL);
|
||||||
|
}
|
||||||
|
return mapOEMCryptoResult(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t WVDrmPlugin::mapOEMCryptoResult(OEMCryptoResult res) {
|
||||||
switch (res) {
|
switch (res) {
|
||||||
case OEMCrypto_SUCCESS:
|
case OEMCrypto_SUCCESS:
|
||||||
return android::OK;
|
return android::OK;
|
||||||
@@ -801,11 +897,19 @@ status_t WVDrmPlugin::mapAndNotifyOfOEMCryptoResult(
|
|||||||
case OEMCrypto_ERROR_SHORT_BUFFER:
|
case OEMCrypto_ERROR_SHORT_BUFFER:
|
||||||
return kErrorIncorrectBufferSize;
|
return kErrorIncorrectBufferSize;
|
||||||
case OEMCrypto_ERROR_NO_DEVICE_KEY:
|
case OEMCrypto_ERROR_NO_DEVICE_KEY:
|
||||||
sendEvent(kDrmPluginEventProvisionRequired, 0, &sessionId, NULL);
|
|
||||||
return android::ERROR_DRM_NOT_PROVISIONED;
|
return android::ERROR_DRM_NOT_PROVISIONED;
|
||||||
case OEMCrypto_ERROR_INVALID_SESSION:
|
case OEMCrypto_ERROR_INVALID_SESSION:
|
||||||
return android::ERROR_DRM_SESSION_NOT_OPENED;
|
return android::ERROR_DRM_SESSION_NOT_OPENED;
|
||||||
|
case OEMCrypto_ERROR_TOO_MANY_SESSIONS:
|
||||||
|
return kErrorTooManySessions;
|
||||||
|
case OEMCrypto_ERROR_INVALID_RSA_KEY:
|
||||||
|
return kErrorInvalidKey;
|
||||||
|
case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES:
|
||||||
|
return android::ERROR_DRM_RESOURCE_BUSY;
|
||||||
|
case OEMCrypto_ERROR_NOT_IMPLEMENTED:
|
||||||
|
return android::ERROR_DRM_CANNOT_HANDLE;
|
||||||
case OEMCrypto_ERROR_UNKNOWN_FAILURE:
|
case OEMCrypto_ERROR_UNKNOWN_FAILURE:
|
||||||
|
case OEMCrypto_ERROR_OPEN_SESSION_FAILED:
|
||||||
return android::ERROR_DRM_UNKNOWN;
|
return android::ERROR_DRM_UNKNOWN;
|
||||||
default:
|
default:
|
||||||
return android::UNKNOWN_ERROR;
|
return android::UNKNOWN_ERROR;
|
||||||
|
|||||||
@@ -56,11 +56,13 @@ class MockCDM : public WvContentDecryptionModule {
|
|||||||
MOCK_METHOD2(QueryKeyControlInfo, CdmResponseType(const CdmSessionId&,
|
MOCK_METHOD2(QueryKeyControlInfo, CdmResponseType(const CdmSessionId&,
|
||||||
CdmQueryMap*));
|
CdmQueryMap*));
|
||||||
|
|
||||||
MOCK_METHOD2(GetProvisioningRequest, CdmResponseType(CdmProvisioningRequest*,
|
MOCK_METHOD4(GetProvisioningRequest, CdmResponseType(CdmCertificateType,
|
||||||
|
const std::string&,
|
||||||
|
CdmProvisioningRequest*,
|
||||||
std::string*));
|
std::string*));
|
||||||
|
|
||||||
MOCK_METHOD1(HandleProvisioningResponse,
|
MOCK_METHOD3(HandleProvisioningResponse,
|
||||||
CdmResponseType(CdmProvisioningResponse&));
|
CdmResponseType(CdmProvisioningResponse&, std::string*, std::string*));
|
||||||
|
|
||||||
MOCK_METHOD1(GetSecureStops, CdmResponseType(CdmSecureStops*));
|
MOCK_METHOD1(GetSecureStops, CdmResponseType(CdmSecureStops*));
|
||||||
|
|
||||||
@@ -93,6 +95,18 @@ class MockCrypto : public WVGenericCryptoInterface {
|
|||||||
MOCK_METHOD6(verify, OEMCryptoResult(OEMCrypto_SESSION, const uint8_t*,
|
MOCK_METHOD6(verify, OEMCryptoResult(OEMCrypto_SESSION, const uint8_t*,
|
||||||
size_t, OEMCrypto_Algorithm,
|
size_t, OEMCrypto_Algorithm,
|
||||||
const uint8_t*, size_t));
|
const uint8_t*, size_t));
|
||||||
|
|
||||||
|
MOCK_METHOD1(openSession, OEMCryptoResult(OEMCrypto_SESSION*));
|
||||||
|
|
||||||
|
MOCK_METHOD1(closeSession, OEMCryptoResult(OEMCrypto_SESSION));
|
||||||
|
|
||||||
|
MOCK_METHOD3(loadDeviceRSAKey, OEMCryptoResult(OEMCrypto_SESSION,
|
||||||
|
const uint8_t*, size_t));
|
||||||
|
|
||||||
|
MOCK_METHOD6(generateRSASignature, OEMCryptoResult(OEMCrypto_SESSION,
|
||||||
|
const uint8_t*, size_t,
|
||||||
|
uint8_t*, size_t*,
|
||||||
|
RSA_Padding_Scheme));
|
||||||
};
|
};
|
||||||
|
|
||||||
class MockDrmPluginListener : public DrmPluginListener {
|
class MockDrmPluginListener : public DrmPluginListener {
|
||||||
@@ -567,15 +581,17 @@ TEST_F(WVDrmPluginTest, GetsProvisioningRequests) {
|
|||||||
|
|
||||||
static const char* kDefaultUrl = "http://google.com/";
|
static const char* kDefaultUrl = "http://google.com/";
|
||||||
|
|
||||||
EXPECT_CALL(cdm, GetProvisioningRequest(_, _))
|
EXPECT_CALL(cdm, GetProvisioningRequest(kCertificateWidevine, IsEmpty(),
|
||||||
.WillOnce(DoAll(SetArgPointee<0>(cdmRequest),
|
_, _))
|
||||||
SetArgPointee<1>(kDefaultUrl),
|
.WillOnce(DoAll(SetArgPointee<2>(cdmRequest),
|
||||||
|
SetArgPointee<3>(kDefaultUrl),
|
||||||
Return(wvcdm::NO_ERROR)));
|
Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
Vector<uint8_t> request;
|
Vector<uint8_t> request;
|
||||||
String8 defaultUrl;
|
String8 defaultUrl;
|
||||||
|
|
||||||
status_t res = plugin.getProvisionRequest(request, defaultUrl);
|
status_t res = plugin.getProvisionRequest(String8(""), String8(""), request,
|
||||||
|
defaultUrl);
|
||||||
|
|
||||||
ASSERT_EQ(OK, res);
|
ASSERT_EQ(OK, res);
|
||||||
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
|
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
|
||||||
@@ -597,10 +613,14 @@ TEST_F(WVDrmPluginTest, HandlesProvisioningResponses) {
|
|||||||
response.appendArray(responseRaw, kResponseSize);
|
response.appendArray(responseRaw, kResponseSize);
|
||||||
|
|
||||||
EXPECT_CALL(cdm, HandleProvisioningResponse(ElementsAreArray(responseRaw,
|
EXPECT_CALL(cdm, HandleProvisioningResponse(ElementsAreArray(responseRaw,
|
||||||
kResponseSize)))
|
kResponseSize),
|
||||||
|
_, _))
|
||||||
.Times(1);
|
.Times(1);
|
||||||
|
|
||||||
status_t res = plugin.provideProvisionResponse(response);
|
Vector<uint8_t> cert;
|
||||||
|
Vector<uint8_t> key;
|
||||||
|
|
||||||
|
status_t res = plugin.provideProvisionResponse(response, cert, key);
|
||||||
|
|
||||||
ASSERT_EQ(OK, res);
|
ASSERT_EQ(OK, res);
|
||||||
}
|
}
|
||||||
|
|||||||
14
libwvdrmengine/test/castv2/Android.mk
Normal file
14
libwvdrmengine/test/castv2/Android.mk
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
LOCAL_PATH:= $(call my-dir)
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_MODULE_TAGS := optional
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||||
|
|
||||||
|
LOCAL_STATIC_JAVA_LIBRARIES := com.android.mediadrm.signer
|
||||||
|
|
||||||
|
LOCAL_PACKAGE_NAME := CastSignAPITest
|
||||||
|
|
||||||
|
include $(BUILD_PACKAGE)
|
||||||
|
|
||||||
|
include $(call all-makefiles-under,$(LOCAL_PATH))
|
||||||
22
libwvdrmengine/test/castv2/AndroidManifest.xml
Normal file
22
libwvdrmengine/test/castv2/AndroidManifest.xml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.android.castv2.test"
|
||||||
|
>
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-library android:name="com.android.mediadrm.signer"
|
||||||
|
android:required="true" />
|
||||||
|
|
||||||
|
<uses-sdk android:minSdkVersion="18"></uses-sdk>
|
||||||
|
<application android:icon="@drawable/icon" android:label="@string/app_name" android:theme="@android:style/Theme.Holo.NoActionBar">
|
||||||
|
<activity android:name="CastSignAPITest"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:screenOrientation="landscape">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
2
libwvdrmengine/test/castv2/default.properties
Normal file
2
libwvdrmengine/test/castv2/default.properties
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Project target.
|
||||||
|
target=android-IceCreamSandwich
|
||||||
BIN
libwvdrmengine/test/castv2/res/drawable-hdpi/icon.png
Normal file
BIN
libwvdrmengine/test/castv2/res/drawable-hdpi/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
BIN
libwvdrmengine/test/castv2/res/drawable-ldpi/icon.png
Normal file
BIN
libwvdrmengine/test/castv2/res/drawable-ldpi/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
BIN
libwvdrmengine/test/castv2/res/drawable-mdpi/icon.png
Normal file
BIN
libwvdrmengine/test/castv2/res/drawable-mdpi/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
6
libwvdrmengine/test/castv2/res/layout/main.xml
Normal file
6
libwvdrmengine/test/castv2/res/layout/main.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
</LinearLayout>
|
||||||
4
libwvdrmengine/test/castv2/res/values/strings.xml
Normal file
4
libwvdrmengine/test/castv2/res/values/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">CastSignAPI Test</string>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
package com.android.castv2.test;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.SurfaceView;
|
||||||
|
import android.view.SurfaceHolder;
|
||||||
|
import android.view.Surface;
|
||||||
|
import android.view.Display;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.media.MediaDrm;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.media.MediaDrm.CryptoSession;
|
||||||
|
import android.media.MediaDrmException;
|
||||||
|
import android.media.NotProvisionedException;
|
||||||
|
import android.media.MediaCrypto;
|
||||||
|
import android.media.MediaCodec;
|
||||||
|
import android.media.MediaCryptoException;
|
||||||
|
import android.media.MediaCodec.CryptoException;
|
||||||
|
import android.media.MediaCodecList;
|
||||||
|
import android.media.MediaCodec.CryptoInfo;
|
||||||
|
import android.media.MediaCodecInfo;
|
||||||
|
import android.media.MediaFormat;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.lang.Exception;
|
||||||
|
import java.lang.InterruptedException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.lang.Math;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.Signature;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import com.android.mediadrm.signer.MediaDrmSigner;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import dalvik.system.DexClassLoader;
|
||||||
|
|
||||||
|
public class CastSignAPITest extends Activity {
|
||||||
|
private final String TAG = "CastSignAPITest";
|
||||||
|
|
||||||
|
static final UUID kWidevineScheme = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL);
|
||||||
|
|
||||||
|
private boolean mTestFailed;
|
||||||
|
private TextView mTextView;
|
||||||
|
private String mDisplayText;
|
||||||
|
|
||||||
|
/** Called when the activity is first created. */
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
mTextView = new TextView(this);
|
||||||
|
|
||||||
|
mDisplayText = new String("Cast Signing API Test\n\n");
|
||||||
|
Display display = getWindowManager().getDefaultDisplay();
|
||||||
|
Point size = new Point();
|
||||||
|
display.getSize(size);
|
||||||
|
int width = size.x;
|
||||||
|
int height = size.y;
|
||||||
|
final int max_lines = 20;
|
||||||
|
int textSize = Math.min(width, height) / max_lines;
|
||||||
|
mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
|
||||||
|
setContentView(mTextView);
|
||||||
|
Log.i(TAG, "width=" + width + " height=" + height + " size=" + textSize);
|
||||||
|
|
||||||
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mTestFailed = false;
|
||||||
|
|
||||||
|
testCastSign();
|
||||||
|
|
||||||
|
if (mTestFailed) {
|
||||||
|
displayText("\nTEST FAILED!");
|
||||||
|
} else {
|
||||||
|
displayText("\nTEST PASSED!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw text on the surface view
|
||||||
|
private void displayText(String text) {
|
||||||
|
Log.i(TAG, text);
|
||||||
|
mDisplayText += text + "\n";
|
||||||
|
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
mTextView.setText(mDisplayText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] openSession(MediaDrm drm) {
|
||||||
|
byte[] sessionId = null;
|
||||||
|
boolean retryOpen;
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
retryOpen = false;
|
||||||
|
sessionId = drm.openSession();
|
||||||
|
} catch (NotProvisionedException e) {
|
||||||
|
Log.i(TAG, "Missing certificate, provisioning");
|
||||||
|
ProvisionRequester provisionRequester = new ProvisionRequester();
|
||||||
|
provisionRequester.doTransact(drm);
|
||||||
|
retryOpen = true;
|
||||||
|
}
|
||||||
|
} while (retryOpen);
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testCastSign() {
|
||||||
|
final byte[] kMessage = hex2ba("ee07514066c23c770a665719abf051e0" +
|
||||||
|
"f75a399578305eb2547ca67ecd2356ca" +
|
||||||
|
"7bc0f5dc1854533eb98ee0fae1107ad2" +
|
||||||
|
"a8707c671be10fcd4853990897b71378" +
|
||||||
|
"70e39957a1536b39132e438ba681810d" +
|
||||||
|
"ebc3f9fb38907a1066b09c63af9016a7" +
|
||||||
|
"57425b29ff7fdf53859ebdc1659bdc49" +
|
||||||
|
"f40a5cb5f597b0b8a836b424ad9c68a9");
|
||||||
|
|
||||||
|
final byte[] kDigest = hex2ba(// digest info header
|
||||||
|
"3021300906052b0e03021a05000414" +
|
||||||
|
// sha1 of kMessage
|
||||||
|
"d2662f893aaec72f3ca6decc2aa942f3949e8b21");
|
||||||
|
|
||||||
|
MediaDrm drm;
|
||||||
|
|
||||||
|
try {
|
||||||
|
drm = new MediaDrm(kWidevineScheme);
|
||||||
|
} catch (MediaDrmException e) {
|
||||||
|
Log.e(TAG, "Failed to create MediaDrm: ", e);
|
||||||
|
mTestFailed = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CertificateRequester certificateRequester = new CertificateRequester();
|
||||||
|
MediaDrmSigner.Certificate signerCert = certificateRequester.doTransact(drm);
|
||||||
|
if (signerCert == null) {
|
||||||
|
displayText("Failed to get certificate from server!");
|
||||||
|
mTestFailed = true;
|
||||||
|
} else {
|
||||||
|
byte[] sessionId = openSession(drm);
|
||||||
|
|
||||||
|
byte[] signature = MediaDrmSigner.signRSA(this, drm, sessionId, "PKCS1-BlockType1",
|
||||||
|
signerCert.getWrappedPrivateKey(),
|
||||||
|
kDigest);
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] certBuf = signerCert.getContent();
|
||||||
|
InputStream certStream = new ByteArrayInputStream(certBuf);
|
||||||
|
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
|
||||||
|
Certificate javaCert = certFactory.generateCertificate(certStream);
|
||||||
|
PublicKey pubkey = javaCert.getPublicKey();
|
||||||
|
Signature instance = Signature.getInstance("SHA1WithRSA");
|
||||||
|
instance.initVerify(pubkey);
|
||||||
|
instance.update(kMessage);
|
||||||
|
if (instance.verify(signature)) {
|
||||||
|
Log.d(TAG, "Signatures match, test passed!");
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Signature did not verify, test failed!");
|
||||||
|
mTestFailed = true;
|
||||||
|
}
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
Log.e(TAG, "Failed to parse certificate", e);
|
||||||
|
mTestFailed = true;
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
mTestFailed = true;
|
||||||
|
Log.e(TAG, "Failed to get signature instance", e);
|
||||||
|
} catch (InvalidKeyException e) {
|
||||||
|
mTestFailed = true;
|
||||||
|
Log.e(TAG, "Invalid public key", e);
|
||||||
|
} catch (SignatureException e) {
|
||||||
|
mTestFailed = true;
|
||||||
|
Log.e(TAG, "Failed to update", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
drm.closeSession(sessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] hex2ba(String s) {
|
||||||
|
int len = s.length();
|
||||||
|
byte[] data = new byte[len / 2];
|
||||||
|
for (int i = 0; i < len; i += 2) {
|
||||||
|
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
|
||||||
|
+ Character.digit(s.charAt(i+1), 16));
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
package com.android.castv2.test;
|
||||||
|
|
||||||
|
import android.media.MediaDrm;
|
||||||
|
import com.android.mediadrm.signer.MediaDrmSigner;
|
||||||
|
import android.media.DeniedByServerException;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.client.ClientProtocolException;
|
||||||
|
import org.apache.http.impl.client.DefaultHttpClient;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
|
||||||
|
public class CertificateRequester {
|
||||||
|
private final String TAG = "CertificateRequester";
|
||||||
|
|
||||||
|
public CertificateRequester() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public MediaDrmSigner.Certificate doTransact(MediaDrm drm) {
|
||||||
|
MediaDrmSigner.Certificate result = null;
|
||||||
|
MediaDrmSigner.CertificateRequest certificateRequest =
|
||||||
|
MediaDrmSigner.getCertificateRequest(drm, MediaDrmSigner.CERTIFICATE_TYPE_X509,
|
||||||
|
"test-certificate");
|
||||||
|
|
||||||
|
PostRequestTask postTask = new PostRequestTask(certificateRequest.getData());
|
||||||
|
Log.i(TAG, "Requesting certificate from server '" + certificateRequest.getDefaultUrl() + "'");
|
||||||
|
postTask.execute(certificateRequest.getDefaultUrl());
|
||||||
|
|
||||||
|
// wait for post task to complete
|
||||||
|
byte[] responseBody;
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
do {
|
||||||
|
responseBody = postTask.getResponseBody();
|
||||||
|
if (responseBody == null) {
|
||||||
|
sleep(100);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (System.currentTimeMillis() - startTime < 5000);
|
||||||
|
|
||||||
|
if (responseBody == null) {
|
||||||
|
Log.e(TAG, "No response from certificate server!");
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
result = MediaDrmSigner.provideCertificateResponse(drm, responseBody);
|
||||||
|
} catch (DeniedByServerException e) {
|
||||||
|
Log.e(TAG, "Server denied certificate request");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PostRequestTask extends AsyncTask<String, Void, Void> {
|
||||||
|
private final String TAG = "PostRequestTask";
|
||||||
|
|
||||||
|
private byte[] mCertificateRequest;
|
||||||
|
private byte[] mResponseBody;
|
||||||
|
|
||||||
|
public PostRequestTask(byte[] certificateRequest) {
|
||||||
|
mCertificateRequest = certificateRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Void doInBackground(String... urls) {
|
||||||
|
mResponseBody = postRequest(urls[0], mCertificateRequest);
|
||||||
|
if (mResponseBody != null) {
|
||||||
|
Log.d(TAG, "response length=" + mResponseBody.length);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getResponseBody() {
|
||||||
|
return mResponseBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] postRequest(String url, byte[] certificateRequest) {
|
||||||
|
HttpClient httpclient = new DefaultHttpClient();
|
||||||
|
HttpPost httppost = new HttpPost(url + "?signedRequest=" + new String(certificateRequest));
|
||||||
|
|
||||||
|
Log.d(TAG, "PostRequest:" + httppost.getRequestLine());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Add data
|
||||||
|
httppost.setHeader("Accept", "*/*");
|
||||||
|
httppost.setHeader("User-Agent", "Widevine CDM v1.0");
|
||||||
|
httppost.setHeader("Content-Type", "application/json");
|
||||||
|
|
||||||
|
// Execute HTTP Post Request
|
||||||
|
HttpResponse response = httpclient.execute(httppost);
|
||||||
|
|
||||||
|
byte[] responseBody;
|
||||||
|
int responseCode = response.getStatusLine().getStatusCode();
|
||||||
|
if (responseCode == 200) {
|
||||||
|
responseBody = EntityUtils.toByteArray(response.getEntity());
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Server returned HTTP error code " + responseCode);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return responseBody;
|
||||||
|
|
||||||
|
} catch (ClientProtocolException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sleep(int msec) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(msec);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
package com.android.castv2.test;
|
||||||
|
|
||||||
|
import android.media.MediaDrm;
|
||||||
|
import android.media.DeniedByServerException;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.client.ClientProtocolException;
|
||||||
|
import org.apache.http.impl.client.DefaultHttpClient;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class ProvisionRequester {
|
||||||
|
private final String TAG = "ProvisionRequester";
|
||||||
|
|
||||||
|
public ProvisionRequester() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doTransact(MediaDrm drm) {
|
||||||
|
MediaDrm.ProvisionRequest drmRequest;
|
||||||
|
drmRequest = drm.getProvisionRequest();
|
||||||
|
|
||||||
|
PostRequestTask postTask = new PostRequestTask(drmRequest.getData());
|
||||||
|
Log.i(TAG, "Attempting to provision from server '" + drmRequest.getDefaultUrl() + "'");
|
||||||
|
postTask.execute(drmRequest.getDefaultUrl());
|
||||||
|
|
||||||
|
// wait for post task to complete
|
||||||
|
byte[] responseBody;
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
do {
|
||||||
|
responseBody = postTask.getResponseBody();
|
||||||
|
if (responseBody == null) {
|
||||||
|
sleep(100);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (System.currentTimeMillis() - startTime < 5000);
|
||||||
|
|
||||||
|
if (responseBody == null) {
|
||||||
|
Log.e(TAG, "No response from provisioning server!");
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
drm.provideProvisionResponse(responseBody);
|
||||||
|
} catch (DeniedByServerException e) {
|
||||||
|
Log.e(TAG, "Server denied provisioning request");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PostRequestTask extends AsyncTask<String, Void, Void> {
|
||||||
|
private final String TAG = "PostRequestTask";
|
||||||
|
|
||||||
|
private byte[] mDrmRequest;
|
||||||
|
private byte[] mResponseBody;
|
||||||
|
|
||||||
|
public PostRequestTask(byte[] drmRequest) {
|
||||||
|
mDrmRequest = drmRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Void doInBackground(String... urls) {
|
||||||
|
mResponseBody = postRequest(urls[0], mDrmRequest);
|
||||||
|
if (mResponseBody != null) {
|
||||||
|
Log.d(TAG, "response length=" + mResponseBody.length);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getResponseBody() {
|
||||||
|
return mResponseBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] postRequest(String url, byte[] drmRequest) {
|
||||||
|
HttpClient httpclient = new DefaultHttpClient();
|
||||||
|
HttpPost httppost = new HttpPost(url + "?signedRequest=" + new String(drmRequest));
|
||||||
|
|
||||||
|
Log.d(TAG, "PostRequest:" + httppost.getRequestLine());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Add data
|
||||||
|
httppost.setHeader("Accept", "*/*");
|
||||||
|
httppost.setHeader("User-Agent", "Widevine CDM v1.0");
|
||||||
|
httppost.setHeader("Content-Type", "application/json");
|
||||||
|
|
||||||
|
// Execute HTTP Post Request
|
||||||
|
HttpResponse response = httpclient.execute(httppost);
|
||||||
|
|
||||||
|
byte[] responseBody;
|
||||||
|
int responseCode = response.getStatusLine().getStatusCode();
|
||||||
|
if (responseCode == 200) {
|
||||||
|
responseBody = EntityUtils.toByteArray(response.getEntity());
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Server returned HTTP error code " + responseCode);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return responseBody;
|
||||||
|
|
||||||
|
} catch (ClientProtocolException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sleep(int msec) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(msec);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user