Minimal implementation of Widevine MediaCAS ECMG.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=226515998
This commit is contained in:
Fang Yu
2018-12-21 11:17:37 -08:00
parent 7487ce5aa8
commit bc68878bdf
88 changed files with 2456 additions and 2774 deletions

View File

@@ -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