//////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Google LLC. // // 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 "common/client_cert.h" #include #include #include #include "glog/logging.h" #include "strings/serialize.h" #include "absl/strings/escaping.h" #include "absl/synchronization/mutex.h" #include "util/gtl/map_util.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" namespace widevine { namespace { const int kKeyboxSizeBytes = 72; } // namespace // instead of ClientCert** to explicitly assigning ownership of the created // object to the caller. 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 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 Status(error_space, error::UNIMPLEMENTED, "client-type-not-implemented"); } } Status ClientCert::CreateWithKeybox(const std::string& keybox_token, ClientCert** client_cert) { CHECK(client_cert); *client_cert = nullptr; std::unique_ptr new_client_cert(new KeyboxClientCert); Status status = new_client_cert->Initialize(keybox_token); if (!status.ok()) { return status; } *client_cert = new_client_cert.release(); return OkStatus(); } Status ClientCert::CreateWithDrmCertificate( const DrmRootCertificate* root_certificate, const std::string& drm_certificate, ClientCert** client_cert) { CHECK(client_cert); *client_cert = nullptr; std::unique_ptr new_client_cert( new CertificateClientCert); Status status = new_client_cert->Initialize(root_certificate, drm_certificate); if (!status.ok()) { return status; } *client_cert = new_client_cert.release(); return OkStatus(); } void ClientCert::CreateSignature(const std::string& message, std::string* signature) { DCHECK(signature); DCHECK(!signing_key().empty()); if (signature == nullptr) { return; } using crypto_util::CreateSignatureHmacSha256; *signature = CreateSignatureHmacSha256(GetServerSigningKey(signing_key()), message); } void ClientCert::GenerateSigningKey(const std::string& message, ProtocolVersion protocol_version) { if (!signing_key_.empty()) return; DCHECK(!key().empty()); using crypto_util::DeriveKey; using crypto_util::kSigningKeyLabel; set_signing_key( DeriveKey(key(), kSigningKeyLabel, protocol_version < VERSION_2_2 ? message : Sha512_Hash(message), SigningKeyMaterialSizeBits(protocol_version))); } KeyboxClientCert::KeyboxClientCert() {} KeyboxClientCert::~KeyboxClientCert() {} void KeyboxClientCert::SetPreProvisioningKeys( const std::multimap& keymap) { std::vector keyvector; keyvector.reserve(keymap.size()); for (std::multimap::const_iterator it = keymap.begin(); it != keymap.end(); ++it) { std::string key = absl::HexStringToBytes(it->second); DCHECK_EQ(key.size(), 16); keyvector.push_back(WvmTokenHandler::PreprovKey(it->first, key)); } WvmTokenHandler::SetPreprovKeys(keyvector); } bool KeyboxClientCert::IsSystemIdKnown(const uint32_t system_id) { return WvmTokenHandler::IsSystemIdKnown(system_id); } uint32_t KeyboxClientCert::GetSystemId(const std::string& keybox_bytes) { return WvmTokenHandler::GetSystemId(keybox_bytes); } Status KeyboxClientCert::Initialize(const std::string& keybox_bytes) { if (keybox_bytes.size() < kKeyboxSizeBytes) { 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; Status status = WvmTokenHandler::DecryptDeviceKey(keybox_bytes, &device_key_, nullptr, &insecure_keybox); if (!status.ok()) { Errors new_code = status.error_code() == error::NOT_FOUND ? MISSING_PRE_PROV_KEY : KEYBOX_DECRYPT_ERROR; return Status(error_space, new_code, status.error_message()); } return OkStatus(); } 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 Status(error_space, INVALID_SIGNATURE, "invalid-keybox-mac"); } return OkStatus(); } CertificateClientCert::CertificateClientCert() {} CertificateClientCert::~CertificateClientCert() {} Status CertificateClientCert::Initialize( const DrmRootCertificate* drm_root_certificate, const std::string& serialized_certificate) { CHECK(drm_root_certificate); SignedDrmCertificate signed_device_cert; DrmCertificate device_cert; Status status = drm_root_certificate->VerifyCertificate( serialized_certificate, &signed_device_cert, &device_cert); if (!status.ok()) { return status; } const SignedDrmCertificate& signer = signed_device_cert.signer(); DrmCertificate model_certificate; if (!model_certificate.ParseFromString(signer.drm_certificate())) { return Status(error_space, INVALID_DRM_CERTIFICATE, "drm-certificate-invalid-signer"); } if (!model_certificate.has_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). if (signer.has_signer()) { DrmCertificate provisioner_certificate; if (!provisioner_certificate.ParseFromString( signer.signer().drm_certificate())) { return Status(error_space, INVALID_DRM_CERTIFICATE, "model-certificate-invalid-signer"); } if (provisioner_certificate.type() == DrmCertificate::PROVISIONER) { set_signed_by_provisioner(true); } else { return Status(error_space, INVALID_DRM_CERTIFICATE, "expected-provisioning-provider-certificate-type"); } if (!provisioner_certificate.has_provider_id() || provisioner_certificate.provider_id().empty()) { 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 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 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 Status(error_space, ENCRYPT_ERROR, "drm-certificate-failed-encrypt-session-key"); } return OkStatus(); } Status CertificateClientCert::VerifySignature( const std::string& message, const std::string& signature, ProtocolVersion protocol_version) { CHECK(rsa_public_key_); if (!rsa_public_key_->VerifySignature( protocol_version < VERSION_2_2 ? message : Sha512_Hash(message), signature)) { return Status(error_space, INVALID_SIGNATURE, ""); } return OkStatus(); } } // namespace widevine