[ Merge of http://go/wvgerrit/22900 ] Add GetClientToken(), GetProvisioningToken(), GetPreProvisionTokenType() to CryptoSession. They return the correct token bytes and token type for preparing the ClientIdentification message for provisioning and license server transactions. Also refactor service certificate handling. OEM certs are introduced in Provisioning 3.0 b/30811184 * Address build breaks [ Merge of http://go/wvgerrit/23162 ] This addresses issues introduced by http://go/wvgerrit/22900 b/30811184 * When http://go/wvgerrit/18012 was merged (ag/1446934) some changes were not merged for mapErrors-inl.h. These changes are included in this CL. * When ag/1678104 was reverse merged to http//go/wvgerrit/21981/ a variable was renamed and some comments were added to add clarity in cdm_engine.cpp. These changes are included in this CL. Test: All unittests other than some oemcrypto, request_license_test passed. Those tests failed with or without this CL. Change-Id: Ie0215509f2f985f2a610f5a4c865db47edec8662
275 lines
10 KiB
C++
275 lines
10 KiB
C++
// Copyright 2017 Google Inc. All Rights Reserved.
|
|
|
|
#include "service_certificate.h"
|
|
|
|
#include "crypto_key.h"
|
|
#include "crypto_session.h"
|
|
#include "log.h"
|
|
#include "privacy_crypto.h"
|
|
#include "properties.h"
|
|
#include "wv_cdm_constants.h"
|
|
|
|
namespace {
|
|
// Service certificate for Google/Widevine Provisioning and License servers.
|
|
const unsigned char kServiceCertificateCAPublicKey[] = {
|
|
0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xb4, 0xfe, 0x39,
|
|
0xc3, 0x65, 0x90, 0x03, 0xdb, 0x3c, 0x11, 0x97, 0x09, 0xe8, 0x68, 0xcd,
|
|
0xf2, 0xc3, 0x5e, 0x9b, 0xf2, 0xe7, 0x4d, 0x23, 0xb1, 0x10, 0xdb, 0x87,
|
|
0x65, 0xdf, 0xdc, 0xfb, 0x9f, 0x35, 0xa0, 0x57, 0x03, 0x53, 0x4c, 0xf6,
|
|
0x6d, 0x35, 0x7d, 0xa6, 0x78, 0xdb, 0xb3, 0x36, 0xd2, 0x3f, 0x9c, 0x40,
|
|
0xa9, 0x95, 0x26, 0x72, 0x7f, 0xb8, 0xbe, 0x66, 0xdf, 0xc5, 0x21, 0x98,
|
|
0x78, 0x15, 0x16, 0x68, 0x5d, 0x2f, 0x46, 0x0e, 0x43, 0xcb, 0x8a, 0x84,
|
|
0x39, 0xab, 0xfb, 0xb0, 0x35, 0x80, 0x22, 0xbe, 0x34, 0x23, 0x8b, 0xab,
|
|
0x53, 0x5b, 0x72, 0xec, 0x4b, 0xb5, 0x48, 0x69, 0x53, 0x3e, 0x47, 0x5f,
|
|
0xfd, 0x09, 0xfd, 0xa7, 0x76, 0x13, 0x8f, 0x0f, 0x92, 0xd6, 0x4c, 0xdf,
|
|
0xae, 0x76, 0xa9, 0xba, 0xd9, 0x22, 0x10, 0xa9, 0x9d, 0x71, 0x45, 0xd6,
|
|
0xd7, 0xe1, 0x19, 0x25, 0x85, 0x9c, 0x53, 0x9a, 0x97, 0xeb, 0x84, 0xd7,
|
|
0xcc, 0xa8, 0x88, 0x82, 0x20, 0x70, 0x26, 0x20, 0xfd, 0x7e, 0x40, 0x50,
|
|
0x27, 0xe2, 0x25, 0x93, 0x6f, 0xbc, 0x3e, 0x72, 0xa0, 0xfa, 0xc1, 0xbd,
|
|
0x29, 0xb4, 0x4d, 0x82, 0x5c, 0xc1, 0xb4, 0xcb, 0x9c, 0x72, 0x7e, 0xb0,
|
|
0xe9, 0x8a, 0x17, 0x3e, 0x19, 0x63, 0xfc, 0xfd, 0x82, 0x48, 0x2b, 0xb7,
|
|
0xb2, 0x33, 0xb9, 0x7d, 0xec, 0x4b, 0xba, 0x89, 0x1f, 0x27, 0xb8, 0x9b,
|
|
0x88, 0x48, 0x84, 0xaa, 0x18, 0x92, 0x0e, 0x65, 0xf5, 0xc8, 0x6c, 0x11,
|
|
0xff, 0x6b, 0x36, 0xe4, 0x74, 0x34, 0xca, 0x8c, 0x33, 0xb1, 0xf9, 0xb8,
|
|
0x8e, 0xb4, 0xe6, 0x12, 0xe0, 0x02, 0x98, 0x79, 0x52, 0x5e, 0x45, 0x33,
|
|
0xff, 0x11, 0xdc, 0xeb, 0xc3, 0x53, 0xba, 0x7c, 0x60, 0x1a, 0x11, 0x3d,
|
|
0x00, 0xfb, 0xd2, 0xb7, 0xaa, 0x30, 0xfa, 0x4f, 0x5e, 0x48, 0x77, 0x5b,
|
|
0x17, 0xdc, 0x75, 0xef, 0x6f, 0xd2, 0x19, 0x6d, 0xdc, 0xbe, 0x7f, 0xb0,
|
|
0x78, 0x8f, 0xdc, 0x82, 0x60, 0x4c, 0xbf, 0xe4, 0x29, 0x06, 0x5e, 0x69,
|
|
0x8c, 0x39, 0x13, 0xad, 0x14, 0x25, 0xed, 0x19, 0xb2, 0xf2, 0x9f, 0x01,
|
|
0x82, 0x0d, 0x56, 0x44, 0x88, 0xc8, 0x35, 0xec, 0x1f, 0x11, 0xb3, 0x24,
|
|
0xe0, 0x59, 0x0d, 0x37, 0xe4, 0x47, 0x3c, 0xea, 0x4b, 0x7f, 0x97, 0x31,
|
|
0x1c, 0x81, 0x7c, 0x94, 0x8a, 0x4c, 0x7d, 0x68, 0x15, 0x84, 0xff, 0xa5,
|
|
0x08, 0xfd, 0x18, 0xe7, 0xe7, 0x2b, 0xe4, 0x47, 0x27, 0x12, 0x11, 0xb8,
|
|
0x23, 0xec, 0x58, 0x93, 0x3c, 0xac, 0x12, 0xd2, 0x88, 0x6d, 0x41, 0x3d,
|
|
0xc5, 0xfe, 0x1c, 0xdc, 0xb9, 0xf8, 0xd4, 0x51, 0x3e, 0x07, 0xe5, 0x03,
|
|
0x6f, 0xa7, 0x12, 0xe8, 0x12, 0xf7, 0xb5, 0xce, 0xa6, 0x96, 0x55, 0x3f,
|
|
0x78, 0xb4, 0x64, 0x82, 0x50, 0xd2, 0x33, 0x5f, 0x91, 0x02, 0x03, 0x01,
|
|
0x00, 0x01};
|
|
} // namespace
|
|
|
|
namespace wvcdm {
|
|
|
|
// Protobuf generated classes.
|
|
using video_widevine::ClientIdentification;
|
|
using video_widevine::DrmDeviceCertificate;
|
|
using video_widevine::EncryptedClientIdentification;
|
|
using video_widevine::SignedDrmDeviceCertificate;
|
|
using video_widevine::SignedMessage;
|
|
|
|
ServiceCertificate::ServiceCertificate()
|
|
: crypto_session_(NULL),
|
|
valid_(false),
|
|
initialized_(false) {
|
|
certificate_.clear();
|
|
}
|
|
|
|
ServiceCertificate::~ServiceCertificate() {}
|
|
|
|
bool ServiceCertificate::Init(const CdmSessionId& session_id,
|
|
CryptoSession* session) {
|
|
if (session_id.empty()) {
|
|
LOGE("ServiceCertificate::Init: empty session id provided");
|
|
return false;
|
|
}
|
|
if (session == NULL) {
|
|
LOGE("ServiceCertificate::Init: crypto session not provided");
|
|
return false;
|
|
}
|
|
session_id_.assign(session_id);
|
|
crypto_session_ = session;
|
|
privacy_mode_enabled_ = Properties::UsePrivacyMode(session_id_);
|
|
SetupServiceCertificate();
|
|
initialized_ = true;
|
|
return true;
|
|
}
|
|
|
|
bool ServiceCertificate::IsRequired() {
|
|
return privacy_mode_enabled_;
|
|
}
|
|
|
|
bool ServiceCertificate::IsAvailable() {
|
|
return valid_;
|
|
}
|
|
|
|
CdmResponseType ServiceCertificate::VerifyAndSet(
|
|
const std::string& signed_service_certificate) {
|
|
CdmResponseType status;
|
|
std::string certificate;
|
|
bool has_provider_id;
|
|
status = VerifyAndExtractFromSignedCertificate(signed_service_certificate,
|
|
&certificate, &has_provider_id,
|
|
NULL);
|
|
if (status == NO_ERROR) {
|
|
Properties::SetServiceCertificate(session_id_, certificate);
|
|
} else {
|
|
LOGE("ServiceCertificate::VerifyAndSet: verify and extract failed with "
|
|
"status %d", status);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
bool ServiceCertificate::PrepareServiceCertificateRequest(
|
|
CdmKeyMessage* signed_request) {
|
|
if (!initialized_) {
|
|
LOGE("ServiceCertificate::PrepareServiceCertificateRequest: "
|
|
"not initialized");
|
|
return false;
|
|
}
|
|
if (!signed_request) {
|
|
LOGE("ServiceCertificate::PrepareServiceCertificateRequest: "
|
|
"no signed request provided");
|
|
return false;
|
|
}
|
|
SignedMessage signed_message;
|
|
signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
|
|
signed_message.SerializeToString(signed_request);
|
|
|
|
return true;
|
|
}
|
|
|
|
CdmResponseType ServiceCertificate::VerifyAndExtractFromSignedCertificate(
|
|
const std::string& signed_certificate, std::string* certificate,
|
|
bool* has_provider_id, std::string* provider_id) {
|
|
SignedDrmDeviceCertificate signed_service_certificate;
|
|
if (!signed_service_certificate.ParseFromString(signed_certificate)) {
|
|
LOGE(
|
|
"ServiceCertificate::VerifyAndExtractFromSignedCertificate: "
|
|
"unable to parse signed service certificate");
|
|
return DEVICE_CERTIFICATE_ERROR_1;
|
|
}
|
|
|
|
RsaPublicKey root_ca_key;
|
|
std::string ca_public_key(
|
|
reinterpret_cast<const char*>(&kServiceCertificateCAPublicKey[0]),
|
|
sizeof(kServiceCertificateCAPublicKey));
|
|
if (!root_ca_key.Init(ca_public_key)) {
|
|
LOGE(
|
|
"ServiceCertificate::VerifyAndExtractFromSignedCertificate: public key "
|
|
"initialization failed");
|
|
return DEVICE_CERTIFICATE_ERROR_2;
|
|
}
|
|
|
|
if (!root_ca_key.VerifySignature(
|
|
signed_service_certificate.drm_certificate(),
|
|
signed_service_certificate.signature())) {
|
|
LOGE(
|
|
"ServiceCertificate::VerifyAndExtractFromSignedCertificate: service "
|
|
"certificate verification failed");
|
|
return DEVICE_CERTIFICATE_ERROR_3;
|
|
}
|
|
|
|
DrmDeviceCertificate service_certificate;
|
|
if (!service_certificate.ParseFromString(
|
|
signed_service_certificate.drm_certificate())) {
|
|
LOGE(
|
|
"ServiceCertificate::VerifyAndExtractFromSignedCertificate: unable to "
|
|
"parse retrieved service certificate");
|
|
return DEVICE_CERTIFICATE_ERROR_4;
|
|
}
|
|
|
|
if (service_certificate.type() !=
|
|
video_widevine::DrmDeviceCertificate_CertificateType_SERVICE) {
|
|
LOGE(
|
|
"ServiceCertificate::VerifyAndExtractFromSignedCertificate: "
|
|
"certificate not of type service, %d", service_certificate.type());
|
|
return INVALID_DEVICE_CERTIFICATE_TYPE;
|
|
}
|
|
|
|
#if 0 // TODO(gmorgan): service cert has no provider_id
|
|
*has_provider_id = service_certificate.has_provider_id();
|
|
if (*has_provider_id && provider_id != NULL) {
|
|
*provider_id = service_certificate.provider_id();
|
|
} else {
|
|
return DEVICE_CERTIFICATE_ERROR_5;
|
|
}
|
|
#endif
|
|
|
|
if (certificate != NULL) {
|
|
*certificate = signed_service_certificate.drm_certificate();
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
|
|
CdmResponseType ServiceCertificate::EncryptClientId(
|
|
const ClientIdentification* clear_client_id,
|
|
EncryptedClientIdentification* encrypted_client_id) {
|
|
DrmDeviceCertificate service_certificate;
|
|
|
|
if (!service_certificate.ParseFromString(certificate_)) {
|
|
LOGE("ServiceCertificate::EncryptClientId: unable to parse retrieved "
|
|
"service certificate");
|
|
return PARSE_SERVICE_CERTIFICATE_ERROR;
|
|
}
|
|
|
|
if (service_certificate.type() !=
|
|
video_widevine::DrmDeviceCertificate_CertificateType_SERVICE) {
|
|
LOGE("ServiceCertificate::EncryptClientId: retrieved certificate not of "
|
|
"type service, %d", service_certificate.type());
|
|
return SERVICE_CERTIFICATE_TYPE_ERROR;
|
|
}
|
|
encrypted_client_id->set_provider_id(service_certificate.provider_id());
|
|
encrypted_client_id->set_service_certificate_serial_number(
|
|
service_certificate.serial_number());
|
|
|
|
std::string iv(KEY_IV_SIZE, 0); // TODO(gmorgan) randomize
|
|
std::string key(KEY_SIZE, 0);
|
|
|
|
if (!crypto_session_->GetRandom(key.size(),
|
|
reinterpret_cast<uint8_t*>(&key[0])))
|
|
return CLIENT_ID_GENERATE_RANDOM_ERROR;
|
|
if (!crypto_session_->GetRandom(iv.size(),
|
|
reinterpret_cast<uint8_t*>(&iv[0])))
|
|
return CLIENT_ID_GENERATE_RANDOM_ERROR;
|
|
std::string id, enc_id, enc_key;
|
|
clear_client_id->SerializeToString(&id);
|
|
|
|
AesCbcKey aes;
|
|
if (!aes.Init(key)) return CLIENT_ID_AES_INIT_ERROR;
|
|
if (!aes.Encrypt(id, &enc_id, &iv)) return CLIENT_ID_AES_ENCRYPT_ERROR;
|
|
|
|
RsaPublicKey rsa;
|
|
if (!rsa.Init(service_certificate.public_key()))
|
|
return CLIENT_ID_RSA_INIT_ERROR;
|
|
if (!rsa.Encrypt(key, &enc_key)) return CLIENT_ID_RSA_ENCRYPT_ERROR;
|
|
|
|
encrypted_client_id->set_encrypted_client_id_iv(iv);
|
|
encrypted_client_id->set_encrypted_privacy_key(enc_key);
|
|
encrypted_client_id->set_encrypted_client_id(enc_id);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
bool ServiceCertificate::SetupServiceCertificate() {
|
|
std::string signed_certificate;
|
|
|
|
valid_ = false;
|
|
certificate_.clear();
|
|
|
|
if (!Properties::GetServiceCertificate(session_id_, &signed_certificate)) {
|
|
return false;
|
|
}
|
|
if (signed_certificate.empty()) {
|
|
return false;
|
|
}
|
|
std::string extracted_certificate;
|
|
std::string extracted_provider_id;
|
|
bool has_provider_id;
|
|
if (NO_ERROR != VerifyAndExtractFromSignedCertificate(
|
|
signed_certificate, &extracted_certificate,
|
|
&has_provider_id, &extracted_provider_id)) {
|
|
return false;
|
|
}
|
|
if (extracted_certificate.empty()) {
|
|
return false;
|
|
}
|
|
has_provider_id_ = has_provider_id;
|
|
if (has_provider_id_) {
|
|
provider_id_ = extracted_provider_id;
|
|
}
|
|
certificate_ = extracted_certificate;
|
|
valid_ = true;
|
|
return true;
|
|
}
|
|
|
|
} // namespace wvcdm
|