Minimal implementation of Widevine MediaCAS ECMG.
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=226515998
This commit is contained in:
@@ -17,19 +17,18 @@
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "util/gtl/map_util.h"
|
||||
#include "common/status.h"
|
||||
#include "common/crypto_util.h"
|
||||
#include "common/drm_root_certificate.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/random_util.h"
|
||||
#include "common/sha_util.h"
|
||||
#include "common/signing_key_util.h"
|
||||
#include "common/status.h"
|
||||
#include "common/wvm_token_handler.h"
|
||||
#include "protos/public/drm_certificate.pb.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/signed_drm_certificate.pb.h"
|
||||
|
||||
// TODO(user): Get rid of this horror.
|
||||
|
||||
namespace widevine {
|
||||
namespace {
|
||||
|
||||
@@ -37,45 +36,44 @@ const int kKeyboxSizeBytes = 72;
|
||||
|
||||
} // namespace
|
||||
|
||||
// TODO(user): change to util::StatusOr<std::unique_ptr<ClientCert>>
|
||||
// instead of ClientCert** to explicitly assigning ownership of the created
|
||||
// object to the caller.
|
||||
|
||||
util::Status ClientCert::Create(const DrmRootCertificate* root_certificate,
|
||||
ClientIdentification::TokenType token_type,
|
||||
const std::string& token, ClientCert** client_cert) {
|
||||
Status ClientCert::Create(const DrmRootCertificate* root_certificate,
|
||||
ClientIdentification::TokenType token_type,
|
||||
const std::string& token, ClientCert** client_cert) {
|
||||
DCHECK(client_cert);
|
||||
if (token_type == ClientIdentification::KEYBOX) {
|
||||
*client_cert = nullptr;
|
||||
if (token.size() < kKeyboxSizeBytes) {
|
||||
return util::Status(error_space, INVALID_KEYBOX_TOKEN,
|
||||
"keybox-token-is-too-short");
|
||||
return Status(error_space, INVALID_KEYBOX_TOKEN,
|
||||
"keybox-token-is-too-short");
|
||||
}
|
||||
return ClientCert::CreateWithKeybox(token, client_cert);
|
||||
} else if (token_type == ClientIdentification::DRM_DEVICE_CERTIFICATE) {
|
||||
return CreateWithDrmCertificate(root_certificate, token, client_cert);
|
||||
} else {
|
||||
return util::Status(error_space, util::error::UNIMPLEMENTED,
|
||||
"client-type-not-implemented");
|
||||
return Status(error_space, error::UNIMPLEMENTED,
|
||||
"client-type-not-implemented");
|
||||
}
|
||||
}
|
||||
|
||||
util::Status ClientCert::CreateWithKeybox(const std::string& keybox_token,
|
||||
ClientCert** client_cert) {
|
||||
Status ClientCert::CreateWithKeybox(const std::string& keybox_token,
|
||||
ClientCert** client_cert) {
|
||||
CHECK(client_cert);
|
||||
*client_cert = nullptr;
|
||||
|
||||
std::unique_ptr<KeyboxClientCert> new_client_cert(new KeyboxClientCert);
|
||||
util::Status status = new_client_cert->Initialize(keybox_token);
|
||||
Status status = new_client_cert->Initialize(keybox_token);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
*client_cert = new_client_cert.release();
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
util::Status ClientCert::CreateWithDrmCertificate(
|
||||
Status ClientCert::CreateWithDrmCertificate(
|
||||
const DrmRootCertificate* root_certificate, const std::string& drm_certificate,
|
||||
ClientCert** client_cert) {
|
||||
CHECK(client_cert);
|
||||
@@ -83,14 +81,14 @@ util::Status ClientCert::CreateWithDrmCertificate(
|
||||
|
||||
std::unique_ptr<CertificateClientCert> new_client_cert(
|
||||
new CertificateClientCert);
|
||||
util::Status status =
|
||||
Status status =
|
||||
new_client_cert->Initialize(root_certificate, drm_certificate);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
*client_cert = new_client_cert.release();
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
void ClientCert::CreateSignature(const std::string& message, std::string* signature) {
|
||||
@@ -110,8 +108,10 @@ void ClientCert::GenerateSigningKey(const std::string& message,
|
||||
DCHECK(!key().empty());
|
||||
using crypto_util::DeriveKey;
|
||||
using crypto_util::kSigningKeyLabel;
|
||||
set_signing_key(DeriveKey(key(), kSigningKeyLabel, message,
|
||||
SigningKeyMaterialSize(protocol_version)));
|
||||
set_signing_key(
|
||||
DeriveKey(key(), kSigningKeyLabel,
|
||||
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
|
||||
SigningKeyMaterialSizeBits(protocol_version)));
|
||||
}
|
||||
|
||||
KeyboxClientCert::KeyboxClientCert() {}
|
||||
@@ -139,51 +139,51 @@ uint32_t KeyboxClientCert::GetSystemId(const std::string& keybox_bytes) {
|
||||
return WvmTokenHandler::GetSystemId(keybox_bytes);
|
||||
}
|
||||
|
||||
util::Status KeyboxClientCert::Initialize(const std::string& keybox_bytes) {
|
||||
Status KeyboxClientCert::Initialize(const std::string& keybox_bytes) {
|
||||
if (keybox_bytes.size() < kKeyboxSizeBytes) {
|
||||
return util::Status(error_space, INVALID_KEYBOX_TOKEN,
|
||||
"keybox-token-is-too-short");
|
||||
return Status(error_space, INVALID_KEYBOX_TOKEN,
|
||||
"keybox-token-is-too-short");
|
||||
}
|
||||
|
||||
set_system_id(WvmTokenHandler::GetSystemId(keybox_bytes));
|
||||
set_serial_number(WvmTokenHandler::GetEncryptedUniqueId(keybox_bytes));
|
||||
bool insecure_keybox = false;
|
||||
util::Status status = WvmTokenHandler::DecryptDeviceKey(
|
||||
keybox_bytes, &device_key_, nullptr, &insecure_keybox);
|
||||
Status status = WvmTokenHandler::DecryptDeviceKey(keybox_bytes, &device_key_,
|
||||
nullptr, &insecure_keybox);
|
||||
if (!status.ok()) {
|
||||
Errors new_code = status.error_code() == util::error::NOT_FOUND
|
||||
Errors new_code = status.error_code() == error::NOT_FOUND
|
||||
? MISSING_PRE_PROV_KEY
|
||||
: KEYBOX_DECRYPT_ERROR;
|
||||
return util::Status(error_space, new_code, status.error_message());
|
||||
return Status(error_space, new_code, status.error_message());
|
||||
}
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
util::Status KeyboxClientCert::VerifySignature(
|
||||
const std::string& message, const std::string& signature,
|
||||
ProtocolVersion protocol_version) {
|
||||
Status KeyboxClientCert::VerifySignature(const std::string& message,
|
||||
const std::string& signature,
|
||||
ProtocolVersion protocol_version) {
|
||||
DCHECK(!signing_key().empty());
|
||||
using crypto_util::VerifySignatureHmacSha256;
|
||||
if (!VerifySignatureHmacSha256(
|
||||
GetClientSigningKey(signing_key(), protocol_version), signature,
|
||||
message)) {
|
||||
return util::Status(error_space, INVALID_SIGNATURE, "invalid-keybox-mac");
|
||||
return Status(error_space, INVALID_SIGNATURE, "invalid-keybox-mac");
|
||||
}
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
CertificateClientCert::CertificateClientCert() {}
|
||||
|
||||
CertificateClientCert::~CertificateClientCert() {}
|
||||
|
||||
util::Status CertificateClientCert::Initialize(
|
||||
Status CertificateClientCert::Initialize(
|
||||
const DrmRootCertificate* drm_root_certificate,
|
||||
const std::string& serialized_certificate) {
|
||||
CHECK(drm_root_certificate);
|
||||
|
||||
SignedDrmCertificate signed_device_cert;
|
||||
DrmCertificate device_cert;
|
||||
util::Status status = drm_root_certificate->VerifyCertificate(
|
||||
Status status = drm_root_certificate->VerifyCertificate(
|
||||
serialized_certificate, &signed_device_cert, &device_cert);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
@@ -192,12 +192,12 @@ util::Status CertificateClientCert::Initialize(
|
||||
const SignedDrmCertificate& signer = signed_device_cert.signer();
|
||||
DrmCertificate model_certificate;
|
||||
if (!model_certificate.ParseFromString(signer.drm_certificate())) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"drm-certificate-invalid-signer");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"drm-certificate-invalid-signer");
|
||||
}
|
||||
if (!model_certificate.has_serial_number()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-signer-serial-number");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-signer-serial-number");
|
||||
}
|
||||
// Check to see if this model certificate is signed by a
|
||||
// provisioner (entity using Widevine Provisioning Server SDK).
|
||||
@@ -205,56 +205,58 @@ util::Status CertificateClientCert::Initialize(
|
||||
DrmCertificate provisioner_certificate;
|
||||
if (!provisioner_certificate.ParseFromString(
|
||||
signer.signer().drm_certificate())) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"model-certificate-invalid-signer");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"model-certificate-invalid-signer");
|
||||
}
|
||||
if (provisioner_certificate.type() == DrmCertificate::PROVISIONER) {
|
||||
set_signed_by_provisioner(true);
|
||||
} else {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"expected-provisioning-provider-certificate-type");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"expected-provisioning-provider-certificate-type");
|
||||
}
|
||||
if (!provisioner_certificate.has_provider_id() ||
|
||||
provisioner_certificate.provider_id().empty()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-provisioning-service-id");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-provisioning-service-id");
|
||||
}
|
||||
set_service_id(provisioner_certificate.provider_id());
|
||||
}
|
||||
set_signer_serial_number(model_certificate.serial_number());
|
||||
set_signer_creation_time_seconds(model_certificate.creation_time_seconds());
|
||||
if (!model_certificate.has_system_id()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"model-certificate-missing-system-id");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"model-certificate-missing-system-id");
|
||||
}
|
||||
set_system_id(model_certificate.system_id());
|
||||
set_serial_number(device_cert.serial_number());
|
||||
set_public_key(device_cert.public_key());
|
||||
rsa_public_key_.reset(RsaPublicKey::Create(public_key()));
|
||||
if (rsa_public_key_ == nullptr) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"drm-certificate-public-key-failed");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"drm-certificate-public-key-failed");
|
||||
}
|
||||
|
||||
// TODO(user): Move this somewhere else. It is license protocol.
|
||||
set_key(Random16Bytes());
|
||||
if (!rsa_public_key_->Encrypt(key(), &encrypted_session_key_)) {
|
||||
return util::Status(error_space, ENCRYPT_ERROR,
|
||||
"drm-certificate-failed-encrypt-session-key");
|
||||
return Status(error_space, ENCRYPT_ERROR,
|
||||
"drm-certificate-failed-encrypt-session-key");
|
||||
}
|
||||
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
util::Status CertificateClientCert::VerifySignature(
|
||||
Status CertificateClientCert::VerifySignature(
|
||||
const std::string& message, const std::string& signature,
|
||||
ProtocolVersion protocol_version) {
|
||||
CHECK(rsa_public_key_);
|
||||
|
||||
if (!rsa_public_key_->VerifySignature(message, signature)) {
|
||||
return util::Status(error_space, INVALID_SIGNATURE, "");
|
||||
if (!rsa_public_key_->VerifySignature(
|
||||
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
|
||||
signature)) {
|
||||
return Status(error_space, INVALID_SIGNATURE, "");
|
||||
}
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
Reference in New Issue
Block a user