From e61259e075f8e0821e812ed3eb1fc3cb302493b8 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Mon, 17 Jul 2017 02:40:50 -0700 Subject: [PATCH] Support provisioning 3.0 [ Merge of http://go/wvgerrit/29004 ] Enable support for provisioning with OEM certificates as root of trust. b/62972441 Test: WV unit/intgration test, cdm_feature_test and GTSMediaTestCases Change-Id: I30576fc0bb68a873eeaaca03f6b9c89fa6a14327 --- .../build_and_run_all_unit_tests.sh | 1 + .../core/include/certificate_provisioning.h | 2 +- .../cdm/core/include/crypto_session.h | 25 +- .../cdm/core/src/certificate_provisioning.cpp | 45 +-- .../cdm/core/src/crypto_session.cpp | 104 +++++- .../cdm/core/test/cdm_engine_test.cpp | 2 +- .../cdm/core/test/config_test_env.cpp | 67 +++- .../cdm/core/test/config_test_env.h | 15 +- libwvdrmengine/cdm/test/Android.mk | 4 + .../cdm/test/cdm_extended_duration_test.cpp | 2 +- libwvdrmengine/cdm/test/cdm_feature_test.cpp | 295 ++++++++++++++++++ .../cdm/test/request_license_test.cpp | 2 +- libwvdrmengine/run_all_unit_tests.sh | 3 + 13 files changed, 506 insertions(+), 61 deletions(-) create mode 100644 libwvdrmengine/cdm/test/cdm_feature_test.cpp diff --git a/libwvdrmengine/build_and_run_all_unit_tests.sh b/libwvdrmengine/build_and_run_all_unit_tests.sh index 965b98d3..ec4273f1 100755 --- a/libwvdrmengine/build_and_run_all_unit_tests.sh +++ b/libwvdrmengine/build_and_run_all_unit_tests.sh @@ -78,6 +78,7 @@ try_adb_push() { try_adb_push base64_test try_adb_push buffer_reader_test try_adb_push cdm_engine_test +try_adb_push cdm_feature_test try_adb_push cdm_extended_duration_test try_adb_push cdm_session_unittest try_adb_push device_files_unittest diff --git a/libwvdrmengine/cdm/core/include/certificate_provisioning.h b/libwvdrmengine/cdm/core/include/certificate_provisioning.h index d54d859c..8ccfca6e 100644 --- a/libwvdrmengine/cdm/core/include/certificate_provisioning.h +++ b/libwvdrmengine/cdm/core/include/certificate_provisioning.h @@ -42,7 +42,7 @@ class CertificateProvisioning { bool GetProvisioningTokenType( video_widevine::ClientIdentification::TokenType* token_type); - bool FillStableIdField(const std::string& origin, const std::string& spoid, + bool SetSpoidParameter(const std::string& origin, const std::string& spoid, video_widevine::ProvisioningRequest* request); video_widevine::SignedProvisioningMessage::ProtocolVersion diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index 17029e55..2e0fb056 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -83,12 +83,13 @@ class CryptoSession { virtual bool GenerateDerivedKeys(const std::string& message); virtual bool GenerateDerivedKeys(const std::string& message, const std::string& session_key); - virtual bool RewrapDeviceRSAKey(const std::string& message, - const std::string& signature, - const std::string& nonce, - const std::string& enc_rsa_key, - const std::string& rsa_key_iv, - std::string* wrapped_rsa_key); + virtual bool RewrapCertificate(const std::string& signed_message, + const std::string& signature, + const std::string& nonce, + const std::string& private_key, + const std::string& iv, + const std::string& wrapping_key, + std::string* wrapped_private_key); // Media data path virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters); @@ -181,6 +182,17 @@ class CryptoSession { bool GenerateRsaSignature(const std::string& message, std::string* signature); size_t GetOffset(std::string message, std::string field); bool SetDestinationBufferType(); + + bool RewrapDeviceRSAKey( + const std::string& message, const std::string& signature, + const std::string& nonce, const std::string& enc_rsa_key, + const std::string& rsa_key_iv, std::string* wrapped_rsa_key); + + bool RewrapDeviceRSAKey30( + const std::string& message, const std::string& nonce, + const std::string& private_key, const std::string& iv, + const std::string& wrapping_key, std::string* wrapped_private_key); + CdmResponseType SelectKey(const std::string& key_id); static const OEMCrypto_Algorithm kInvalidAlgorithm = @@ -214,6 +226,7 @@ class CryptoSession { bool open_; CdmClientTokenType pre_provision_token_type_; + std::string oem_token_; // Cached OEMCrypto Public Key bool update_usage_table_after_close_session_; CryptoSessionId oec_session_id_; diff --git a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp index e32b8f63..0deb8bdc 100644 --- a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp +++ b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp @@ -55,7 +55,8 @@ void CertificateProvisioning::ComposeJsonRequestAsQueryString( */ bool CertificateProvisioning::GetProvisioningTokenType( ClientIdentification::TokenType* token_type) { - switch (crypto_session_.GetPreProvisionTokenType()) { + CdmClientTokenType token = crypto_session_.GetPreProvisionTokenType(); + switch (token) { case kClientTokenKeybox: *token_type = ClientIdentification::KEYBOX; return true; @@ -65,22 +66,23 @@ bool CertificateProvisioning::GetProvisioningTokenType( case kClientTokenDrmCert: default: // shouldn't happen + LOGE("CertificateProvisioning::GetProvisioningTokenType: unexpected " + "provisioning type: %d", token); return false; } } /* - * Fill in the appropriate field relating to stable IDs in the provisioning - * request, no more than one of |stable_id|, |provider_id|, and |spoid|. It is - * also valid (though deprecated) to fill in none of these in order to leave the - * stable ID behavior up to the provisioning server. + * Fill in the appropriate SPOID (Stable Per-Origin IDentifier) option. + * One of spoid, provider_id or stable_id will be passed to the provisioning + * server for determining a unique per origin ID for the device. + * It is also valid (though deprecated) to leave the settings unset. */ -bool CertificateProvisioning::FillStableIdField( - const std::string& origin, - const std::string& spoid, +bool CertificateProvisioning::SetSpoidParameter( + const std::string& origin, const std::string& spoid, ProvisioningRequest* request) { if (!request) { - LOGE("CertificateProvisioning::FillStableIdField : No request buffer " + LOGE("CertificateProvisioning::SetSpoidParameter: No request buffer " "passed to method."); return false; } @@ -160,21 +162,6 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest( client_id->set_token(token); client_id->set_type(token_type); -#if 0 // TODO(gmorgan) in progress - encrypt ClientIdentification. - if (encrypt) { - EncryptedClientIdentification* encrypted_client_id = - provisioning_request->mutable_encrypted_client_id(); - CdmResponseType sts; - sts = EncryptClientId(client_id, encrypted_client_id, certificate); - if (NO_ERROR == sts) { - provisioning_request->clear_client_id(); - } else { - provisioning_request->clear_encrypted_client_id(); - } - return sts; - } -#endif - uint32_t nonce; if (!crypto_session_.GenerateNonce(&nonce)) { LOGE("GetProvisioningRequest: fails to generate a nonce"); @@ -204,7 +191,7 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest( cert_type_ = cert_type; options->set_certificate_authority(cert_authority); - if (!FillStableIdField(origin, spoid, &provisioning_request)) { + if (!SetSpoidParameter(origin, spoid, &provisioning_request)) { return CERT_PROVISIONING_GET_KEYBOX_ERROR_2; } @@ -326,11 +313,13 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse( const std::string& enc_rsa_key = provisioning_response.device_rsa_key(); const std::string& nonce = provisioning_response.nonce(); const std::string& rsa_key_iv = provisioning_response.device_rsa_key_iv(); + const std::string& wrapping_key = (provisioning_response.has_wrapping_key()) ? + provisioning_response.wrapping_key() : std::string(); const std::string& signature = signed_response.signature(); std::string wrapped_rsa_key; - if (!crypto_session_.RewrapDeviceRSAKey(signed_message, signature, nonce, - enc_rsa_key, rsa_key_iv, - &wrapped_rsa_key)) { + if (!crypto_session_.RewrapCertificate(signed_message, signature, nonce, + enc_rsa_key, rsa_key_iv, wrapping_key, + &wrapped_rsa_key)) { LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails"); return CERT_PROVISIONING_RESPONSE_ERROR_6; } diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index f66a0dab..927e0bed 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -162,7 +162,15 @@ bool CryptoSession::GetTokenFromKeybox(std::string* token) { } bool CryptoSession::GetTokenFromOemCert(std::string* token) { + if (token == NULL) { + LOGE("CryptoSession::GetTokenFromOemCert: token not provided "); + return false; + } OEMCryptoResult status; + if (!oem_token_.empty()) { + token->assign(oem_token_); + return true; + } std::string temp_buffer(CERTIFICATE_DATA_SIZE, '\0'); // lock is held by caller bool retrying = false; @@ -171,7 +179,9 @@ bool CryptoSession::GetTokenFromOemCert(std::string* token) { uint8_t* buf = reinterpret_cast(&temp_buffer[0]); status = OEMCrypto_GetOEMPublicCertificate(oec_session_id_, buf, &buf_size); if (OEMCrypto_SUCCESS == status) { - token->swap(temp_buffer); + temp_buffer.resize(buf_size); + oem_token_.assign(temp_buffer); + token->assign(temp_buffer); return true; } if (OEMCrypto_ERROR_SHORT_BUFFER && !retrying) { @@ -568,8 +578,8 @@ bool CryptoSession::PrepareRequest(const std::string& message, return false; } - // TODO(gmorgan): rework this for OEM certs. - if (!Properties::use_certificates_as_identification() || is_provisioning) { + if (!Properties::use_certificates_as_identification() || + (is_provisioning && (pre_provision_token_type_ == kClientTokenKeybox))) { if (!GenerateDerivedKeys(message)) return false; if (!GenerateSignature(message, signature)) return false; @@ -739,8 +749,15 @@ bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) { LOGV("CryptoSession::LoadCertificatePrivateKey: Lock"); AutoLock auto_lock(crypto_lock_); + // Call OEMCrypto_GetOEMPublicCertificate before OEMCrypto_LoadDeviceRSAKey + // so it caches the OEMCrypto Public Key and then throw away result + std::string temp_buffer(CERTIFICATE_DATA_SIZE, '\0'); + size_t buf_size = temp_buffer.size(); + uint8_t* buf = reinterpret_cast(&temp_buffer[0]); + OEMCryptoResult sts = + OEMCrypto_GetOEMPublicCertificate(oec_session_id_, buf, &buf_size); + LOGV("LoadDeviceRSAKey: id=%ld", (uint32_t)oec_session_id_); - OEMCryptoResult sts; M_TIME( sts = OEMCrypto_LoadDeviceRSAKey( oec_session_id_, @@ -1458,6 +1475,34 @@ bool CryptoSession::SetDestinationBufferType() { return true; } +bool CryptoSession::RewrapCertificate(const std::string& signed_message, + const std::string& signature, + const std::string& nonce, + const std::string& private_key, + const std::string& iv, + const std::string& wrapping_key, + std::string* wrapped_private_key) { + LOGV("CryptoSession::RewrapCertificate, session id=%ld", + static_cast(oec_session_id_)); + + if (pre_provision_token_type_ == kClientTokenKeybox) { + + return RewrapDeviceRSAKey(signed_message, signature, nonce, private_key, + iv, wrapped_private_key); + + } else if (pre_provision_token_type_ == kClientTokenOemCert) { + + return RewrapDeviceRSAKey30(signed_message, nonce, private_key, iv, + wrapping_key, wrapped_private_key); + + } else { + LOGE("CryptoSession::RewrapCertificate, Bad pre-provision type=%d: " + "session id=%ld", pre_provision_token_type_, + static_cast(oec_session_id_)); + return false; + } +} + bool CryptoSession::RewrapDeviceRSAKey(const std::string& message, const std::string& signature, const std::string& nonce, @@ -1531,6 +1576,57 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message, return true; } +bool CryptoSession::RewrapDeviceRSAKey30(const std::string& message, + const std::string& nonce, + const std::string& private_key, + const std::string& iv, + const std::string& wrapping_key, + std::string* wrapped_private_key) { + LOGV("CryptoSession::RewrapDeviceRSAKey30, session id=%ld", + static_cast(oec_session_id_)); + + const uint8_t* signed_msg = reinterpret_cast(message.data()); + const uint8_t* msg_private_key = NULL; + const uint8_t* msg_iv = NULL; + const uint32_t* msg_nonce = NULL; + const uint8_t* msg_wrapping_key = NULL; + if (private_key.size() >= MAC_KEY_SIZE && iv.size() >= KEY_IV_SIZE) { + msg_private_key = signed_msg + GetOffset(message, private_key); + msg_iv = signed_msg + GetOffset(message, iv); + msg_nonce = reinterpret_cast(signed_msg + + GetOffset(message, nonce)); + msg_wrapping_key = signed_msg + GetOffset(message, wrapping_key); + } + + // Gets wrapped_rsa_key_length by passing NULL as uint8_t* wrapped_rsa_key + // and 0 as wrapped_rsa_key_length. + size_t wrapped_private_key_length = 0; + OEMCryptoResult status = OEMCrypto_RewrapDeviceRSAKey30( + oec_session_id_, msg_nonce, msg_wrapping_key, wrapping_key.size(), + msg_private_key, private_key.size(), msg_iv, NULL, + &wrapped_private_key_length); + + if (status != OEMCrypto_ERROR_SHORT_BUFFER) { + LOGE("OEMCrypto_RewrapDeviceRSAKey30 failed getting wrapped key length"); + return false; + } + + wrapped_private_key->resize(wrapped_private_key_length); + status = OEMCrypto_RewrapDeviceRSAKey30( + oec_session_id_, msg_nonce, msg_wrapping_key, wrapping_key.size(), + msg_private_key, private_key.size(), msg_iv, + reinterpret_cast(&(*wrapped_private_key)[0]), + &wrapped_private_key_length); + + wrapped_private_key->resize(wrapped_private_key_length); + + if (OEMCrypto_SUCCESS != status) { + LOGE("OEMCrypto_RewrapDeviceRSAKey fails with %d", status); + return false; + } + return true; +} + bool CryptoSession::GetHdcpCapabilities(HdcpCapability* current, HdcpCapability* max) { LOGV("GetHdcpCapabilities: id=%ld", (uint32_t)oec_session_id_); diff --git a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp index c73aaeef..5fac4718 100644 --- a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp @@ -51,7 +51,7 @@ class WvCdmEngineTest : public testing::Test { g_client_auth.assign(config.client_auth()); g_key_system.assign(config.key_system()); g_wrong_key_id.assign(config.wrong_key_id()); - g_license_server.assign(config.license_server()); + g_license_server.assign(config.license_server_url()); g_key_id_pssh.assign(a2bs_hex(config.key_id())); // Extract the key ID from the PSSH box. diff --git a/libwvdrmengine/cdm/core/test/config_test_env.cpp b/libwvdrmengine/cdm/core/test/config_test_env.cpp index 4af0b2f1..1f034626 100644 --- a/libwvdrmengine/cdm/core/test/config_test_env.cpp +++ b/libwvdrmengine/cdm/core/test/config_test_env.cpp @@ -11,6 +11,10 @@ const std::string kWidevineKeySystem = "com.widevine.alpha"; // Content Protection license server (UAT) data const std::string kCpUatLicenseServer = "http://widevine-proxy.appspot.com/proxy"; +const std::string kUatProvisioningServerUrl = + "https://staging-www.sandbox.googleapis.com/" + "certificateprovisioning/v1/devicecertificates/create" + "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; const std::string kCpClientAuth = ""; const std::string kCpKeyId = "00000042" // blob size @@ -53,9 +57,46 @@ const std::string kCpUatServiceCertificate = "7C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A250A4EB9C84AB3E6539F6B6FDF" "56899EA29914"; +const std::string kCpProductionLicenseServer = + "https://widevine-proxy.appspot.com/proxy"; +const std::string kProductionProvisioningServerUrl = + "https://www.googleapis.com/" + "certificateprovisioning/v1/devicecertificates/create" + "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; +// NOTE: Provider ID = staging.google.com +const std::string kCpProductionServiceCertificate = + "0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F9900522" + "8E023082010A0282010100B52112B8D05D023FCC5D95E2C251C1C649B417" + "7CD8D2BEEF355BB06743DE661E3D2ABC3182B79946D55FDC08DFE9540781" + "5E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94B2516F075B66EF811D" + "0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1EF9B6" + "AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A0" + "40C50B09BBC740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A" + "0E498CC01F00532BAC217850BD905E90923656B7DFEFEF42486767F33EF6" + "283D4F4254AB72589390BEE55808F1D668080D45D893C2BCA2F74D60A0C0" + "D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD943020301" + "00013A1273746167696E672E676F6F676C652E636F6D128003983E303526" + "75F40BA715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AA" + "EFC5E27BC980DAEADABF3FC386D084A02C82537848CC753FF497B011A7DA" + "97788A00E2AA6B84CD7D71C07A48EBF61602CCA5A3F32030A7295C30DA91" + "5B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE18FA82E81BB0" + "32630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0" + "EFD45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F28" + "8F0D9D45960E259E85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F86932" + "1F6ADE18905F4D92F9A6DA6536DB8475871D168E870BB2303CF70C6E9784" + "C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D2592C72429F8C01742" + "BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F3940" + "383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D8" + "38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A" + "250A4EB9C84AB3E6539F6B6FDF56899EA29914"; + // Content Protection license server (staging) data const std::string kCpStagingLicenseServer = "http://wv-staging-proxy.appspot.com/proxy"; +const std::string kStagingProvisioningServerUrl = + "https://staging-www.sandbox.googleapis.com/" + "certificateprovisioning/v1/devicecertificates/create" + "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; const std::string kCpStagingServiceCertificate = "0AC102080312101705B917CC1204868B06333A2F772A8C1882B4829205228E023082010A02" "8201010099ED5B3B327DAB5E24EFC3B62A95B598520AD5BCCB37503E0645B814D876B8DF40" @@ -116,19 +157,17 @@ const std::string kWrongKeyId = "0901121094889920e8d6520098577df8" "f2dd5546"; -// URL of provisioning server (returned by GetProvisioningRequest()) -const std::string kProductionProvisioningServerUrl = - "https://www.googleapis.com/" - "certificateprovisioning/v1/devicecertificates/create" - "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; - const ConfigTestEnv::LicenseServerConfiguration license_servers[] = { - {kGooglePlayServer, kGpLicenseServer, kGpClientAuth, kGpKeyId, - kGpOfflineKeyId, ""}, - {kContentProtectionUatServer, kCpUatLicenseServer, kCpClientAuth, - kCpKeyId, kCpOfflineKeyId, kCpUatServiceCertificate}, + {kGooglePlayServer, kGpLicenseServer, "", kGpClientAuth, kGpKeyId, + kGpOfflineKeyId, kProductionProvisioningServerUrl}, + {kContentProtectionProductionServer, kCpProductionLicenseServer, + kCpProductionServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId, + kProductionProvisioningServerUrl}, + {kContentProtectionUatServer, kCpUatLicenseServer, kCpUatServiceCertificate, + kCpClientAuth, kCpKeyId, kCpOfflineKeyId, kUatProvisioningServerUrl}, {kContentProtectionStagingServer, kCpStagingLicenseServer, - kCpClientAuth, kCpKeyId, kCpOfflineKeyId, kCpStagingServiceCertificate}, + kCpStagingServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId, + kStagingProvisioningServerUrl}, }; } // namespace @@ -162,9 +201,9 @@ void ConfigTestEnv::Init(LicenseServerId server_id) { client_auth_ = license_servers[server_id].client_tag; key_id_ = license_servers[server_id].key_id; key_system_ = kWidevineKeySystem; - license_server_ = license_servers[server_id].url; - provisioning_server_url_ = kProductionProvisioningServerUrl; - service_certificate_ = license_servers[server_id].service_certificate; + license_server_url_ = license_servers[server_id].license_server_url; + provisioning_server_url_ = license_servers[server_id].provisioning_server_url; + service_certificate_ = license_servers[server_id].license_service_certificate; wrong_key_id_ = kWrongKeyId; } diff --git a/libwvdrmengine/cdm/core/test/config_test_env.h b/libwvdrmengine/cdm/core/test/config_test_env.h index 6e02425a..0fa6f034 100644 --- a/libwvdrmengine/cdm/core/test/config_test_env.h +++ b/libwvdrmengine/cdm/core/test/config_test_env.h @@ -11,6 +11,7 @@ typedef enum { kGooglePlayServer, kContentProtectionUatServer, kContentProtectionStagingServer, + kContentProtectionProductionServer, } LicenseServerId; // Configures default test environment. @@ -18,11 +19,12 @@ class ConfigTestEnv { public: typedef struct { LicenseServerId id; - std::string url; + std::string license_server_url; + std::string license_service_certificate; std::string client_tag; std::string key_id; std::string offline_key_id; - std::string service_certificate; + std::string provisioning_server_url; } LicenseServerConfiguration; explicit ConfigTestEnv(LicenseServerId server_id); @@ -34,7 +36,7 @@ class ConfigTestEnv { const std::string& client_auth() const { return client_auth_; } const KeyId& key_id() const { return key_id_; } const CdmKeySystem& key_system() const { return key_system_; } - const std::string& license_server() const { return license_server_; } + const std::string& license_server_url() const { return license_server_url_; } const std::string& provisioning_server_url() const { return provisioning_server_url_; } @@ -48,7 +50,10 @@ class ConfigTestEnv { key_system_.assign(key_system); } void set_license_server(std::string& license_server) { - license_server_.assign(license_server); + license_server_url_.assign(license_server); + } + void set_provisioning_server(std::string& provisioning_server) { + provisioning_server_url_.assign(provisioning_server); } private: @@ -57,7 +62,7 @@ class ConfigTestEnv { std::string client_auth_; KeyId key_id_; CdmKeySystem key_system_; - std::string license_server_; + std::string license_server_url_; std::string provisioning_server_url_; std::string service_certificate_; KeyId wrong_key_id_; diff --git a/libwvdrmengine/cdm/test/Android.mk b/libwvdrmengine/cdm/test/Android.mk index 6944bc1b..d27b48b7 100644 --- a/libwvdrmengine/cdm/test/Android.mk +++ b/libwvdrmengine/cdm/test/Android.mk @@ -11,6 +11,10 @@ test_name := buffer_reader_test test_src_dir := ../core/test include $(LOCAL_PATH)/unit-test.mk +test_name := cdm_feature_test +test_src_dir := . +include $(LOCAL_PATH)/unit-test.mk + test_name := cdm_engine_test test_src_dir := ../core/test include $(LOCAL_PATH)/unit-test.mk diff --git a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp index 8f8976ef..4824a970 100644 --- a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp +++ b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp @@ -1641,7 +1641,7 @@ int main(int argc, char** argv) { g_key_id.assign(g_config->key_id()); } if (g_license_server.empty()) { - g_license_server.assign(g_config->license_server()); + g_license_server.assign(g_config->license_server_url()); } // Displays server url, port and key Id being used diff --git a/libwvdrmengine/cdm/test/cdm_feature_test.cpp b/libwvdrmengine/cdm/test/cdm_feature_test.cpp new file mode 100644 index 00000000..384f073b --- /dev/null +++ b/libwvdrmengine/cdm/test/cdm_feature_test.cpp @@ -0,0 +1,295 @@ +// Copyright 2017 Google Inc. All Rights Reserved. + +// These tests validate features supported by the CDM that may not be +// supported by the OEM supplied L1 OEMCrypto implementation. These test use +// the modifiable/mock OEMCrypto. OEMCrypto configuration flags +// are enabled/disabled when validating features in tests. +// +// Instructions to build and use the modifiable OEMCrypto are here +// https://docs.google.com/a/google.com/document/d/1hLK7ThUsBBKkokPhKh8urU1Zo9a8U4dX3bBKDgOU_iY/edit?usp=sharing +// +// Use the install script to backup the OEM supplied L1 OEMCrypto and +// replace with the modifiable/mock OEMCrypto. Use default configuration +// amd modify flags as mentioned by the tests. + +#include +#include +#include + +#include +#include +#include + +#include "config_test_env.h" +#include "log.h" +#include "oemcrypto_adapter.h" +#include "OEMCryptoCENC.h" +#include "string_conversions.h" +#include "test_base.h" +#include "test_printers.h" +#include "url_request.h" +#include "wv_cdm_constants.h" +#include "wv_content_decryption_module.h" + +using ::testing::_; + +namespace { + +#define N_ELEM(a) (sizeof(a)/sizeof(a[0])) + +const char kPathDelimiter = '/'; + +// HTTP response codes. +const int kHttpOk = 200; +const int kHttpBadRequest = 400; +const int kHttpInternalServerError = 500; + + +// Default license server, can be configured using --server command line option +// Default key id (pssh), can be configured using --keyid command line option +std::string g_client_auth; +wvcdm::ConfigTestEnv* g_config = NULL; +wvcdm::KeyId g_key_id; +wvcdm::CdmKeySystem g_key_system; +std::string g_license_server; +wvcdm::KeyId g_wrong_key_id; +wvcdm::LicenseServerId g_license_server_id = wvcdm::kContentProtectionUatServer; +std::string g_service_certificate; + +} // namespace + +namespace wvcdm { +// Protobuf generated classes + +class WvCdmFeatureTest : public WvCdmTestBase { + public: + WvCdmFeatureTest() {} + ~WvCdmFeatureTest() {} + + void LogResponseError(const std::string& message, int http_status_code) { + LOGD("HTTP Status code = %d", http_status_code); + LOGD("HTTP response(%d): %s", message.size(), b2a_hex(message).c_str()); + } + + // Post a request and extract the signed provisioning message from + // the HTTP response. + std::string GetCertRequestResponse(const std::string& server_url) { + // Use secure connection and chunk transfer coding. + UrlRequest url_request(server_url); + EXPECT_TRUE(url_request.is_connected()) << "Fail to connect to " + << server_url; + url_request.PostCertRequestInQueryString(key_msg_); + std::string message; + EXPECT_TRUE(url_request.GetResponse(&message)); + + int http_status_code = url_request.GetStatusCode(message); + if (kHttpOk != http_status_code) { + LogResponseError(message, http_status_code); + } + EXPECT_EQ(kHttpOk, http_status_code); + return message; + } + + protected: + wvcdm::WvContentDecryptionModule decryptor_; + CdmKeyMessage key_msg_; + CdmSessionId session_id_; +}; + +// To run this test set options, +// use_keybox 0 +TEST_F(WvCdmFeatureTest, OEMCertificateProvisioning) { + decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, + &session_id_); + std::string provisioning_server_url; + CdmCertificateType cert_type = kCertificateWidevine; + std::string cert_authority, cert, wrapped_key; + + EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest( + cert_type, cert_authority, + kDefaultCdmIdentifier, &key_msg_, + &provisioning_server_url)); + EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url()); + + provisioning_server_url = + "https://staging-www.sandbox.googleapis.com/" + "certificateprovisioning/v1/devicecertificates/create" + "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; + + std::string response = + GetCertRequestResponse(provisioning_server_url); + //GetCertRequestResponse(g_config->provisioning_server_url()); + EXPECT_NE(0, static_cast(response.size())); + EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse( + kDefaultCdmIdentifier, response, &cert, + &wrapped_key)); + EXPECT_EQ(0, static_cast(cert.size())); + EXPECT_EQ(0, static_cast(wrapped_key.size())); + decryptor_.CloseSession(session_id_); +} + +// To run this test set options, +// use_keybox 1 +TEST_F(WvCdmFeatureTest, KeyboxProvisioning) { + decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, + &session_id_); + std::string provisioning_server_url; + CdmCertificateType cert_type = kCertificateWidevine; + std::string cert_authority, cert, wrapped_key; + + EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest( + cert_type, cert_authority, + kDefaultCdmIdentifier, &key_msg_, + &provisioning_server_url)); + EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url()); + + provisioning_server_url = + "https://staging-www.sandbox.googleapis.com/" + "certificateprovisioning/v1/devicecertificates/create" + "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; + + std::string response = + GetCertRequestResponse(provisioning_server_url); + //GetCertRequestResponse(g_config->provisioning_server_url()); + EXPECT_NE(0, static_cast(response.size())); + EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse( + kDefaultCdmIdentifier, response, &cert, + &wrapped_key)); + EXPECT_EQ(0, static_cast(cert.size())); + EXPECT_EQ(0, static_cast(wrapped_key.size())); + decryptor_.CloseSession(session_id_); +} + +} // namespace wvcdm + +void show_menu(char* prog_name) { + std::cout << std::endl; + std::cout << "usage: " << prog_name << " [options]" << std::endl << std::endl; + std::cout << " enclose multiple arguments in '' when using adb shell" + << std::endl; + std::cout << " e.g. adb shell '" << prog_name << " --server=\"url\"'" + << std::endl; + std::cout << " or adb shell '" << prog_name << " -u\"url\"'" << std::endl + << std::endl; + + std::cout << " -i/--license_server_id=" << std::endl; + std::cout << " specifies which default server settings to use: " + << std::endl; + std::cout << " gp for GooglePlay server" << std::endl; + std::cout << " cp for Content Protection UAT server" << std::endl; + std::cout << " st for Content Protection Staging server" << std::endl + << std::endl; + + std::cout << " -k/--keyid=" << std::endl; + std::cout << " configure the key id or pssh, in hex format" << std::endl + << std::endl; + + std::cout << " -s/--cert=" << std::endl; + std::cout << " configure the signed service certificate" << std::endl; + std::cout << " Specify the SignedDeviceCertificate (from " + << "device_certificate.proto) " << std::endl; + std::cout << " in hex format." << std::endl; + std::cout << " Due to the length of the argument use, " << std::endl; + std::cout << " echo \"/system/bin/request_license_test -s \\\"" + << "0ABF02...A29914\\\"\" \\" << std::endl; + std::cout << " > run_request_license_test.sh" << std::endl; + std::cout << " chmod +x run_request_license_test.sh" << std::endl; + std::cout << " adb push run_request_license_test.sh /system/bin" + << std::endl; + std::cout << " adb shell sh /system/bin/run_request_license_test.sh" + << std::endl + << std::endl; + + std::cout << " -u/--server=" << std::endl; + std::cout << " configure the license server url, please include http[s]" + << " in the url" << std::endl + << std::endl; +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + bool show_usage = false; + static const struct option long_options[] = { + {"keyid", required_argument, NULL, 'k'}, + {"license_server_id", required_argument, NULL, 'i'}, + {"service_certificate", required_argument, NULL, 's'}, + {"license_server_url", required_argument, NULL, 'u'}, + {NULL, 0, NULL, '\0'}}; + + int option_index = 0; + int opt = 0; + while ((opt = getopt_long(argc, argv, "i:k:s:u:", long_options, + &option_index)) != -1) { + switch (opt) { + case 'i': { + std::string license_id(optarg); + if (!license_id.compare("gp")) { + g_license_server_id = wvcdm::kGooglePlayServer; + } else if (!license_id.compare("cp")) { + g_license_server_id = wvcdm::kContentProtectionUatServer; + } else if (!license_id.compare("st")) { + g_license_server_id = wvcdm::kContentProtectionStagingServer; + } else { + std::cout << "Invalid license server id" << optarg << std::endl; + show_usage = true; + } + break; + } + case 'k': { + g_key_id.clear(); + g_key_id.assign(optarg); + break; + } + case 's': { + g_service_certificate.clear(); + g_service_certificate.assign(optarg); + break; + } + case 'u': { + g_license_server.clear(); + g_license_server.assign(optarg); + break; + } + case '?': { + show_usage = true; + break; + } + } + } + + if (show_usage) { + show_menu(argv[0]); + return 0; + } + + g_config = new wvcdm::ConfigTestEnv(g_license_server_id); + g_client_auth.assign(g_config->client_auth()); + g_key_system.assign(g_config->key_system()); + g_wrong_key_id.assign(g_config->wrong_key_id()); + + // The following variables are configurable through command line + // options. If the command line arguments are absent, use the settings + // in kLicenseServers[] pointed to by g_config. + if (g_key_id.empty()) { + g_key_id.assign(g_config->key_id()); + } + if (g_service_certificate.empty()) { + g_service_certificate.assign(g_config->service_certificate()); + } + if (g_license_server.empty()) { + g_license_server.assign(g_config->license_server_url()); + } + + // Displays server url, port and key Id being used + std::cout << std::endl; + std::cout << "Server: " << g_license_server << std::endl; + std::cout << "KeyID: " << g_key_id << std::endl << std::endl; + + g_key_id = wvcdm::a2bs_hex(g_key_id); + g_config->set_license_server(g_license_server); + + int status = RUN_ALL_TESTS(); + delete g_config; + return status; +} diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 17cc037d..40453700 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -3999,7 +3999,7 @@ int main(int argc, char** argv) { g_service_certificate.assign(g_config->service_certificate()); } if (g_license_server.empty()) { - g_license_server.assign(g_config->license_server()); + g_license_server.assign(g_config->license_server_url()); } // Displays server url, port and key Id being used diff --git a/libwvdrmengine/run_all_unit_tests.sh b/libwvdrmengine/run_all_unit_tests.sh index c9f1fa2d..13a2ae2a 100755 --- a/libwvdrmengine/run_all_unit_tests.sh +++ b/libwvdrmengine/run_all_unit_tests.sh @@ -75,6 +75,9 @@ adb_shell_run request_license_test # cdm_extended_duration_test takes >30 minutes to run. # adb_shell_run cdm_extended_duration_test +# cdm_feature_test to be run with modified/mock oemcrypto +# adb_shell_run cdm_feature_test + # Additional tests adb_shell_run base64_test adb_shell_run buffer_reader_test