Consolidate provisioning related protobuf parsing
[ Merge of http://go/wvgerrit/87905 ] Protobuf parsing of the provisioning message has been centralized in certificate_provisioning.cpp since it will be invoked from multiple locations. This will also ease maintainability of the code. Bug: 142731300 Test: android unit/integration tests Change-Id: Idebf6b0145b317698559cac1cf18a3a0b98315ad
This commit is contained in:
@@ -48,6 +48,19 @@ class CertificateProvisioning {
|
|||||||
FileSystem* file_system, const CdmProvisioningResponse& response,
|
FileSystem* file_system, const CdmProvisioningResponse& response,
|
||||||
std::string* cert, std::string* wrapped_key);
|
std::string* cert, std::string* wrapped_key);
|
||||||
|
|
||||||
|
// Helper methods
|
||||||
|
|
||||||
|
// Extract serial number and system ID from a DRM Device certificate.
|
||||||
|
// Either |serial_number| or |system_id| may be null, but not both.
|
||||||
|
static bool ExtractDeviceInfo(const std::string& device_certificate,
|
||||||
|
std::string* serial_number,
|
||||||
|
uint32_t* system_id);
|
||||||
|
|
||||||
|
// Removes json wrapping if applicable to extract the
|
||||||
|
// SignedProvisioningMessage
|
||||||
|
static bool ExtractAndDecodeSignedMessageForTesting(
|
||||||
|
const std::string& provisioning_response, std::string* result);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CdmResponseType SetSpoidParameter(
|
CdmResponseType SetSpoidParameter(
|
||||||
const std::string& origin, const std::string& spoid,
|
const std::string& origin, const std::string& spoid,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
#include "wv_cdm_types.h"
|
#include "wv_cdm_types.h"
|
||||||
|
|
||||||
#if defined(UNIT_TEST)
|
#if defined(UNIT_TEST)
|
||||||
#include <gtest/gtest_prod.h>
|
# include <gtest/gtest_prod.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
@@ -218,10 +218,6 @@ class DeviceFiles {
|
|||||||
virtual bool DeleteUsageTableInfo();
|
virtual bool DeleteUsageTableInfo();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Extract serial number and system ID from DRM Device certificate
|
|
||||||
bool ExtractDeviceInfo(const std::string& device_certificate,
|
|
||||||
std::string* serial_number, uint32_t* system_id);
|
|
||||||
|
|
||||||
// Helpers that wrap the File interface and automatically handle hashing, as
|
// Helpers that wrap the File interface and automatically handle hashing, as
|
||||||
// well as adding the device files base path to to the file name.
|
// well as adding the device files base path to to the file name.
|
||||||
ResponseType StoreFileWithHash(const std::string& name,
|
ResponseType StoreFileWithHash(const std::string& name,
|
||||||
|
|||||||
@@ -58,43 +58,50 @@ const std::string kCpProductionServiceCertificate = wvcdm::a2bs_hex(
|
|||||||
*
|
*
|
||||||
* If an error occurs during the parse or the decode, return an empty string.
|
* If an error occurs during the parse or the decode, return an empty string.
|
||||||
*/
|
*/
|
||||||
void ExtractAndDecodeSignedMessage(const std::string& provisioning_response,
|
bool ExtractAndDecodeSignedMessage(const std::string& provisioning_response,
|
||||||
std::string* result) {
|
std::string* result) {
|
||||||
const std::string json_start_substr("\"signedResponse\": \"");
|
const std::string json_start_substr("\"signedResponse\": \"");
|
||||||
const std::string json_end_substr("\"");
|
const std::string json_end_substr("\"");
|
||||||
std::string message_string;
|
std::string message_string;
|
||||||
|
|
||||||
|
if (result == nullptr) {
|
||||||
|
LOGE("Output parameter |result| is not provided");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
size_t start = provisioning_response.find(json_start_substr);
|
size_t start = provisioning_response.find(json_start_substr);
|
||||||
|
|
||||||
if (start == provisioning_response.npos) {
|
if (start == provisioning_response.npos) {
|
||||||
// Message is not properly wrapped - reject it.
|
// Message is not properly wrapped - reject it.
|
||||||
LOGE("Cannot locate start substring");
|
LOGE("Cannot locate start substring");
|
||||||
result->clear();
|
result->clear();
|
||||||
return;
|
return false;
|
||||||
} else {
|
|
||||||
// Appears to be JSON-wrapped protobuf - find end of protobuf portion.
|
|
||||||
size_t end = provisioning_response.find(json_end_substr,
|
|
||||||
start + json_start_substr.length());
|
|
||||||
if (end == provisioning_response.npos) {
|
|
||||||
LOGE("Cannot locate end substring");
|
|
||||||
result->clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
size_t b64_string_size = end - start - json_start_substr.length();
|
|
||||||
message_string.assign(provisioning_response,
|
|
||||||
start + json_start_substr.length(), b64_string_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Appears to be JSON-wrapped protobuf - find end of protobuf portion.
|
||||||
|
const size_t end = provisioning_response.find(
|
||||||
|
json_end_substr, start + json_start_substr.length());
|
||||||
|
if (end == provisioning_response.npos) {
|
||||||
|
LOGE("Cannot locate end substring");
|
||||||
|
result->clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t b64_string_size = end - start - json_start_substr.length();
|
||||||
|
message_string.assign(provisioning_response,
|
||||||
|
start + json_start_substr.length(), b64_string_size);
|
||||||
|
|
||||||
if (message_string.empty()) {
|
if (message_string.empty()) {
|
||||||
LOGE("CDM provisioning response is empty");
|
LOGE("CDM provisioning response is empty");
|
||||||
result->clear();
|
result->clear();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode the base64-encoded message.
|
// Decode the base64-encoded message.
|
||||||
const std::vector<uint8_t> decoded_message =
|
const std::vector<uint8_t> decoded_message =
|
||||||
wvcdm::Base64SafeDecode(message_string);
|
wvcdm::Base64SafeDecode(message_string);
|
||||||
result->assign(decoded_message.begin(), decoded_message.end());
|
result->assign(decoded_message.begin(), decoded_message.end());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -103,10 +110,12 @@ namespace wvcdm {
|
|||||||
// Protobuf generated classes.
|
// Protobuf generated classes.
|
||||||
using video_widevine::ClientIdentification_ClientCapabilities;
|
using video_widevine::ClientIdentification_ClientCapabilities;
|
||||||
using video_widevine::ClientIdentification_NameValue;
|
using video_widevine::ClientIdentification_NameValue;
|
||||||
|
using video_widevine::DrmDeviceCertificate;
|
||||||
using video_widevine::EncryptedClientIdentification;
|
using video_widevine::EncryptedClientIdentification;
|
||||||
using video_widevine::ProvisioningOptions;
|
using video_widevine::ProvisioningOptions;
|
||||||
using video_widevine::ProvisioningRequest;
|
using video_widevine::ProvisioningRequest;
|
||||||
using video_widevine::ProvisioningResponse;
|
using video_widevine::ProvisioningResponse;
|
||||||
|
using video_widevine::SignedDrmDeviceCertificate;
|
||||||
using video_widevine::SignedProvisioningMessage;
|
using video_widevine::SignedProvisioningMessage;
|
||||||
|
|
||||||
CdmResponseType CertificateProvisioning::Init(
|
CdmResponseType CertificateProvisioning::Init(
|
||||||
@@ -317,8 +326,8 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
|||||||
} else {
|
} else {
|
||||||
// The response is base64 encoded in a JSON wrapper.
|
// The response is base64 encoded in a JSON wrapper.
|
||||||
// Extract it and decode it. On error return an empty string.
|
// Extract it and decode it. On error return an empty string.
|
||||||
ExtractAndDecodeSignedMessage(response_message, &response);
|
bool result = ExtractAndDecodeSignedMessage(response_message, &response);
|
||||||
if (response.empty()) {
|
if (!result || response.empty()) {
|
||||||
LOGE("Provisioning response message is an invalid JSON/base64 string");
|
LOGE("Provisioning response message is an invalid JSON/base64 string");
|
||||||
return CERT_PROVISIONING_RESPONSE_ERROR_1;
|
return CERT_PROVISIONING_RESPONSE_ERROR_1;
|
||||||
}
|
}
|
||||||
@@ -418,4 +427,43 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
|||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Static
|
||||||
|
bool CertificateProvisioning::ExtractAndDecodeSignedMessageForTesting(
|
||||||
|
const std::string& provisioning_response, std::string* result) {
|
||||||
|
return ExtractAndDecodeSignedMessage(provisioning_response, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CertificateProvisioning::ExtractDeviceInfo(
|
||||||
|
const std::string& device_certificate, std::string* serial_number,
|
||||||
|
uint32_t* system_id) {
|
||||||
|
LOGV("Extracting device info");
|
||||||
|
if (serial_number == nullptr && system_id == nullptr) {
|
||||||
|
LOGE("Output parameters |serial_number| and |system_id| not provided");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get serial number and system ID from certificate
|
||||||
|
SignedDrmDeviceCertificate signed_drm_device_certificate;
|
||||||
|
if (!signed_drm_device_certificate.ParseFromString(device_certificate) ||
|
||||||
|
!signed_drm_device_certificate.has_drm_certificate()) {
|
||||||
|
LOGE("Failed to parse signed DRM device certificate");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DrmDeviceCertificate drm_device_certificate;
|
||||||
|
if (!drm_device_certificate.ParseFromString(
|
||||||
|
signed_drm_device_certificate.drm_certificate()) ||
|
||||||
|
(drm_device_certificate.type() !=
|
||||||
|
video_widevine::DrmDeviceCertificate::DRM_USER_DEVICE)) {
|
||||||
|
LOGE("Failed to parse DRM device certificate message");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (serial_number != nullptr) {
|
||||||
|
*serial_number = drm_device_certificate.serial_number();
|
||||||
|
}
|
||||||
|
if (system_id != nullptr) {
|
||||||
|
*system_id = drm_device_certificate.system_id();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace wvcdm
|
} // namespace wvcdm
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "certificate_provisioning.h"
|
||||||
#include "file_store.h"
|
#include "file_store.h"
|
||||||
#include "license_protocol.pb.h"
|
#include "license_protocol.pb.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
@@ -37,9 +38,6 @@ using video_widevine_client::sdk::
|
|||||||
using video_widevine_client::sdk::
|
using video_widevine_client::sdk::
|
||||||
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_USAGE_INFO;
|
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_USAGE_INFO;
|
||||||
|
|
||||||
using video_widevine::DrmDeviceCertificate;
|
|
||||||
using video_widevine::SignedDrmDeviceCertificate;
|
|
||||||
|
|
||||||
// Stringify turns macro arguments into static C strings.
|
// Stringify turns macro arguments into static C strings.
|
||||||
// Example: STRINGIFY(this_argument) -> "this_argument"
|
// Example: STRINGIFY(this_argument) -> "this_argument"
|
||||||
#define STRINGIFY(PARAM...) #PARAM
|
#define STRINGIFY(PARAM...) #PARAM
|
||||||
@@ -164,41 +162,8 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
|||||||
DeviceCertificate device_certificate = file.device_certificate();
|
DeviceCertificate device_certificate = file.device_certificate();
|
||||||
*certificate = device_certificate.certificate();
|
*certificate = device_certificate.certificate();
|
||||||
*wrapped_private_key = device_certificate.wrapped_private_key();
|
*wrapped_private_key = device_certificate.wrapped_private_key();
|
||||||
return ExtractDeviceInfo(device_certificate.certificate(), serial_number,
|
return CertificateProvisioning::ExtractDeviceInfo(
|
||||||
system_id);
|
device_certificate.certificate(), serial_number, system_id);
|
||||||
}
|
|
||||||
|
|
||||||
bool DeviceFiles::ExtractDeviceInfo(const std::string& device_certificate,
|
|
||||||
std::string* serial_number,
|
|
||||||
uint32_t* system_id) {
|
|
||||||
LOGV("Extracting device info");
|
|
||||||
if (serial_number == nullptr && system_id == nullptr) {
|
|
||||||
LOGE("Output parameters |serial_number| and |system_id| not provided");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get serial number and system ID from certificate
|
|
||||||
SignedDrmDeviceCertificate signed_drm_device_certificate;
|
|
||||||
if (!signed_drm_device_certificate.ParseFromString(device_certificate) ||
|
|
||||||
!signed_drm_device_certificate.has_drm_certificate()) {
|
|
||||||
LOGE("Failed to parse signed DRM device certificate");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
DrmDeviceCertificate drm_device_certificate;
|
|
||||||
if (!drm_device_certificate.ParseFromString(
|
|
||||||
signed_drm_device_certificate.drm_certificate()) ||
|
|
||||||
(drm_device_certificate.type() !=
|
|
||||||
video_widevine::DrmDeviceCertificate::DRM_USER_DEVICE)) {
|
|
||||||
LOGE("Failed to parse DRM device certificate message");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (serial_number != nullptr) {
|
|
||||||
*serial_number = drm_device_certificate.serial_number();
|
|
||||||
}
|
|
||||||
if (system_id != nullptr) {
|
|
||||||
*system_id = drm_device_certificate.system_id();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeviceFiles::HasCertificate() {
|
bool DeviceFiles::HasCertificate() {
|
||||||
|
|||||||
Reference in New Issue
Block a user