465 lines
18 KiB
C++
465 lines
18 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
// Copyright 2016 Google Inc.
|
|
//
|
|
// This software is licensed under the terms defined in the Widevine Master
|
|
// License Agreement. For a copy of this agreement, please contact
|
|
// widevine-licensing@google.com.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "provisioning_sdk/internal/provisioning_engine_impl.h"
|
|
|
|
#include <stddef.h>
|
|
#include <time.h>
|
|
#include <cstdint>
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include "glog/logging.h"
|
|
#include "common/random_util.h"
|
|
#include "common/rsa_key.h"
|
|
#include "provisioning_sdk/internal/certificates/root_certificates.h"
|
|
#include "provisioning_sdk/public/provisioning_status.h"
|
|
|
|
#define LOG_WITH_PROTO(message, proto) \
|
|
LOG(WARNING) << (message) << " [proto: " << (proto).ShortDebugString() << "]"
|
|
|
|
namespace widevine {
|
|
namespace {
|
|
|
|
// Verify that |certificate| is signed by |public_key|. If |public_key| is null,
|
|
// |certificate| should be self signed.
|
|
bool VerifyAndExtractCertificate(const RsaPublicKey* public_key,
|
|
const std::string& certificate,
|
|
SignedDrmDeviceCertificate* signed_drm_cert,
|
|
DrmDeviceCertificate* drm_cert) {
|
|
DCHECK(signed_drm_cert);
|
|
DCHECK(drm_cert);
|
|
if (!signed_drm_cert->ParseFromString(certificate)) {
|
|
LOG(WARNING) << "Failed to parse SignedDrmDeviceCertificate.";
|
|
return false;
|
|
}
|
|
if (signed_drm_cert->drm_certificate().empty()) {
|
|
LOG_WITH_PROTO("Missing drm_certificate", *signed_drm_cert);
|
|
return false;
|
|
}
|
|
if (signed_drm_cert->signature().empty()) {
|
|
LOG_WITH_PROTO("Missing signature", *signed_drm_cert);
|
|
return false;
|
|
}
|
|
|
|
if (!drm_cert->ParseFromString(signed_drm_cert->drm_certificate())) {
|
|
LOG_WITH_PROTO("Failed to parse DrmDeviceCertificate", *signed_drm_cert);
|
|
return false;
|
|
}
|
|
if (drm_cert->public_key().empty()) {
|
|
LOG_WITH_PROTO("Missing public_key", *drm_cert);
|
|
return false;
|
|
}
|
|
|
|
std::unique_ptr<RsaPublicKey> local_public_key;
|
|
if (!public_key) {
|
|
local_public_key.reset(RsaPublicKey::Create(drm_cert->public_key()));
|
|
if (!local_public_key) {
|
|
LOG_WITH_PROTO("Invalid root public key", *drm_cert);
|
|
return false;
|
|
}
|
|
public_key = local_public_key.get();
|
|
}
|
|
|
|
if (!public_key->VerifySignature(signed_drm_cert->drm_certificate(),
|
|
signed_drm_cert->signature())) {
|
|
LOG_WITH_PROTO("Signature verification failed", *signed_drm_cert);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool GenerateCertificate(DrmDeviceCertificate::CertificateType type,
|
|
uint32_t system_id,
|
|
const std::string& provider_id,
|
|
const std::string& serial_number,
|
|
const std::string& public_key,
|
|
const RsaPrivateKey& signing_key,
|
|
const SignedDrmDeviceCertificate& signer,
|
|
std::string* certificate) {
|
|
DCHECK(type == DrmDeviceCertificate::DRM_INTERMEDIATE ||
|
|
type == DrmDeviceCertificate::DRM_USER_DEVICE);
|
|
if (serial_number.empty()) {
|
|
LOG(WARNING) << "Require an non-empty serial number.";
|
|
return false;
|
|
}
|
|
|
|
DrmDeviceCertificate drm_cert;
|
|
drm_cert.set_type(type);
|
|
drm_cert.set_system_id(system_id);
|
|
if (!provider_id.empty()) drm_cert.set_provider_id(provider_id);
|
|
drm_cert.set_serial_number(serial_number);
|
|
drm_cert.set_creation_time_seconds(time(nullptr));
|
|
drm_cert.set_public_key(public_key);
|
|
|
|
SignedDrmDeviceCertificate signed_cert;
|
|
if (!drm_cert.SerializeToString(
|
|
signed_cert.mutable_drm_certificate())) {
|
|
LOG(WARNING) << "Error serializing DrmDeviceCertificate.";
|
|
return false;
|
|
}
|
|
if (!signing_key.GenerateSignature(signed_cert.drm_certificate(),
|
|
signed_cert.mutable_signature())) {
|
|
LOG(WARNING) << "Failed to generate signature for DrmDeviceCertificate.";
|
|
return false;
|
|
}
|
|
*signed_cert.mutable_signer() = signer;
|
|
if (!signed_cert.SerializeToString(certificate)) {
|
|
LOG(WARNING) << "Failed to serialize SignedDrmDeviceCertificate to string.";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Compares oem serial number, which is a 16-byte big number.
|
|
bool IsSerialNumberEq(const std::string& a, const std::string& b) {
|
|
// An empty serial number indicates that it does not need to be matched.
|
|
if (a.empty() || b.empty()) return true;
|
|
int a_index = a.size() - 1;
|
|
int b_index = b.size() - 1;
|
|
// Matching a and b backwards.
|
|
for (; a_index >= 0 && b_index >= 0; --a_index, --b_index)
|
|
if (a[a_index] != b[b_index]) return false;
|
|
// The remaining characters should be 0.
|
|
for (; a_index >= 0; --a_index)
|
|
if (a[a_index] != 0) return false;
|
|
for (; b_index >= 0; --b_index)
|
|
if (b[b_index] != 0) return false;
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
ProvisioningEngineImpl::ProvisioningEngineImpl()
|
|
: rsa_key_factory_(new RsaKeyFactory) {}
|
|
|
|
ProvisioningEngineImpl::~ProvisioningEngineImpl() {}
|
|
|
|
ProvisioningStatus ProvisioningEngineImpl::Initialize(
|
|
CertificateType certificate_type, const std::string& drm_service_certificate,
|
|
const std::string& service_private_key,
|
|
const std::string& service_private_key_phassphrase,
|
|
const std::string& provisioning_drm_certificate,
|
|
const std::string& provisioning_private_key,
|
|
const std::string& provisioning_private_key_phassphrase,
|
|
const std::string& secret_spoid_sauce) {
|
|
if (!LoadDrmRootPublicKey(certificate_type)) return INVALID_CERTIFICATE_TYPE;
|
|
|
|
SignedDrmDeviceCertificate signed_drm_cert;
|
|
DrmDeviceCertificate drm_cert;
|
|
if (!VerifyAndExtractCertificate(root_public_key_.get(),
|
|
drm_service_certificate, &signed_drm_cert,
|
|
&drm_cert)) {
|
|
return INVALID_SERVICE_DRM_CERTIFICATE;
|
|
}
|
|
if (drm_cert.type() != DrmDeviceCertificate::SERVICE) {
|
|
LOG(WARNING) << "Expecting SERVICE certificate.";
|
|
return INVALID_SERVICE_DRM_CERTIFICATE;
|
|
}
|
|
service_public_key_ =
|
|
rsa_key_factory_->CreateFromPkcs1PublicKey(drm_cert.public_key());
|
|
if (!service_public_key_) return INVALID_SERVICE_DRM_CERTIFICATE;
|
|
service_private_key_ = rsa_key_factory_->CreateFromPkcs8PrivateKey(
|
|
service_private_key, service_private_key_phassphrase);
|
|
if (!service_private_key_) return INVALID_SERVICE_PRIVATE_KEY;
|
|
if (!service_public_key_->MatchesPrivateKey(*service_private_key_)) {
|
|
LOG(WARNING) << "Services public key and private key do not match.";
|
|
return INVALID_SERVICE_PRIVATE_KEY;
|
|
}
|
|
|
|
if (!VerifyAndExtractCertificate(root_public_key_.get(),
|
|
provisioning_drm_certificate,
|
|
&signed_provisioning_cert_, &drm_cert)) {
|
|
return INVALID_PROVISIONER_DRM_CERTIFICATE;
|
|
}
|
|
if (drm_cert.type() != DrmDeviceCertificate::ROOT &&
|
|
drm_cert.type() != DrmDeviceCertificate::PROVISIONER) {
|
|
LOG(WARNING) << "Expecting ROOT or PROVISIONER certificate.";
|
|
return INVALID_PROVISIONER_DRM_CERTIFICATE;
|
|
}
|
|
|
|
provisioning_public_key_ =
|
|
rsa_key_factory_->CreateFromPkcs1PublicKey(drm_cert.public_key());
|
|
if (!provisioning_public_key_) return INVALID_PROVISIONER_DRM_CERTIFICATE;
|
|
provisioning_private_key_ = rsa_key_factory_->CreateFromPkcs8PrivateKey(
|
|
provisioning_private_key, provisioning_private_key_phassphrase);
|
|
if (!provisioning_private_key_) return INVALID_PROVISIONER_PRIVATE_KEY;
|
|
if (!provisioning_public_key_->MatchesPrivateKey(
|
|
*provisioning_private_key_)) {
|
|
LOG(WARNING) << "Provisioning public key and private key do not match.";
|
|
return INVALID_PROVISIONER_PRIVATE_KEY;
|
|
}
|
|
|
|
if (secret_spoid_sauce.empty()) {
|
|
LOG(WARNING) << "SPOID secret sauce is empty!";
|
|
return INVALID_SPOID_SAUCE;
|
|
}
|
|
secret_spoid_sauce_ = secret_spoid_sauce;
|
|
|
|
if (!oem_device_cert_.Initialize(certificate_type)) return INTERNAL_ERROR;
|
|
return OK;
|
|
}
|
|
|
|
ProvisioningStatus ProvisioningEngineImpl::SetCertificateStatusList(
|
|
const std::string& certificate_status_list, uint32_t expiration_period_seconds) {
|
|
if (certificate_status_list.empty()) {
|
|
LOG(WARNING) << "Empty certificate_status_list.";
|
|
return INVALID_STATUS_LIST;
|
|
}
|
|
|
|
SignedCertificateStatusList signed_cert_status_list;
|
|
if (!signed_cert_status_list.ParseFromString(certificate_status_list)) {
|
|
LOG(WARNING) << "Error parsing SignedCertificateStatusList.";
|
|
return INVALID_STATUS_LIST;
|
|
}
|
|
if (!root_public_key_->VerifySignature(
|
|
signed_cert_status_list.certificate_status_list(),
|
|
signed_cert_status_list.signature())) {
|
|
LOG_WITH_PROTO("Signature verification failed", signed_cert_status_list);
|
|
return INVALID_STATUS_LIST;
|
|
}
|
|
|
|
DeviceCertificateStatusList cert_status_list;
|
|
if (!cert_status_list.ParseFromString(
|
|
signed_cert_status_list.certificate_status_list())) {
|
|
LOG_WITH_PROTO("Error parsing DeviceCertificateStatusList",
|
|
signed_cert_status_list);
|
|
return INVALID_STATUS_LIST;
|
|
}
|
|
|
|
WriterMutexLock writer_lock(&mutex_);
|
|
|
|
if (expiration_period_seconds == 0) {
|
|
certificate_expiration_seconds_utc_ = std::numeric_limits<uint32_t>::max();
|
|
} else {
|
|
certificate_expiration_seconds_utc_ =
|
|
cert_status_list.creation_time_seconds() + expiration_period_seconds;
|
|
}
|
|
|
|
certificate_status_map_.clear();
|
|
for (DeviceCertificateStatus& cert_status :
|
|
*cert_status_list.mutable_certificate_status()) {
|
|
const uint32_t system_id = cert_status.device_info().system_id();
|
|
certificate_status_map_[system_id].Swap(&cert_status);
|
|
}
|
|
|
|
// Remove items that are no longer valid.
|
|
for (auto it = intermediate_certs_info_.begin();
|
|
it != intermediate_certs_info_.end();) {
|
|
auto certificate_status_it = certificate_status_map_.find(it->first);
|
|
if (certificate_status_it == certificate_status_map_.end())
|
|
intermediate_certs_info_.erase(it++);
|
|
else
|
|
++it;
|
|
}
|
|
// Set / Update device info.
|
|
for (const auto& cert_status : certificate_status_map_) {
|
|
auto device_info = std::make_shared<ProvisionedDeviceInfo>();
|
|
*device_info = cert_status.second.device_info();
|
|
intermediate_certs_info_[cert_status.first].device_info = device_info;
|
|
// intermediate certificate and private key are not changed if exists.
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
ProvisioningStatus ProvisioningEngineImpl::GenerateDrmIntermediateCertificate(
|
|
uint32_t system_id, const std::string& public_key, std::string* certificate) const {
|
|
auto intermediate_public_key =
|
|
rsa_key_factory_->CreateFromPkcs1PublicKey(public_key);
|
|
if (!intermediate_public_key) return INVALID_INTERMEDIATE_PUBLIC_KEY;
|
|
|
|
const size_t kCertificateSerialNumberSize = 16;
|
|
std::string serial_number;
|
|
if (!RandomBytes(kCertificateSerialNumberSize, &serial_number)) {
|
|
LOG(WARNING) << "Failed to generate serial_number.";
|
|
return INTERNAL_ERROR;
|
|
}
|
|
if (!GenerateCertificate(DrmDeviceCertificate::DRM_INTERMEDIATE,
|
|
system_id, std::string(), serial_number, public_key,
|
|
*provisioning_private_key_,
|
|
signed_provisioning_cert_, certificate)) {
|
|
return INTERNAL_ERROR;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
ProvisioningStatus ProvisioningEngineImpl::AddDrmIntermediateCertificate(
|
|
const std::string& intermediate_cert, const std::string& cert_private_key,
|
|
const std::string& cert_private_key_passphrase) {
|
|
SignedDrmDeviceCertificate intermediate_signed_cert;
|
|
DrmDeviceCertificate intermediate_drm_cert;
|
|
if (!VerifyAndExtractCertificate(provisioning_public_key_.get(),
|
|
intermediate_cert, &intermediate_signed_cert,
|
|
&intermediate_drm_cert)) {
|
|
return INVALID_INTERMEDIATE_DRM_CERTIFICATE;
|
|
}
|
|
if (intermediate_drm_cert.type() !=
|
|
DrmDeviceCertificate::DRM_INTERMEDIATE) {
|
|
LOG_WITH_PROTO("Invalid device certificate type", intermediate_drm_cert);
|
|
return INVALID_INTERMEDIATE_DRM_CERTIFICATE;
|
|
}
|
|
if (!intermediate_drm_cert.has_system_id()) {
|
|
LOG_WITH_PROTO("Missing system_id", intermediate_drm_cert);
|
|
return UNKNOWN_SYSTEM_ID;
|
|
}
|
|
|
|
const std::string empty_oem_ca_serial_number;
|
|
ProvisioningStatus status = CheckDeviceStatus(
|
|
intermediate_drm_cert.system_id(), empty_oem_ca_serial_number);
|
|
if (status != OK) return status;
|
|
|
|
auto intermediate_public_key = rsa_key_factory_->CreateFromPkcs1PublicKey(
|
|
intermediate_drm_cert.public_key());
|
|
if (!intermediate_public_key) return INVALID_INTERMEDIATE_DRM_CERTIFICATE;
|
|
std::unique_ptr<RsaPrivateKey> intermediate_private_key =
|
|
rsa_key_factory_->CreateFromPkcs8PrivateKey(cert_private_key,
|
|
cert_private_key_passphrase);
|
|
if (!intermediate_private_key) return INVALID_INTERMEDIATE_PRIVATE_KEY;
|
|
if (!intermediate_public_key->MatchesPrivateKey(*intermediate_private_key)) {
|
|
LOG(WARNING) << "Intermediate public key and private key do not match.";
|
|
return INVALID_INTERMEDIATE_PRIVATE_KEY;
|
|
}
|
|
|
|
WriterMutexLock writer_lock(&mutex_);
|
|
auto& certificate_info =
|
|
intermediate_certs_info_[intermediate_drm_cert.system_id()];
|
|
certificate_info.signed_drm_certificate.Swap(&intermediate_signed_cert);
|
|
certificate_info.private_key = std::move(intermediate_private_key);
|
|
return OK;
|
|
}
|
|
|
|
ProvisioningStatus ProvisioningEngineImpl::GenerateDeviceDrmCertificate(
|
|
uint32_t system_id, const std::string& oem_ca_serial_number,
|
|
const std::string& public_key, const std::string& certificate_serial_number,
|
|
std::string* certificate) const {
|
|
// |oem_ca_serial_number| could be empty if it is called directly from
|
|
// ProvisioningEngine::GenerateDeviceDrmCertificate.
|
|
DCHECK(!certificate_serial_number.empty());
|
|
|
|
return GenerateProviderDeviceDrmCertificate(
|
|
system_id, oem_ca_serial_number, std::string(), public_key,
|
|
certificate_serial_number, certificate);
|
|
}
|
|
|
|
ProvisioningStatus ProvisioningEngineImpl::GenerateProviderDeviceDrmCertificate(
|
|
uint32_t system_id, const std::string& oem_ca_serial_number,
|
|
const std::string& provider_id, const std::string& public_key,
|
|
const std::string& certificate_serial_number, std::string* certificate) const {
|
|
// |oem_ca_serial_number| could be empty if it is called directly from
|
|
// ProvisioningEngine::GenerateDeviceDrmCertificate.
|
|
DCHECK(!certificate_serial_number.empty());
|
|
|
|
ProvisioningStatus status =
|
|
CheckDeviceStatus(system_id, oem_ca_serial_number);
|
|
if (status != OK) return status;
|
|
|
|
std::shared_ptr<RsaPrivateKey> intermediate_private_key;
|
|
const SignedDrmDeviceCertificate* intermediate_cert = nullptr;
|
|
{
|
|
ReaderMutexLock reader_lock(&mutex_);
|
|
|
|
auto info_it = intermediate_certs_info_.find(system_id);
|
|
if (info_it == intermediate_certs_info_.end() ||
|
|
!info_it->second.private_key) {
|
|
LOG(WARNING) << "Cannot find the intermediate certificate for system: "
|
|
<< system_id;
|
|
return MISSING_DRM_INTERMEDIATE_CERT;
|
|
}
|
|
intermediate_private_key = info_it->second.private_key;
|
|
intermediate_cert = &info_it->second.signed_drm_certificate;
|
|
}
|
|
|
|
if (!GenerateCertificate(DrmDeviceCertificate::DRM_USER_DEVICE,
|
|
system_id, provider_id, certificate_serial_number,
|
|
public_key, *intermediate_private_key,
|
|
*intermediate_cert, certificate)) {
|
|
return INTERNAL_ERROR;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
std::shared_ptr<ProvisionedDeviceInfo> ProvisioningEngineImpl::GetDeviceInfo(
|
|
uint32_t system_id) const {
|
|
ReaderMutexLock reader_lock(&mutex_);
|
|
auto info_it = intermediate_certs_info_.find(system_id);
|
|
if (info_it == intermediate_certs_info_.end()) {
|
|
LOG(WARNING) << "Cannot find the system id in device certificate list: "
|
|
<< system_id;
|
|
return std::shared_ptr<ProvisionedDeviceInfo>();
|
|
}
|
|
return info_it->second.device_info;
|
|
}
|
|
|
|
bool ProvisioningEngineImpl::LoadDrmRootPublicKey(
|
|
CertificateType certificate_type) {
|
|
const std::string* root_cert_string = nullptr;
|
|
RootCertificates root_certificates;
|
|
switch (certificate_type) {
|
|
case kCertTesting:
|
|
root_cert_string = &root_certificates.drm_root_test_certificate();
|
|
break;
|
|
case kCertDevelopment:
|
|
root_cert_string = &root_certificates.drm_root_dev_certificate();
|
|
break;
|
|
case kCertProduction:
|
|
root_cert_string = &root_certificates.drm_root_prod_certificate();
|
|
break;
|
|
default:
|
|
LOG(WARNING) << "Invalid certificate type " << certificate_type;
|
|
return false;
|
|
}
|
|
|
|
SignedDrmDeviceCertificate signed_root_cert;
|
|
DrmDeviceCertificate root_cert;
|
|
if (!VerifyAndExtractCertificate(nullptr /* self signed */, *root_cert_string,
|
|
&signed_root_cert, &root_cert)) {
|
|
LOG(WARNING) << "Failed to extract root certificate.";
|
|
return false;
|
|
}
|
|
if (root_cert.type() != DrmDeviceCertificate::ROOT) {
|
|
LOG(WARNING) << "Expecting ROOT certificate.";
|
|
return false;
|
|
}
|
|
root_public_key_ =
|
|
rsa_key_factory_->CreateFromPkcs1PublicKey(root_cert.public_key());
|
|
CHECK(root_public_key_);
|
|
return true;
|
|
}
|
|
|
|
ProvisioningStatus ProvisioningEngineImpl::CheckDeviceStatus(
|
|
uint32_t system_id, const std::string& oem_ca_serial_number) const {
|
|
ReaderMutexLock reader_lock(&mutex_);
|
|
|
|
if (certificate_expiration_seconds_utc_ < time(nullptr))
|
|
return STATUS_LIST_EXPIRED;
|
|
|
|
auto certificate_status_it = certificate_status_map_.find(system_id);
|
|
if (certificate_status_it == certificate_status_map_.end()) {
|
|
LOG(WARNING) << "Cannot find the system id in device certificate list: "
|
|
<< system_id;
|
|
return UNKNOWN_SYSTEM_ID;
|
|
}
|
|
if (!IsSerialNumberEq(certificate_status_it->second.oem_serial_number(),
|
|
oem_ca_serial_number)) {
|
|
LOG(WARNING) << "Provided serial number does not match with stored serial "
|
|
"number. It may come from a revoked device. System Id: "
|
|
<< system_id;
|
|
return DEVICE_REVOKED;
|
|
}
|
|
if (certificate_status_it->second.status() !=
|
|
DeviceCertificateStatus::VALID) {
|
|
LOG(WARNING) << "Device revoked " << system_id;
|
|
return DEVICE_REVOKED;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
} // namespace widevine
|