Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=224206719
This commit is contained in:
Ramji Chandramouli
2018-12-05 13:02:27 -08:00
committed by Fang Yu
parent df7566c0c1
commit 7f649cf826
49 changed files with 2697 additions and 2130 deletions

View File

@@ -25,14 +25,6 @@ package_group(
)
filegroup(
name = "binary_release_files",
srcs = [
"client_cert.h",
],
visibility = ["//visibility:public"],
)
cc_library(
name = "session_impl",
srcs = [
@@ -52,14 +44,15 @@ cc_library(
"//util:status",
"//common:aes_cbc_util",
"//common:certificate_type",
"//common:certificate_util",
"//common:client_cert",
"//common:crypto_util",
"//common:drm_root_certificate",
"//common:drm_service_certificate",
"//common:device_status_list",
"//common:error_space",
"//common:random_util",
"//common:remote_attestation_verifier",
"//common:drm_root_certificate",
"//common:rsa_key",
"//common:drm_service_certificate",
"//common:signing_key_util",
"//common:verified_media_pipeline",
"//common:vmp_checker",
@@ -75,15 +68,11 @@ cc_library(
cc_library(
name = "sdk",
srcs = [
"client_cert.cc",
"device_status_list.cc",
"key_control_block.cc",
"parse_content_id.cc",
"generate_error_response.cc",
],
hdrs = [
"client_cert.h",
"device_status_list.h",
"generate_error_response.h",
"key_control_block.h",
"parse_content_id.h",
@@ -98,11 +87,14 @@ cc_library(
"//util/endian",
"//util/gtl:map_util",
"//util:status",
"//common:client_cert",
"//common:crypto_util",
"//common:drm_service_certificate",
"//common:device_status_list",
"//common:error_space",
"//common:random_util",
"//common:rsa_key",
"//common:drm_root_certificate",
"//common:drm_service_certificate",
"//common:signing_key_util",
"//common:wvm_token_handler",
"//sdk/external/common/wvpl:wvpl_types",
@@ -130,15 +122,17 @@ cc_test(
"//testing:gunit_main",
"@abseil_repo//absl/strings",
"//common:aes_cbc_util",
"//common:client_cert",
"//common:crypto_util",
"//common:drm_root_certificate",
"//common:error_space",
"//common:remote_attestation_verifier",
"//common:device_status_list",
"//common:drm_root_certificate",
"//common:rsa_key",
"//common:rsa_test_keys",
"//common:rsa_util",
"//common:signing_key_util",
"//common:test_certificates",
"//common:test_drm_certificates",
"//common:test_utils",
"//protos/public:client_identification_proto",
"//protos/public:device_certificate_status_proto",
@@ -168,24 +162,6 @@ cc_test(
],
)
cc_test(
name = "device_status_list_test",
timeout = "short",
srcs = ["device_status_list_test.cc"],
deps = [
":sdk",
"//base",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
"//common:rsa_key",
"//common:rsa_test_keys",
"//protos/public:client_identification_proto",
"//protos/public:errors_proto",
"//protos/public:provisioned_device_info_proto",
"//protos/public:signed_drm_certificate_proto",
],
)
cc_test(
name = "parse_content_id_test",
timeout = "short",

View File

@@ -1,408 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// 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 "license_server_sdk/internal/client_cert.h"
#include <memory>
#include <utility>
#include <vector>
#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 "util/status.h"
#include "common/crypto_util.h"
#include "common/error_space.h"
#include "common/random_util.h"
#include "common/signing_key_util.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 kPreProvisioningKeySizeBytes = 16;
const int kKeyboxSizeBytes = 72;
struct ValidatedSignerCertificate {
std::string certificate;
std::string signature;
};
class ValidatedSignerCache {
public:
void SetRootPublicKey(std::unique_ptr<RsaPublicKey> new_root_key);
bool Exist(const std::string& serial_number, const std::string& certificate,
const std::string& signature);
util::Status ValidateSigner(const std::string& serial_number,
const std::string& certificate,
const std::string& signature);
void ResetSignerCache();
size_t SignerCacheSize();
static ValidatedSignerCache* GetSingleton();
private:
absl::Mutex rsa_root_public_key_mutex_;
std::unique_ptr<RsaPublicKey> rsa_root_public_key_
GUARDED_BY(&rsa_root_public_key_mutex_);
absl::Mutex validated_signer_cache_mutex_;
std::map<std::string, ValidatedSignerCertificate> validated_signer_cache_
GUARDED_BY(&validated_signer_cache_mutex_);
};
void ValidatedSignerCache::SetRootPublicKey(
std::unique_ptr<RsaPublicKey> new_root_key) {
absl::WriterMutexLock lock(&rsa_root_public_key_mutex_);
rsa_root_public_key_ = std::move(new_root_key);
}
bool ValidatedSignerCache::Exist(const std::string& serial_number,
const std::string& certificate,
const std::string& signature) {
absl::ReaderMutexLock lock(&validated_signer_cache_mutex_);
ValidatedSignerCertificate* validated_signer =
gtl::FindOrNull(validated_signer_cache_, serial_number);
return (validated_signer != nullptr) &&
(validated_signer->certificate == certificate) &&
(validated_signer->signature == signature);
}
util::Status ValidatedSignerCache::ValidateSigner(const std::string& serial_number,
const std::string& certificate,
const std::string& signature) {
{
absl::ReaderMutexLock key_lock(&rsa_root_public_key_mutex_);
if (rsa_root_public_key_ == nullptr) {
return util::Status(error_space, ROOT_CERTIFICATE_NOT_SET, "");
}
if (!rsa_root_public_key_->VerifySignature(certificate, signature)) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
"signer-certificate-verification-failed");
}
}
absl::WriterMutexLock cache_lock(&validated_signer_cache_mutex_);
validated_signer_cache_[serial_number].certificate = certificate;
validated_signer_cache_[serial_number].signature = signature;
return util::OkStatus();
}
void ValidatedSignerCache::ResetSignerCache() {
absl::WriterMutexLock lock(&validated_signer_cache_mutex_);
validated_signer_cache_.clear();
}
size_t ValidatedSignerCache::SignerCacheSize() {
absl::ReaderMutexLock lock(&validated_signer_cache_mutex_);
return validated_signer_cache_.size();
}
ValidatedSignerCache* ValidatedSignerCache::GetSingleton() {
static auto* const kInstance = new ValidatedSignerCache();
return kInstance;
}
} // 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(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 ClientCert::CreateWithToken(token, client_cert);
} else if (token_type == ClientIdentification::DRM_DEVICE_CERTIFICATE) {
return CreateCertificateClientCert(token, client_cert);
} else {
return util::Status(error_space, util::error::UNIMPLEMENTED,
"client-type-not-implemented");
}
}
util::Status ClientCert::CreateWithToken(const std::string& keybox_token,
ClientCert** client_cert) {
*client_cert = nullptr;
std::unique_ptr<ClientCert> ret(new KeyboxClientCert(keybox_token));
if (ret->status() != util::OkStatus()) {
return ret->status();
}
*client_cert = ret.release();
return util::OkStatus();
}
util::Status ClientCert::CreateCertificateClientCert(
const std::string& drm_certificate, ClientCert** client_cert) {
std::unique_ptr<ClientCert> ret(new CertificateClientCert(drm_certificate));
if (ret->status() != util::OkStatus()) {
return ret->status();
}
*client_cert = ret.release();
return util::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, message,
SigningKeyMaterialSize(protocol_version)));
}
KeyboxClientCert::~KeyboxClientCert() {}
void KeyboxClientCert::SetPreProvisioningKeys(
const std::multimap<uint32_t, std::string>& keymap) {
std::vector<WvmTokenHandler::PreprovKey> keyvector;
keyvector.reserve(keymap.size());
for (std::multimap<uint32_t, std::string>::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);
}
KeyboxClientCert::KeyboxClientCert(const std::string& keybox_bytes) {
if (keybox_bytes.size() < kKeyboxSizeBytes) {
set_status(util::Status(error_space, INVALID_KEYBOX_TOKEN,
"keybox-token-is-too-short"));
return;
}
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);
if (!status.ok()) {
Errors new_code = status.error_code() == util::error::NOT_FOUND
? MISSING_PRE_PROV_KEY
: KEYBOX_DECRYPT_ERROR;
set_status(util::Status(error_space, new_code, status.error_message()));
}
}
bool 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)) {
set_status(util::Status(error_space, INVALID_SIGNATURE, ""));
return false;
}
return true;
}
util::Status CertificateClientCert::SetDrmRootCertificatePublicKey(
const std::string& root_public_key) {
std::unique_ptr<RsaPublicKey> new_root_key(
RsaPublicKey::Create(root_public_key));
if (new_root_key == nullptr) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
"root-certificate-rsa-public-key-failed");
}
ValidatedSignerCache::GetSingleton()->SetRootPublicKey(
std::move(new_root_key));
return util::OkStatus();
}
// Checks the device certificate using the following steps.
// 1. Load the certificate bytes into a signed device certificate.
// 2. Get the signer for the certificate.
// 3. Verify the signature of the certificate using the signer.
// 4. Load the root certificate.
// 5. Verify the signature of the signer certificate.
util::Status CertificateClientCert::ValidateCertificate(
const SignedDrmCertificate& signed_drm_certificate) {
// TODO(user): Cache valid certificates.
// TODO(user): Find out why signed_drm_certificate.has_signer() always
// returns false. Blindly assuming signer is there for now.
const SignedDrmCertificate& signer = signed_drm_certificate.signer();
DrmCertificate intermediate_certificate;
if (!intermediate_certificate.ParseFromString(signer.drm_certificate())) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
"device-certificate-invalid-signer");
}
std::unique_ptr<RsaPublicKey> rsa_public_signer_key(
RsaPublicKey::Create(intermediate_certificate.public_key()));
if (!rsa_public_signer_key.get()) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
"signer-certificate-public-key-failed");
}
if (!rsa_public_signer_key->VerifySignature(
signed_drm_certificate.drm_certificate(),
signed_drm_certificate.signature())) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
"device-certificate-verification-failed");
}
if (!intermediate_certificate.has_serial_number()) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-signer-serial-number");
}
// Check to see if this intermediate device certificate is signed by a
// provisioner (entity using Widevine Provisioning Server SDK).
// TODO(user): refactor this code for clarity with the cert chaining.
if (signer.has_signer()) {
DrmCertificate provisioner_certificate;
if (!provisioner_certificate.ParseFromString(
signer.signer().drm_certificate())) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
"intermediate-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");
}
if (!CheckSignerCache(provisioner_certificate.serial_number(),
signer.signer().drm_certificate(),
signer.signature())) {
util::Status status = ValidateSigner(
provisioner_certificate.serial_number(),
signer.signer().drm_certificate(), signer.signer().signature());
if (!status.ok()) {
return status;
}
}
if (!provisioner_certificate.has_provider_id() ||
provisioner_certificate.provider_id().empty()) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-provisioning-service-id");
}
set_service_id(provisioner_certificate.provider_id());
} else {
if (!CheckSignerCache(intermediate_certificate.serial_number(),
signer.drm_certificate(), signer.signature())) {
util::Status status =
ValidateSigner(intermediate_certificate.serial_number(),
signer.drm_certificate(), signer.signature());
if (!status.ok()) {
return status;
}
}
}
set_signer_serial_number(intermediate_certificate.serial_number());
set_signer_creation_time_seconds(
intermediate_certificate.creation_time_seconds());
return util::OkStatus();
}
CertificateClientCert::CertificateClientCert(
const std::string& signed_drm_certificate_bytes) {
SignedDrmCertificate signed_drm_certificate;
if (!signed_drm_certificate.ParseFromString(signed_drm_certificate_bytes)) {
set_status(util::Status(error_space, INVALID_DRM_CERTIFICATE,
"device-certificate-invalid-token"));
return;
}
util::Status status = ValidateCertificate(signed_drm_certificate);
if (!status.ok()) {
set_status(status);
return;
}
DrmCertificate drm_certificate;
if (!drm_certificate.ParseFromString(
signed_drm_certificate.drm_certificate())) {
set_status(util::Status(error_space, INVALID_DRM_CERTIFICATE,
"device-certificate-invalid"));
return;
}
if (!drm_certificate.has_system_id()) {
set_status(util::Status(error_space, INVALID_DRM_CERTIFICATE,
"device-certificate-missing-system-id"));
}
set_system_id(drm_certificate.system_id());
set_serial_number(drm_certificate.serial_number());
set_public_key(drm_certificate.public_key());
rsa_public_key_.reset(RsaPublicKey::Create(public_key()));
if (rsa_public_key_ == nullptr) {
set_status(util::Status(error_space, INVALID_DRM_CERTIFICATE,
"device-certificate-public-key-failed"));
return;
}
set_key(Random16Bytes());
if (!rsa_public_key_->Encrypt(key(), &encrypted_session_key_)) {
set_status(util::Status(error_space, ENCRYPT_ERROR,
"device-certificate-failed-encrypt-session-key"));
return;
}
}
CertificateClientCert::~CertificateClientCert() {}
bool CertificateClientCert::VerifySignature(const std::string& message,
const std::string& signature,
ProtocolVersion protocol_version) {
if (!rsa_public_key_->VerifySignature(message, signature)) {
set_status(util::Status(error_space, INVALID_SIGNATURE, ""));
return false;
}
return true;
}
bool CertificateClientCert::CheckSignerCache(const std::string& serial_number,
const std::string& certificate,
const std::string& signature) const {
return ValidatedSignerCache::GetSingleton()->Exist(serial_number, certificate,
signature);
}
util::Status CertificateClientCert::ValidateSigner(const std::string& serial_number,
const std::string& certificate,
const std::string& signature) {
return ValidatedSignerCache::GetSingleton()->ValidateSigner(
serial_number, certificate, signature);
}
void CertificateClientCert::ResetSignerCache() {
ValidatedSignerCache::GetSingleton()->ResetSignerCache();
}
size_t CertificateClientCert::SignerCacheSize() {
return ValidatedSignerCache::GetSingleton()->SignerCacheSize();
}
} // namespace widevine

View File

@@ -1,216 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// 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.
////////////////////////////////////////////////////////////////////////////////
#ifndef LICENSE_SERVER_SDK_INTERNAL_CLIENT_CERT_H__
#define LICENSE_SERVER_SDK_INTERNAL_CLIENT_CERT_H__
#include <map>
#include <memory>
#include <string>
#include "util/status.h"
#include "common/rsa_key.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/license_protocol.pb.h"
namespace widevine {
class SignedDrmCertificate;
// Handler class for LicenseRequests; validates requests and encrypts licenses.
// TODO(user): Remove extra accessors after Keybox parsing is moved
// to a separate class in KeyboxClientCert class.
class ClientCert {
public:
virtual ~ClientCert() {}
static util::Status Create(
widevine::ClientIdentification::TokenType token_type,
const std::string& token, ClientCert** client_cert);
// Creates a Keybox based ClientCert.
static util::Status CreateWithToken(const std::string& keybox_token,
ClientCert** client_cert);
// Creates a Device Certificate based ClientCert.
static util::Status CreateCertificateClientCert(const std::string& drm_certificate,
ClientCert** client_cert);
// Creates a HMAC SHA256 signature based on the message and the key().
// signature is owned by the caller and can not be NULL.
virtual void CreateSignature(const std::string& message, std::string* signature);
// Checks the passed in signature against a signature created used the
// classes information and the passed in message. Returns true if signature
// is valid.
virtual bool VerifySignature(const std::string& message, const std::string& signature,
ProtocolVersion protocol_version) = 0;
// Creates a signing_key that is accessible using signing_key(). Signing_key
// is constructed by doing a key derivation using the key() and message.
virtual void GenerateSigningKey(const std::string& message,
ProtocolVersion protocol_version);
// Used to create signing keys. For Keybox token types this is the device key.
// For Device Certificate token types this the session key.
virtual const std::string& key() const = 0;
virtual void set_key(const std::string& key) = 0;
virtual const std::string& encrypted_key() const = 0;
virtual uint32_t system_id() const { return system_id_; }
virtual const std::string& signing_key() const { return signing_key_; }
virtual const std::string& public_key() const { return public_key_; }
virtual const std::string& serial_number() const { return serial_number_; }
virtual void set_serial_number(const std::string& serial_number) {
serial_number_ = serial_number;
}
virtual const std::string& signer_serial_number() const {
return signer_serial_number_;
}
virtual uint32_t signer_creation_time_seconds() const {
return signer_creation_time_seconds_;
}
virtual const util::Status& status() const { return status_; }
virtual widevine::ClientIdentification::TokenType type() const = 0;
virtual std::string service_id() const { return service_id_; }
virtual bool signed_by_provisioner() const { return signed_by_provisioner_; }
protected:
ClientCert() {}
virtual void set_system_id(uint32_t system_id) { system_id_ = system_id; }
virtual void set_signing_key(const std::string& signing_key) {
signing_key_ = signing_key;
}
virtual void set_status(const util::Status& status) { status_ = status; }
virtual void set_service_id(const std::string& service_id) {
service_id_ = service_id;
}
virtual void set_signed_by_provisioner(bool provisioner_signed_flag) {
signed_by_provisioner_ = provisioner_signed_flag;
}
std::string public_key_;
std::string serial_number_;
std::string signer_serial_number_;
uint32_t signer_creation_time_seconds_ = 0;
bool signed_by_provisioner_ = false;
private:
uint32_t system_id_ = 0;
std::string signing_key_;
util::Status status_;
std::string service_id_;
DISALLOW_COPY_AND_ASSIGN(ClientCert);
};
// This class implements the crypto operations based on the Widevine keybox.
// It will unpack token and perform all the crypto operations for securing
// the key material in the license response.
class KeyboxClientCert : public ClientCert {
public:
~KeyboxClientCert() override;
// Set the system-wide pre-provisioning keys; argument must be human-readable
// hex digits.
// Must be called before any other method of this class is called, unless
// created by ClientCert::CreateWithPreProvisioningKey(...).
static void SetPreProvisioningKeys(const std::multimap<uint32_t, std::string>& keys);
static bool IsSystemIdKnown(const uint32_t system_id);
static uint32_t GetSystemId(const std::string& keybox_bytes);
bool VerifySignature(const std::string& message, const std::string& signature,
ProtocolVersion protocol_version) override;
const std::string& key() const override { return device_key_; }
void set_key(const std::string& key) override { device_key_ = key; }
const std::string& encrypted_key() const override { return encrypted_device_key_; }
widevine::ClientIdentification::TokenType type() const override {
return widevine::ClientIdentification::KEYBOX;
}
private:
friend class ClientCert;
friend class MockKeyboxClientCert;
explicit KeyboxClientCert(const std::string& keybox_bytes);
std::string device_key_;
std::string encrypted_device_key_;
DISALLOW_IMPLICIT_CONSTRUCTORS(KeyboxClientCert);
};
// This class implements the device certificate operations based on RSA keys.
// It will unpack token and perform all the crypto operations for securing
// the key material in the license response.
using widevine::RsaPublicKey;
class CertificateClientCert : public ClientCert {
public:
~CertificateClientCert() override;
// Sets the root certificate for certificate validation.
static util::Status SetDrmRootCertificatePublicKey(
const std::string& root_public_key);
bool VerifySignature(const std::string& message, const std::string& signature,
ProtocolVersion protocol_version) override;
const std::string& key() const override { return session_key_; }
void set_key(const std::string& key) override { session_key_ = key; }
const std::string& encrypted_key() const override {
return encrypted_session_key_;
}
widevine::ClientIdentification::TokenType type() const override {
return widevine::ClientIdentification::DRM_DEVICE_CERTIFICATE;
}
protected:
friend class ClientCert;
friend class MockCertificateClientCert;
explicit CertificateClientCert(const std::string& signed_drm_certificate_bytes);
util::Status ValidateCertificate(
const SignedDrmCertificate& signed_drm_certificate);
virtual void set_public_key(const std::string& public_key) {
public_key_ = public_key;
}
virtual void set_signer_serial_number(const std::string& signer_serial_number) {
signer_serial_number_ = signer_serial_number;
}
virtual void set_signer_creation_time_seconds(uint32_t creation_time_seconds) {
signer_creation_time_seconds_ = creation_time_seconds;
}
// This method checks to see if a cached signature for the signer certificate
// exists. The cache is populated by the ValidateSignature method, below.
// - serial_number is the signer (intermediate) certificate serial number.
// This method does a cached signature lookup using this value as the key.
// - certificate is the serialized signer certificate. This method compares
// this value to the value cached to determine whether there is a match.
// - signature is the signature for the serialized signer certificate. This
// method compares this value to the value cached to determine whether
// there is a match.
// Returns true if there exists a matching cached signature for the signer
// certificate with the specified serial number, serialized
// DrmCertificate, and serialized DrmCertificate signature.
// Returns false otherwise.
virtual bool CheckSignerCache(const std::string& serial_number,
const std::string& certificate,
const std::string& signature) const;
// This method verifies the signature of a signer (intermediate) certificate,
// caching it in the signer cache if verification succeeds.
// - serial_number is the signer (intermediate) certificate serial number.
// - certificate is the serialized signer certificate.
// - signature is the signature for the serialized signer certificate, signed
// with the root certificate private key.
// Returns util::Status::OK and caches the validated signer information in
// the signer cache if signature validation succeeds. Otherwise, returns
virtual util::Status ValidateSigner(const std::string& serial_number,
const std::string& certificate,
const std::string& signature);
// The below two functions are only used for testing.
static void ResetSignerCache();
static size_t SignerCacheSize();
std::string session_key_;
std::string encrypted_session_key_;
std::unique_ptr<RsaPublicKey> rsa_public_key_;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(CertificateClientCert);
};
} // namespace widevine
#endif // LICENSE_SERVER_SDK_INTERNAL_CLIENT_CERT_H__

View File

@@ -1,607 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// 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 "license_server_sdk/internal/client_cert.h"
#include <stddef.h>
#include <memory>
#include <string>
#include <utility>
#include "glog/logging.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "common/drm_root_certificate.h"
#include "common/error_space.h"
#include "common/rsa_test_keys.h"
#include "common/wvm_test_keys.h"
#include "protos/public/drm_certificate.pb.h"
#include "protos/public/errors.pb.h"
#include "protos/public/signed_drm_certificate.pb.h"
// TODO(user): Change these tests to use on-the-fly generated intermediate
// and device certificates based on RsaTestKeys.
// TODO(user): Add testcase(s) VerifySignature, CreateSignature,
// and GenerateSigningKey.
namespace widevine {
using ::testing::_;
using ::testing::Return;
class ClientCertTest : public ::testing::Test {
public:
void SetUp() override {
if (!setup_preprov_keys_) {
KeyboxClientCert::SetPreProvisioningKeys(
wvm_test_keys::GetPreprovKeyMultimap());
setup_preprov_keys_ = true;
}
CHECK_OK(CertificateClientCert::SetDrmRootCertificatePublicKey(
test_keys_.public_test_key_1_3072_bits()));
}
protected:
// Simple container struct for test value and expected keys.
class TestTokenAndKeys {
public:
const std::string token_;
uint32_t expected_system_id_;
const std::string expected_serial_number_;
const std::string expected_device_key_;
TestTokenAndKeys(const std::string& token, uint32_t expected_system_id,
const std::string& expected_serial_number,
const std::string& expected_device_key)
: token_(token),
expected_system_id_(expected_system_id),
expected_serial_number_(expected_serial_number),
expected_device_key_(expected_device_key) {}
};
class TestCertificateAndData {
public:
const std::string certificate_;
const std::string expected_serial_number_;
uint32_t expected_system_id_;
util::Status expected_status_;
TestCertificateAndData(const std::string& certificate,
const std::string& expected_serial_number,
uint32_t expected_system_id,
util::Status expected_status)
: certificate_(certificate),
expected_serial_number_(expected_serial_number),
expected_system_id_(expected_system_id),
expected_status_(std::move(expected_status)) {}
};
void TestBasicValidation(const TestTokenAndKeys& expectation,
const bool expect_success,
const bool compare_device_key);
void TestBasicValidationDrmCertificate(
const TestCertificateAndData& expectation, const bool compare_data);
void GenerateSignature(const std::string& message, const std::string& private_key,
std::string* signature);
SignedDrmCertificate* SignCertificate(const DrmCertificate& certificate,
SignedDrmCertificate* signer,
const std::string& private_key);
DrmCertificate* GenerateProvisionerCertificate(uint32_t system_id,
const std::string& serial_number,
const std::string& provider_id);
SignedDrmCertificate* GenerateSignedProvisionerCertificate(
uint32_t system_id, const std::string& serial_number, const std::string& service_id);
DrmCertificate* GenerateIntermediateCertificate(uint32_t system_id,
const std::string& serial_number);
SignedDrmCertificate* GenerateSignedIntermediateCertificate(
SignedDrmCertificate* signer, uint32_t system_id,
const std::string& serial_number);
DrmCertificate* GenerateDrmCertificate(uint32_t system_id,
const std::string& serial_number);
SignedDrmCertificate* GenerateSignedDrmCertificate(
SignedDrmCertificate* signer, uint32_t system_id,
const std::string& serial_number);
RsaTestKeys test_keys_;
static bool setup_preprov_keys_;
};
bool ClientCertTest::setup_preprov_keys_(false);
void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation,
const bool expect_success,
const bool compare_device_key) {
// Test validation of a valid request.
util::Status status;
ClientCert* client_cert_ptr = nullptr;
// Two ways to create a client cert object, test both.
for (int i = 0; i < 2; i++) {
if (i == 0) {
status = ClientCert::Create(ClientIdentification::KEYBOX,
expectation.token_, &client_cert_ptr);
} else {
status =
ClientCert::CreateWithToken(expectation.token_, &client_cert_ptr);
}
std::unique_ptr<ClientCert> keybox_cert(client_cert_ptr);
if (expect_success) {
ASSERT_EQ(util::OkStatus(), status);
ASSERT_TRUE(keybox_cert.get());
EXPECT_EQ(expectation.expected_system_id_, keybox_cert->system_id());
EXPECT_EQ(expectation.expected_serial_number_,
keybox_cert->serial_number());
if (compare_device_key) {
EXPECT_EQ(expectation.expected_device_key_, keybox_cert->key());
}
} else {
EXPECT_NE(util::OkStatus(), status);
EXPECT_FALSE(keybox_cert);
}
}
}
void ClientCertTest::TestBasicValidationDrmCertificate(
const TestCertificateAndData& expectation, const bool compare_data) {
// Test validation of a valid request.
util::Status status;
ClientCert* client_cert_ptr = nullptr;
status = ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE,
expectation.certificate_, &client_cert_ptr);
std::unique_ptr<ClientCert> drm_certificate_cert(client_cert_ptr);
ASSERT_EQ(expectation.expected_status_, status);
if (expectation.expected_status_.ok()) {
ASSERT_TRUE(drm_certificate_cert.get());
if (compare_data) {
ASSERT_EQ(expectation.expected_serial_number_,
drm_certificate_cert->signer_serial_number());
ASSERT_EQ(expectation.expected_system_id_,
drm_certificate_cert->system_id());
}
} else {
ASSERT_FALSE(drm_certificate_cert.get());
}
}
void ClientCertTest::GenerateSignature(const std::string& message,
const std::string& private_key,
std::string* signature) {
std::unique_ptr<RsaPrivateKey> rsa_private_key(
RsaPrivateKey::Create(private_key));
ASSERT_TRUE(rsa_private_key != nullptr);
rsa_private_key->GenerateSignature(message, signature);
}
// The caller relinquishes ownership of |signer|, which may also be nullptr.
SignedDrmCertificate* ClientCertTest::SignCertificate(
const DrmCertificate& certificate, SignedDrmCertificate* signer,
const std::string& private_key) {
std::unique_ptr<SignedDrmCertificate> signed_certificate(
new SignedDrmCertificate);
signed_certificate->set_drm_certificate(certificate.SerializeAsString());
GenerateSignature(signed_certificate->drm_certificate(), private_key,
signed_certificate->mutable_signature());
if (signer != nullptr) {
signed_certificate->set_allocated_signer(signer);
}
return signed_certificate.release();
}
DrmCertificate* ClientCertTest::GenerateIntermediateCertificate(
uint32_t system_id, const std::string& serial_number) {
std::unique_ptr<DrmCertificate> intermediate_certificate(new DrmCertificate);
intermediate_certificate->set_type(DrmCertificate::DEVICE_MODEL);
intermediate_certificate->set_serial_number(serial_number);
intermediate_certificate->set_public_key(
test_keys_.public_test_key_2_2048_bits());
intermediate_certificate->set_system_id(system_id);
intermediate_certificate->set_creation_time_seconds(1234);
return intermediate_certificate.release();
}
SignedDrmCertificate* ClientCertTest::GenerateSignedIntermediateCertificate(
SignedDrmCertificate* signer, uint32_t system_id,
const std::string& serial_number) {
std::unique_ptr<DrmCertificate> intermediate_certificate(
GenerateIntermediateCertificate(system_id, serial_number));
return SignCertificate(*intermediate_certificate, signer,
test_keys_.private_test_key_1_3072_bits());
}
DrmCertificate* ClientCertTest::GenerateDrmCertificate(
uint32_t system_id, const std::string& serial_number) {
std::unique_ptr<DrmCertificate> drm_certificate(new DrmCertificate);
drm_certificate->set_type(DrmCertificate::DEVICE);
drm_certificate->set_serial_number(serial_number);
drm_certificate->set_system_id(system_id);
drm_certificate->set_public_key(test_keys_.public_test_key_3_2048_bits());
drm_certificate->set_creation_time_seconds(4321);
return drm_certificate.release();
}
SignedDrmCertificate* ClientCertTest::GenerateSignedDrmCertificate(
SignedDrmCertificate* signer, uint32_t system_id,
const std::string& serial_number) {
std::unique_ptr<DrmCertificate> drm_certificate(
GenerateDrmCertificate(system_id, serial_number));
std::unique_ptr<SignedDrmCertificate> signed_drm_certificate(SignCertificate(
*drm_certificate, signer, test_keys_.private_test_key_2_2048_bits()));
return signed_drm_certificate.release();
}
DrmCertificate* ClientCertTest::GenerateProvisionerCertificate(
uint32_t system_id, const std::string& serial_number, const std::string& provider_id) {
std::unique_ptr<DrmCertificate> provisioner_certificate(new DrmCertificate);
provisioner_certificate->set_type(DrmCertificate::PROVISIONER);
provisioner_certificate->set_serial_number(serial_number);
// TODO(user): Need to generate 3072 bit test for provisioner certificates.
provisioner_certificate->set_public_key(
test_keys_.public_test_key_2_2048_bits());
provisioner_certificate->set_system_id(system_id);
provisioner_certificate->set_provider_id(provider_id);
provisioner_certificate->set_creation_time_seconds(1234);
return provisioner_certificate.release();
}
SignedDrmCertificate* ClientCertTest::GenerateSignedProvisionerCertificate(
uint32_t system_id, const std::string& serial_number, const std::string& service_id) {
std::unique_ptr<DrmCertificate> provisioner_certificate(
GenerateProvisionerCertificate(system_id, serial_number, service_id));
return SignCertificate(*provisioner_certificate, nullptr,
test_keys_.private_test_key_1_3072_bits());
}
TEST_F(ClientCertTest, BasicValidation) {
const TestTokenAndKeys kValidTokenAndExpectedKeys[] = {
TestTokenAndKeys(
absl::HexStringToBytes(
"00000002000001128e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
"2517a12f4922953e"),
274, absl::HexStringToBytes("8e1ebfe037828096ca6538b4f6f4bcb5"),
absl::HexStringToBytes("4071197f1f8910d9bf10c6bc4c987638")),
TestTokenAndKeys(
absl::HexStringToBytes(
"0000000200000112d906feebe1750c5886ff77c2dfa31bb40e002f3adbc0fa5b"
"eb2486cf5f419549cdaa23230e5165ac2ffab56d53b692b7ba0c1857400c6add"
"3af3ff3d5cb24985"),
274, absl::HexStringToBytes("d906feebe1750c5886ff77c2dfa31bb4"),
absl::HexStringToBytes("42cfb1765201042302a404d1e0fac8ed"))};
for (size_t i = 0; i < ABSL_ARRAYSIZE(kValidTokenAndExpectedKeys); ++i) {
SCOPED_TRACE("Test data: " + absl::StrCat(i));
TestBasicValidation(kValidTokenAndExpectedKeys[i], true, true);
}
EXPECT_EQ(
wvm_test_keys::kTestSystemId,
KeyboxClientCert::GetSystemId(kValidTokenAndExpectedKeys[0].token_));
}
TEST_F(ClientCertTest, BasicCertValidation) {
const uint32_t system_id = 1234;
const std::string serial_number("serial_number");
std::unique_ptr<SignedDrmCertificate> signed_cert(
GenerateSignedDrmCertificate(GenerateSignedIntermediateCertificate(
nullptr, system_id, serial_number),
system_id, serial_number + "-device"));
const TestCertificateAndData kValidCertificateAndExpectedData(
signed_cert->SerializeAsString(), serial_number, system_id,
util::OkStatus());
const bool compare_data = true;
TestBasicValidationDrmCertificate(kValidCertificateAndExpectedData,
compare_data);
}
TEST_F(ClientCertTest, InvalidKeybox) {
const TestTokenAndKeys kInvalidTokenAndExpectedKeys[] = {
// This tests a malformed, but appropriately sized keybox.
TestTokenAndKeys(
absl::HexStringToBytes(
"00000002000001129e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
"2517a12f4922953e"),
0, absl::HexStringToBytes(""), absl::HexStringToBytes("")),
// This has a length and system_id, but nothing else.
TestTokenAndKeys(absl::HexStringToBytes("0000000200000112"), 0,
absl::HexStringToBytes(""), absl::HexStringToBytes("")),
// This has only a byte.
TestTokenAndKeys(absl::HexStringToBytes(""), 0,
absl::HexStringToBytes(""), absl::HexStringToBytes("")),
// This has an emptry std::string for the keybox.
TestTokenAndKeys(absl::HexStringToBytes(""), 0,
absl::HexStringToBytes(""), absl::HexStringToBytes(""))};
for (size_t i = 0; i < ABSL_ARRAYSIZE(kInvalidTokenAndExpectedKeys); ++i) {
SCOPED_TRACE("Test data: " + absl::StrCat(i));
TestBasicValidation(kInvalidTokenAndExpectedKeys[i], false, false);
}
}
TEST_F(ClientCertTest, InvalidCertificate) {
const uint32_t system_id(1234);
const std::string device_sn("device-serial-number");
const std::string signer_sn("signer-serial-number");
std::unique_ptr<DrmCertificate> dev_cert;
std::unique_ptr<DrmCertificate> signer_cert;
std::unique_ptr<SignedDrmCertificate> signed_signer;
// Invalid serialized device certificate.
std::unique_ptr<SignedDrmCertificate> invalid_drm_cert(
new SignedDrmCertificate);
invalid_drm_cert->set_drm_certificate("bad-serialized-cert");
GenerateSignature(invalid_drm_cert->drm_certificate(),
test_keys_.private_test_key_2_2048_bits(),
invalid_drm_cert->mutable_signature());
invalid_drm_cert->set_allocated_signer(
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
// Missing system ID.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
dev_cert->clear_system_id();
std::unique_ptr<SignedDrmCertificate> no_system_id(SignCertificate(
*dev_cert.get(),
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn),
test_keys_.private_test_key_2_2048_bits()));
// Invalid device public key.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
dev_cert->set_public_key("bad-device-public-key");
std::unique_ptr<SignedDrmCertificate> bad_device_public_key(SignCertificate(
*dev_cert,
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn),
test_keys_.private_test_key_2_2048_bits()));
// Invalid serialized intermediate certificate.
signed_signer.reset(
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
signed_signer->set_drm_certificate("bad-serialized-cert");
GenerateSignature(signed_signer->drm_certificate(),
test_keys_.private_test_key_1_3072_bits(),
signed_signer->mutable_signature());
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
std::unique_ptr<SignedDrmCertificate> invalid_signer(
SignCertificate(*dev_cert, signed_signer.release(),
test_keys_.private_test_key_2_2048_bits()));
// Invalid signer public key.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
signer_cert->set_public_key("bad-signer-public-key");
std::unique_ptr<SignedDrmCertificate> bad_signer_public_key(SignCertificate(
*dev_cert,
SignCertificate(*signer_cert, nullptr,
test_keys_.private_test_key_1_3072_bits()),
test_keys_.private_test_key_2_2048_bits()));
// Invalid device certificate signature.
std::unique_ptr<SignedDrmCertificate> bad_device_signature(
GenerateSignedDrmCertificate(
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn),
system_id, device_sn));
bad_device_signature->set_signature("bad-signature");
// Missing signer serial number.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
signer_cert->clear_serial_number();
std::unique_ptr<SignedDrmCertificate> missing_signer_sn(SignCertificate(
*dev_cert,
SignCertificate(*signer_cert, nullptr,
test_keys_.private_test_key_1_3072_bits()),
test_keys_.private_test_key_2_2048_bits()));
// Invalid serialized intermediate certificate.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
signed_signer.reset(
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
signed_signer->set_signature("bad-signature");
std::unique_ptr<SignedDrmCertificate> bad_signer_signature(
SignCertificate(*dev_cert, signed_signer.release(),
test_keys_.private_test_key_2_2048_bits()));
const TestCertificateAndData kInvalidCertificate[] = {
TestCertificateAndData("f", "", 0,
util::Status(error_space, INVALID_DRM_CERTIFICATE,
"device-certificate-invalid-token")),
TestCertificateAndData(invalid_drm_cert->SerializeAsString(), "", 0,
util::Status(error_space, INVALID_DRM_CERTIFICATE,
"device-certificate-invalid")),
TestCertificateAndData(
no_system_id->SerializeAsString(), "", 0,
util::Status(error_space, INVALID_DRM_CERTIFICATE,
"device-certificate-missing-system-id")),
TestCertificateAndData(
bad_device_public_key->SerializeAsString(), "", 0,
util::Status(error_space, INVALID_DRM_CERTIFICATE,
"device-certificate-public-key-failed")),
TestCertificateAndData(invalid_signer->SerializeAsString(), "", 0,
util::Status(error_space, INVALID_DRM_CERTIFICATE,
"device-certificate-invalid-signer")),
TestCertificateAndData(
bad_signer_public_key->SerializeAsString(), "", 0,
util::Status(error_space, INVALID_DRM_CERTIFICATE,
"signer-certificate-public-key-failed")),
TestCertificateAndData(
bad_device_signature->SerializeAsString(), "", 0,
util::Status(error_space, INVALID_DRM_CERTIFICATE,
"device-certificate-verification-failed")),
TestCertificateAndData(missing_signer_sn->SerializeAsString(), "", 0,
util::Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-signer-serial-number")),
TestCertificateAndData(
bad_signer_signature->SerializeAsString(), "", 0,
util::Status(error_space, INVALID_DRM_CERTIFICATE,
"signer-certificate-verification-failed")),
};
for (size_t i = 0; i < ABSL_ARRAYSIZE(kInvalidCertificate); ++i) {
TestBasicValidationDrmCertificate(kInvalidCertificate[i], false);
}
}
class MockCertificateClientCert : public CertificateClientCert {
public:
using CertificateClientCert::ResetSignerCache;
using CertificateClientCert::SignerCacheSize;
explicit MockCertificateClientCert(const std::string& cert_bytes)
: CertificateClientCert(cert_bytes) {}
MOCK_METHOD3(ValidateSigner, util::Status(const std::string& serial_number,
const std::string& certificate,
const std::string& signature));
util::Status CallValidateCertificate(
const SignedDrmCertificate& signed_drm_certificate) {
return ValidateCertificate(signed_drm_certificate);
}
};
TEST_F(ClientCertTest, SignerCache) {
const uint32_t system_id = 1234;
const std::string serial_number("serial-number");
std::unique_ptr<SignedDrmCertificate> signed_cert(
GenerateSignedDrmCertificate(GenerateSignedIntermediateCertificate(
nullptr, system_id, serial_number),
system_id, serial_number + "-device"));
// TODO(user): Remove work from the ClientCert constructors to make it
// more testable, and because it's just bad practice.
MockCertificateClientCert::ResetSignerCache();
MockCertificateClientCert client_cert(signed_cert->SerializeAsString());
EXPECT_EQ(1, MockCertificateClientCert::SignerCacheSize());
EXPECT_CALL(client_cert, ValidateSigner(_, _, _))
.Times(0)
.WillRepeatedly(Return(util::OkStatus()));
EXPECT_EQ(util::OkStatus(),
client_cert.CallValidateCertificate(*signed_cert));
}
TEST_F(ClientCertTest, MissingPreProvKey) {
// system ID in token is 0x01234567
const std::string token(absl::HexStringToBytes(
"00000002012345678e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
"2517a12f4922953e"));
ClientCert* client_cert_ptr = nullptr;
util::Status status = ClientCert::CreateWithToken(token, &client_cert_ptr);
ASSERT_EQ(MISSING_PRE_PROV_KEY, status.error_code());
}
TEST_F(ClientCertTest, ValidProvisionerDeviceCert) {
const uint32_t system_id = 4890;
const std::string service_id("widevine_test.com");
const std::string device_serial_number("device-serial-number");
const std::string intermediate_serial_number("intermediate-serial-number");
const std::string provisioner_serial_number("provisioner-serial-number");
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert(
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
service_id));
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
GenerateSignedIntermediateCertificate(signed_provisioner_cert.release(),
system_id,
intermediate_serial_number));
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
system_id, device_serial_number));
std::string serialized_cert;
signed_device_cert->SerializeToString(&serialized_cert);
ClientCert* client_cert_ptr = nullptr;
EXPECT_OK(ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE,
serialized_cert, &client_cert_ptr));
ASSERT_TRUE(client_cert_ptr != nullptr);
std::unique_ptr<ClientCert> drm_cert(client_cert_ptr);
EXPECT_EQ(service_id, drm_cert->service_id());
EXPECT_EQ(device_serial_number, drm_cert->serial_number());
EXPECT_EQ(intermediate_serial_number, drm_cert->signer_serial_number());
EXPECT_EQ(system_id, drm_cert->system_id());
}
TEST_F(ClientCertTest, InValidProvisionerDeviceCertEmptyServiceId) {
const uint32_t system_id = 4890;
const std::string service_id("");
const std::string device_serial_number("device-serial-number");
const std::string intermediate_serial_number("intermediate-serial-number");
const std::string provisioner_serial_number("provisioner-serial-number");
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert(
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
service_id));
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
GenerateSignedIntermediateCertificate(signed_provisioner_cert.release(),
system_id,
intermediate_serial_number));
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
system_id, device_serial_number));
std::string serialized_cert;
signed_device_cert->SerializeToString(&serialized_cert);
ClientCert* client_cert_ptr = nullptr;
EXPECT_EQ("missing-provisioning-service-id",
ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE,
serialized_cert, &client_cert_ptr)
.error_message());
EXPECT_FALSE(client_cert_ptr);
}
TEST_F(ClientCertTest, InValidProvisionerDeviceCertChain) {
const uint32_t system_id = 4890;
const uint32_t system_id2 = 4892;
const std::string service_id("widevine_test.com");
const std::string device_serial_number("device-serial-number");
const std::string intermediate_serial_number("intermediate-serial-number");
const std::string intermediate_serial_number2("intermediate-serial-number-2");
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert2(
GenerateSignedIntermediateCertificate(nullptr, system_id2,
intermediate_serial_number2));
// Instead of using a provisioner certificate to sign this intermediate
// certificate, use another intermediate certificate. This is an invalid
// chain and should generate an error when trying to create a client
// certificate.
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
GenerateSignedIntermediateCertificate(signed_intermediate_cert2.release(),
system_id,
intermediate_serial_number));
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
system_id, device_serial_number));
std::string serialized_cert;
signed_device_cert->SerializeToString(&serialized_cert);
ClientCert* client_cert_ptr = nullptr;
ASSERT_EQ("expected-provisioning-provider-certificate-type",
ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE,
serialized_cert, &client_cert_ptr)
.error_message());
EXPECT_FALSE(client_cert_ptr);
// Make a normal intermediate certificate.
signed_intermediate_cert.reset(GenerateSignedIntermediateCertificate(
nullptr, system_id, intermediate_serial_number));
signed_device_cert.reset(GenerateSignedDrmCertificate(
signed_intermediate_cert.release(), system_id, device_serial_number));
signed_device_cert->SerializeToString(&serialized_cert);
EXPECT_OK(ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE,
serialized_cert, &client_cert_ptr));
// The service Id should only get set if a provisioning cert exist.
std::unique_ptr<ClientCert> drm_cert(client_cert_ptr);
EXPECT_TRUE(drm_cert->service_id().empty());
EXPECT_EQ(device_serial_number, drm_cert->serial_number());
EXPECT_EQ(intermediate_serial_number, drm_cert->signer_serial_number());
EXPECT_EQ(system_id, drm_cert->system_id());
}
} // namespace widevine

View File

@@ -1,241 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// 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.
////////////////////////////////////////////////////////////////////////////////
// Implements the DeviceStatusList class.
#include "license_server_sdk/internal/device_status_list.h"
#include <time.h>
#include <memory>
#include "glog/logging.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "util/gtl/map_util.h"
#include "common/error_space.h"
#include "common/rsa_key.h"
#include "license_server_sdk/internal/client_cert.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/errors.pb.h"
namespace widevine {
DeviceStatusList* DeviceStatusList::Instance() {
// TODO(user): This is "ok" according to Google's Coding for Dummies, but
// we should inject the status list into the sessions. This will require
// exposing additional objects in the public interface.
static DeviceStatusList* device_status_list(nullptr);
if (!device_status_list) device_status_list = new DeviceStatusList;
return device_status_list;
}
DeviceStatusList::DeviceStatusList()
: creation_time_seconds_(0),
expiration_period_seconds_(0),
allow_unknown_devices_(true),
allow_test_only_devices_(false) {}
DeviceStatusList::~DeviceStatusList() {}
util::Status DeviceStatusList::UpdateStatusList(
const std::string& root_certificate_public_key,
const std::string& serialized_certificate_status_list,
uint32_t expiration_period_seconds) {
SignedDeviceCertificateStatusList signed_certificate_status_list;
if (!signed_certificate_status_list.ParseFromString(
serialized_certificate_status_list)) {
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"signed-certificate-status-list-parse-error");
}
if (!signed_certificate_status_list.has_certificate_status_list()) {
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"missing-status-list");
}
if (!signed_certificate_status_list.has_signature()) {
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"missing-status-list-signature");
}
std::unique_ptr<RsaPublicKey> root_key(
RsaPublicKey::Create(root_certificate_public_key));
if (root_key == nullptr) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-root-public-key");
}
if (!root_key->VerifySignature(
signed_certificate_status_list.certificate_status_list(),
signed_certificate_status_list.signature())) {
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"invalid-status-list-signature");
}
DeviceCertificateStatusList certificate_status_list;
if (!certificate_status_list.ParseFromString(
signed_certificate_status_list.certificate_status_list())) {
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"certificate-status-list-parse-error");
}
if (expiration_period_seconds &&
(GetCurrentTime() > (certificate_status_list.creation_time_seconds() +
expiration_period_seconds))) {
return util::Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
"certificate-status-list-expired");
}
absl::WriterMutexLock lock(&status_map_lock_);
device_status_map_.clear();
for (int i = 0, n = certificate_status_list.certificate_status_size(); i < n;
i++) {
const DeviceCertificateStatus& cert_status =
certificate_status_list.certificate_status(i);
if (cert_status.has_device_info()) {
const ProvisionedDeviceInfo& device_info = cert_status.device_info();
if (device_info.has_system_id()) {
device_status_map_[device_info.system_id()] = cert_status;
} else {
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"device-info-missing-system-id");
}
}
}
creation_time_seconds_ = certificate_status_list.creation_time_seconds();
expiration_period_seconds_ = expiration_period_seconds;
return util::OkStatus();
}
util::Status DeviceStatusList::GetCertStatus(
const ClientCert& client_cert, ProvisionedDeviceInfo* device_info) {
CHECK(device_info);
// Keybox checks.
if (client_cert.type() == ClientIdentification::KEYBOX) {
if (!KeyboxClientCert::IsSystemIdKnown(client_cert.system_id())) {
return util::Status(error_space, UNSUPPORTED_SYSTEM_ID,
"keybox-unsupported-system-id");
}
// Get device information from certificate status list if available.
if (!GetDeviceInfo(client_cert, device_info)) {
device_info->Clear();
}
return util::OkStatus();
}
// DRM certificate checks.
if (client_cert.type() != ClientIdentification::DRM_DEVICE_CERTIFICATE) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
"device-certificate-unsupported-token-type");
}
absl::ReaderMutexLock lock(&status_map_lock_);
if (expiration_period_seconds_ &&
(GetCurrentTime() >
(creation_time_seconds_ + expiration_period_seconds_))) {
return util::Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
"certificate-status-list-expired");
}
DeviceCertificateStatus* device_cert_status =
gtl::FindOrNull(device_status_map_, client_cert.system_id());
if (device_cert_status) {
*device_info = device_cert_status->device_info();
if (device_cert_status->status() ==
DeviceCertificateStatus::STATUS_REVOKED) {
if (IsRevokedSystemIdAllowed(client_cert.system_id())) {
LOG(WARNING) << "Allowing REVOKED device: "
<< device_info->ShortDebugString();
} else {
return util::Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED,
"device-certificate-revoked");
}
}
if ((device_cert_status->status() ==
DeviceCertificateStatus::STATUS_TEST_ONLY) &&
!allow_test_only_devices_) {
return util::Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
"test-only-drm-certificate-not-allowed");
}
if (!client_cert.signed_by_provisioner() &&
(client_cert.signer_serial_number() !=
device_cert_status->drm_serial_number())) {
// Widevine-provisioned device, and the intermediate certificate serial
// number does not match that in the status list. If the status list is
// newer than the certificate, indicate an invalid certificate, so that
// the device re-provisions. If, on the other hand, the certificate status
// list is older than the certificate, the certificate is for all purposes
// unknown.
if (client_cert.signer_creation_time_seconds() < creation_time_seconds_) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
"intermediate-certificate-serial-number-mismatch");
}
return util::Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
"device-certificate-status-unknown");
}
} else {
if (!allow_unknown_devices_) {
return util::Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
"device-certificate-status-unknown");
}
device_info->Clear();
}
return util::OkStatus();
}
bool DeviceStatusList::GetDeviceInfo(const ClientCert& client_cert,
ProvisionedDeviceInfo* device_info) {
CHECK(device_info);
absl::ReaderMutexLock lock(&status_map_lock_);
DeviceCertificateStatus* device_cert_status =
gtl::FindOrNull(device_status_map_, client_cert.system_id());
if (device_cert_status) {
*device_info = device_cert_status->device_info();
return true;
}
return false;
}
bool DeviceStatusList::IsSystemIdActive(uint32_t system_id) {
absl::ReaderMutexLock lock(&status_map_lock_);
DeviceCertificateStatus* device_cert_status =
gtl::FindOrNull(device_status_map_, system_id);
if (!device_cert_status) {
return allow_unknown_devices_ ||
KeyboxClientCert::IsSystemIdKnown(system_id);
}
if (device_cert_status->status() ==
DeviceCertificateStatus::STATUS_TEST_ONLY) {
return allow_test_only_devices_;
}
if (device_cert_status) {
ProvisionedDeviceInfo device_info = device_cert_status->device_info();
if (device_cert_status->status() ==
DeviceCertificateStatus::STATUS_REVOKED) {
if (IsRevokedSystemIdAllowed(system_id)) {
LOG(WARNING) << "REVOKED system_id: " << system_id
<< " is allowed to be active";
return true;
}
}
}
return device_cert_status->status() !=
DeviceCertificateStatus::STATUS_REVOKED;
}
uint32_t DeviceStatusList::GetCurrentTime() const { return time(nullptr); }
void DeviceStatusList::AllowRevokedDevices(const std::string& system_id_list) {
for (absl::string_view sp : absl::StrSplit(system_id_list, ',')) {
allowed_revoked_devices_.push_back(std::stoi(std::string(sp)));
}
std::sort(allowed_revoked_devices_.begin(), allowed_revoked_devices_.end());
}
bool DeviceStatusList::IsRevokedSystemIdAllowed(uint32_t system_id) {
auto it = std::binary_search(allowed_revoked_devices_.begin(),
allowed_revoked_devices_.end(), system_id);
return it;
}
} // namespace widevine

View File

@@ -1,99 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// 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.
////////////////////////////////////////////////////////////////////////////////
// DeviceStatusList class header.
#ifndef LICENSE_SERVER_SDK_INTERNAL_DEVICE_STATUS_LIST_H__
#define LICENSE_SERVER_SDK_INTERNAL_DEVICE_STATUS_LIST_H__
#include <map>
#include <string>
#include "base/macros.h"
#include "absl/synchronization/mutex.h"
#include "util/status.h"
#include "protos/public/device_certificate_status.pb.h"
#include "protos/public/provisioned_device_info.pb.h"
namespace widevine {
class ClientCert;
// Manages the certificate status of devices. The list of
// DeviceCertificateStatus is provided by the DRM server. Each license
// request is checked to ensure the certificate in the request is valid and
// not revoked. Also checks to see if the intermediate certificates were
// updated where the system Id is the same, but the serial number changes.
// This case should cause the clients to re-provision.
class DeviceStatusList {
public:
// Returns a pointer to a singleton DeviceStatusList.
static DeviceStatusList* Instance();
DeviceStatusList();
virtual ~DeviceStatusList();
// Takes |signed_certificate_status_list| and copies to an internal map of
// device certifcate status list. The internal map is used to verify
// a device was not revoked. Returns true is the list was successfully parsed.
util::Status UpdateStatusList(const std::string& root_certificate_public_key,
const std::string& signed_certificate_status_list,
uint32_t expiration_period_seconds);
void set_allow_unknown_devices(bool flag) { allow_unknown_devices_ = flag; }
bool allow_unknown_devices() const { return allow_unknown_devices_; }
void set_allow_test_only_devices(bool allow) {
allow_test_only_devices_ = allow;
}
bool allow_test_only_devices() const { return allow_test_only_devices_; }
// Checks the device status list and returns either:
// OK
// UNSUPPORTED_SYSTEM_ID
// INVALID_DRM_CERTIFICATE
// DRM_DEVICE_CERTIFICATE_REVOKED
// DRM_DEVICE_CERTIFICATE_UNKNOWN
// If status is OK, a copy of the provisioned device info is copied
// into |device_info|. Caller owns |device_info| and it must not be null.
util::Status GetCertStatus(
const ClientCert& client_cert,
widevine::ProvisionedDeviceInfo* device_info);
// Returns true if the pre-provisioning key or certificate for the specified
// system ID are active (not disallowed or revoked).
bool IsSystemIdActive(uint32_t system_id);
// Returns true if the system ID
// Returns true is a ProvisionedDeviceInfo exist based on <client_cert>.
// Caller owns <device_info> and it must not be null.
bool GetDeviceInfo(const ClientCert& client_cert,
widevine::ProvisionedDeviceInfo* device_info);
// Returns the current POSIX time.
virtual uint32_t GetCurrentTime() const;
// Enable delivery of licenses to revoked client devices. |system_id_list| is
// a comma separated list of systems Ids to allow even if revoked.
virtual void AllowRevokedDevices(const std::string& system_id_list);
private:
// Returns true if the system ID is allowed to be revoked.
// Caller owns |system_id|. They must not be null.
bool IsRevokedSystemIdAllowed(uint32_t system_id);
absl::Mutex status_map_lock_;
// Key is the system id for the device.
std::map<uint32_t, widevine::DeviceCertificateStatus> device_status_map_;
uint32_t creation_time_seconds_;
uint32_t expiration_period_seconds_;
bool allow_unknown_devices_;
bool allow_test_only_devices_;
// Contains the list of system_id values that are allowed to succeed even if
// revoked.
std::vector<uint32_t> allowed_revoked_devices_;
DISALLOW_COPY_AND_ASSIGN(DeviceStatusList);
};
} // namespace widevine
#endif // LICENSE_SERVER_SDK_INTERNAL_DEVICE_STATUS_LIST_H__

View File

@@ -1,377 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// 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 "license_server_sdk/internal/device_status_list.h"
#include <stddef.h>
#include <memory>
#include <type_traits>
#include <utility>
#include "glog/logging.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/strings/str_cat.h"
#include "common/rsa_key.h"
#include "common/rsa_test_keys.h"
#include "license_server_sdk/internal/client_cert.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/errors.pb.h"
#include "protos/public/provisioned_device_info.pb.h"
#include "protos/public/signed_drm_certificate.pb.h"
namespace widevine {
using ::testing::_;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::ReturnRefOfCopy;
const uint32_t kValidCertSystemId = 100;
const uint32_t kRevokedCertSystemId = 101;
const uint32_t kValidPpkSystemId = 102;
const uint32_t kTestOnlyCertSystemId = 103;
const uint32_t kRevokedAllowedDeviceCertSystemId = 104;
const uint32_t kUnknownSystemId = 666;
const char kValidSerialNumber[] = "valid-serial-number";
const char kRevokedSerialNumber[] = "revoked-serial-number";
const char kRevokedAllowDeviceSerialNumber[] =
"revoked-allow-device-serial-number";
const char kTestOnlySerialNumber[] = "test_only-serial-number";
const char kMismatchSerialNumber[] = "mismatch-serial-number";
const char kDeviceModel[] = "device-model-x";
const char kTestPreprovKey[] = "00112233445566778899aabbccddeeff";
const uint32_t kStatusListCreationTime = 17798001;
const uint32_t kDefaultExpirePeriod = 0;
class MockCertificateClientCert : public CertificateClientCert {
public:
explicit MockCertificateClientCert() : CertificateClientCert("token-bytes") {}
MOCK_CONST_METHOD0(system_id, uint32_t());
MOCK_CONST_METHOD0(signer_serial_number, std::string &());
MOCK_CONST_METHOD0(signer_creation_time_seconds, uint32_t());
MOCK_CONST_METHOD0(type, ClientIdentification::TokenType());
MOCK_CONST_METHOD0(signed_by_provisioner, bool());
};
class MockKeyboxClientCert : public KeyboxClientCert {
public:
explicit MockKeyboxClientCert() : KeyboxClientCert("token-bytes") {}
MOCK_CONST_METHOD0(system_id, uint32_t());
MOCK_CONST_METHOD0(type, ClientIdentification::TokenType());
};
class DeviceStatusListTest : public ::testing::Test {
public:
~DeviceStatusListTest() override {}
void SetUp() override {
DeviceCertificateStatus *cert_status;
// Device cert with status RELEASED.
cert_status = cert_status_list_.add_certificate_status();
cert_status->mutable_device_info()->set_system_id(kValidCertSystemId);
cert_status->set_drm_serial_number(kValidSerialNumber);
cert_status->mutable_device_info()->set_model(kDeviceModel);
cert_status->set_status(DeviceCertificateStatus::STATUS_RELEASED);
// Device cert with status REVOKED.
cert_status = cert_status_list_.add_certificate_status();
cert_status->mutable_device_info()->set_system_id(kRevokedCertSystemId);
cert_status->set_drm_serial_number(kRevokedSerialNumber);
cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED);
// Device cert with status REVOKED ALLOWED DEVICE.
cert_status = cert_status_list_.add_certificate_status();
cert_status->mutable_device_info()->set_system_id(
kRevokedAllowedDeviceCertSystemId);
cert_status->set_drm_serial_number(kRevokedAllowDeviceSerialNumber);
cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED);
device_status_list_.AllowRevokedDevices(
absl::StrCat(kRevokedAllowedDeviceCertSystemId));
// Device cert with status TEST_ONLY.
cert_status = cert_status_list_.add_certificate_status();
cert_status->mutable_device_info()->set_system_id(kTestOnlyCertSystemId);
cert_status->set_drm_serial_number(kTestOnlySerialNumber);
cert_status->set_status(DeviceCertificateStatus::STATUS_TEST_ONLY);
cert_status_list_.set_creation_time_seconds(kStatusListCreationTime);
cert_status_list_.SerializeToString(
signed_cert_status_list_.mutable_certificate_status_list());
std::unique_ptr<RsaPrivateKey> root_key(
RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits()));
ASSERT_TRUE(root_key);
ASSERT_TRUE(root_key->GenerateSignature(
signed_cert_status_list_.certificate_status_list(),
signed_cert_status_list_.mutable_signature()));
ASSERT_TRUE(
signed_cert_status_list_.SerializeToString(&serialized_status_list_));
ASSERT_EQ(util::OkStatus(),
device_status_list_.UpdateStatusList(
test_keys_.public_test_key_1_3072_bits(),
serialized_status_list_, kDefaultExpirePeriod));
}
DeviceStatusList device_status_list_;
RsaTestKeys test_keys_;
DeviceCertificateStatusList cert_status_list_;
SignedDeviceCertificateStatusList signed_cert_status_list_;
std::string serialized_status_list_;
};
// Returns the number of DevcieCertificateStatus messages in the list.
TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
// Test case where the Certificate status is set to Valid.
ProvisionedDeviceInfo device_info;
MockCertificateClientCert valid_client_cert;
std::string valid_drm_serial_number(kValidSerialNumber);
EXPECT_CALL(valid_client_cert, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(valid_client_cert, system_id())
.WillRepeatedly(Return(kValidCertSystemId));
EXPECT_CALL(valid_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
EXPECT_EQ(util::OkStatus(),
device_status_list_.GetCertStatus(valid_client_cert, &device_info));
EXPECT_TRUE(device_info.has_model());
EXPECT_EQ(kDeviceModel, device_info.model());
// Test case where the Certificate status is Revoked.
MockCertificateClientCert revoked_client_cert;
std::string revoked_drm_serial_number(kRevokedSerialNumber);
EXPECT_CALL(revoked_client_cert, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(revoked_client_cert, system_id())
.WillRepeatedly(Return(kRevokedCertSystemId));
EXPECT_CALL(revoked_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(revoked_drm_serial_number));
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_REVOKED,
device_status_list_.GetCertStatus(revoked_client_cert, &device_info)
.error_code());
// Test case where the revoked cert is allowed.
device_status_list_.AllowRevokedDevices(absl::StrCat(kRevokedCertSystemId));
EXPECT_OK(
device_status_list_.GetCertStatus(revoked_client_cert, &device_info));
}
TEST_F(DeviceStatusListTest, TestOnlyCertAllowed) {
ProvisionedDeviceInfo device_info;
MockCertificateClientCert test_only_client_cert;
std::string test_only_drm_serial_number(kTestOnlySerialNumber);
EXPECT_CALL(test_only_client_cert, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(test_only_client_cert, system_id())
.WillRepeatedly(Return(kTestOnlyCertSystemId));
EXPECT_CALL(test_only_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
EXPECT_EQ(
DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
device_status_list_.GetCertStatus(test_only_client_cert, &device_info)
.error_code());
}
TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
ProvisionedDeviceInfo device_info;
MockCertificateClientCert test_only_client_cert;
std::string test_only_drm_serial_number(kTestOnlySerialNumber);
device_status_list_.set_allow_test_only_devices(true);
EXPECT_CALL(test_only_client_cert, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(test_only_client_cert, system_id())
.WillRepeatedly(Return(kTestOnlyCertSystemId));
EXPECT_CALL(test_only_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
EXPECT_EQ(util::OkStatus(), device_status_list_.GetCertStatus(
test_only_client_cert, &device_info));
}
TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) {
std::multimap<uint32_t, std::string> preprov_keys;
preprov_keys.insert(std::make_pair(kValidCertSystemId, kTestPreprovKey));
KeyboxClientCert::SetPreProvisioningKeys(preprov_keys);
// Test case where the Certificate status is set to Valid.
ProvisionedDeviceInfo device_info;
MockKeyboxClientCert valid_client_keybox;
std::string valid_drm_serial_number(kValidSerialNumber);
EXPECT_CALL(valid_client_keybox, type())
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
EXPECT_CALL(valid_client_keybox, system_id())
.WillRepeatedly(Return(kValidCertSystemId));
EXPECT_EQ(util::OkStatus(), device_status_list_.GetCertStatus(
valid_client_keybox, &device_info));
EXPECT_TRUE(device_info.has_model());
EXPECT_EQ(kDeviceModel, device_info.model());
MockKeyboxClientCert unknown_client_keybox;
EXPECT_CALL(unknown_client_keybox, type())
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
EXPECT_CALL(unknown_client_keybox, system_id())
.WillRepeatedly(Return(kUnknownSystemId));
EXPECT_EQ(
UNSUPPORTED_SYSTEM_ID,
device_status_list_.GetCertStatus(unknown_client_keybox, &device_info)
.error_code());
EXPECT_TRUE(device_info.has_model());
EXPECT_EQ(kDeviceModel, device_info.model());
}
TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
device_status_list_.set_allow_unknown_devices(true);
// Test case where the signer certificate is older than the current status
// list.
MockCertificateClientCert older_client_cert;
ProvisionedDeviceInfo device_info;
std::string mismatch_drm_serial_number(kMismatchSerialNumber);
EXPECT_CALL(older_client_cert, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(older_client_cert, system_id())
.WillRepeatedly(Return(kValidCertSystemId));
EXPECT_CALL(older_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
EXPECT_CALL(older_client_cert, signer_creation_time_seconds())
.WillRepeatedly(Return(kStatusListCreationTime - 1));
EXPECT_EQ(INVALID_DRM_CERTIFICATE,
device_status_list_.GetCertStatus(older_client_cert, &device_info)
.error_code());
// We allow this case only for certs signed by a provisioner cert.
EXPECT_CALL(older_client_cert, signed_by_provisioner())
.WillOnce(Return(true));
EXPECT_EQ(util::OkStatus(),
device_status_list_.GetCertStatus(older_client_cert, &device_info));
EXPECT_TRUE(device_info.has_system_id());
EXPECT_EQ(kValidCertSystemId, device_info.system_id());
// Test case where the signer certificate is newer than the current status
// list, and unknown devices are allowed.
MockCertificateClientCert newer_client_cert1;
EXPECT_CALL(newer_client_cert1, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(newer_client_cert1, system_id())
.WillRepeatedly(Return(kValidCertSystemId));
EXPECT_CALL(newer_client_cert1, signer_serial_number())
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
EXPECT_CALL(newer_client_cert1, signer_creation_time_seconds())
.WillRepeatedly(Return(kStatusListCreationTime));
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_UNKNOWN,
device_status_list_.GetCertStatus(newer_client_cert1, &device_info)
.error_code());
// Test case where the signer certificate is newer than the current status
// list, and unknown devices are not allowed.
device_status_list_.set_allow_unknown_devices(false);
MockCertificateClientCert newer_client_cert2;
EXPECT_CALL(newer_client_cert2, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(newer_client_cert2, system_id())
.WillRepeatedly(Return(kValidCertSystemId));
EXPECT_CALL(newer_client_cert2, signer_serial_number())
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
EXPECT_CALL(newer_client_cert2, signer_creation_time_seconds())
.WillRepeatedly(Return(kStatusListCreationTime + 1));
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_UNKNOWN,
device_status_list_.GetCertStatus(newer_client_cert2, &device_info)
.error_code());
}
TEST_F(DeviceStatusListTest, InvalidStatusList) {
EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST,
device_status_list_
.UpdateStatusList(test_keys_.public_test_key_2_2048_bits(),
serialized_status_list_, 0)
.error_code());
++(*signed_cert_status_list_.mutable_certificate_status_list())[4];
ASSERT_TRUE(
signed_cert_status_list_.SerializeToString(&serialized_status_list_));
EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST,
device_status_list_
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
serialized_status_list_, 0)
.error_code());
}
class MockDeviceStatusList : public DeviceStatusList {
public:
MOCK_CONST_METHOD0(GetCurrentTime, uint32_t());
};
TEST_F(DeviceStatusListTest, ExpiredStatusListOnSet) {
MockDeviceStatusList mock_device_status_list;
EXPECT_CALL(mock_device_status_list, GetCurrentTime())
.Times(2)
.WillOnce(Return(kStatusListCreationTime + 100))
.WillOnce(Return(kStatusListCreationTime + 101));
EXPECT_EQ(util::OkStatus(), mock_device_status_list.UpdateStatusList(
test_keys_.public_test_key_1_3072_bits(),
serialized_status_list_, 100));
EXPECT_EQ(EXPIRED_CERTIFICATE_STATUS_LIST,
mock_device_status_list
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
serialized_status_list_, 100)
.error_code());
}
TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) {
MockDeviceStatusList mock_device_status_list;
EXPECT_CALL(mock_device_status_list, GetCurrentTime())
.Times(3)
.WillOnce(Return(kStatusListCreationTime + 100))
.WillOnce(Return(kStatusListCreationTime + 100))
.WillOnce(Return(kStatusListCreationTime + 101));
EXPECT_EQ(util::OkStatus(), mock_device_status_list.UpdateStatusList(
test_keys_.public_test_key_1_3072_bits(),
serialized_status_list_, 100));
ProvisionedDeviceInfo device_info;
MockCertificateClientCert valid_client_cert;
std::string valid_drm_serial_number(kValidSerialNumber);
EXPECT_CALL(valid_client_cert, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(valid_client_cert, system_id())
.WillRepeatedly(Return(kValidCertSystemId));
EXPECT_CALL(valid_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
EXPECT_CALL(valid_client_cert, signer_creation_time_seconds())
.WillRepeatedly(Return(kStatusListCreationTime - 1));
EXPECT_EQ(util::OkStatus(), mock_device_status_list.GetCertStatus(
valid_client_cert, &device_info));
EXPECT_EQ(
EXPIRED_CERTIFICATE_STATUS_LIST,
mock_device_status_list.GetCertStatus(valid_client_cert, &device_info)
.error_code());
}
TEST_F(DeviceStatusListTest, IsSystemIdActive) {
std::multimap<uint32_t, std::string> preprov_keys;
preprov_keys.insert(
std::make_pair(kValidPpkSystemId, "00112233445566778899aabbccddeeff"));
KeyboxClientCert::SetPreProvisioningKeys(preprov_keys);
device_status_list_.set_allow_unknown_devices(false);
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidCertSystemId));
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidPpkSystemId));
EXPECT_FALSE(device_status_list_.IsSystemIdActive(kRevokedCertSystemId));
EXPECT_FALSE(device_status_list_.IsSystemIdActive(kUnknownSystemId));
device_status_list_.set_allow_unknown_devices(true);
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidCertSystemId));
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidPpkSystemId));
EXPECT_FALSE(device_status_list_.IsSystemIdActive(kRevokedCertSystemId));
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kUnknownSystemId));
EXPECT_TRUE(
device_status_list_.IsSystemIdActive(kRevokedAllowedDeviceCertSystemId));
}
} // namespace widevine

View File

@@ -26,9 +26,9 @@
#include "util/endian/endian.h"
#include "util/random/global_id.h"
#include "common/aes_cbc_util.h"
#include "common/certificate_type.h"
#include "common/certificate_util.h"
#include "common/client_cert.h"
#include "common/crypto_util.h"
#include "common/device_status_list.h"
#include "common/drm_root_certificate.h"
#include "common/drm_service_certificate.h"
#include "common/error_space.h"
@@ -38,8 +38,6 @@
#include "common/signing_key_util.h"
#include "common/verified_media_pipeline.h"
#include "common/vmp_checker.h"
#include "license_server_sdk/internal/client_cert.h"
#include "license_server_sdk/internal/device_status_list.h"
#include "license_server_sdk/internal/generate_error_response.h"
#include "license_server_sdk/internal/key_control_block.h"
#include "license_server_sdk/internal/parse_content_id.h"
@@ -93,26 +91,38 @@ void SessionImpl::SetPreProvisioningKeys(
}
util::Status SessionImpl::SetCertificateStatusList(
CertificateType cert_type, const std::string& certificate_status_list,
const DrmRootCertificate* root_cert, const std::string& certificate_status_list,
uint32_t expiration_period_seconds, bool allow_unknown_devices) {
return widevine::SetCertificateStatusList(
cert_type, certificate_status_list, expiration_period_seconds,
CHECK(root_cert);
util::Status status = DeviceStatusList::Instance()->UpdateStatusList(
root_cert->public_key(), certificate_status_list,
expiration_period_seconds);
if (!status.ok()) {
return status;
}
DeviceStatusList::Instance()->set_allow_unknown_devices(
allow_unknown_devices);
return util::OkStatus();
}
util::Status SessionImpl::AddDrmServiceCertificate(
CertificateType cert_type, const std::string& service_certificate,
const DrmRootCertificate* root_cert, const std::string& service_certificate,
const std::string& service_private_key,
const std::string& service_private_key_passphrase) {
util::Status status = widevine::AddDrmServiceCertificate(
cert_type, service_certificate, service_private_key,
CHECK(root_cert);
util::Status status = DrmServiceCertificate::AddDrmServiceCertificate(
root_cert, service_certificate, service_private_key,
service_private_key_passphrase);
if (!status.ok()) return status;
status = DrmServiceCertificate::ValidateDrmServiceCertificate();
if (status.ok()) {
is_service_certificate_loaded_ = true;
if (!status.ok()) {
return status;
}
return status;
is_service_certificate_loaded_ = true;
// TODO(user): Remove the need for this by having a LicenseEngine.
return VmpChecker::Instance()->SelectCertificateType(root_cert->type());
}
void SessionImpl::AllowDevelopmentClients(bool enable) {
@@ -124,22 +134,24 @@ void SessionImpl::AllowRevokedDevices(const std::string& system_id_list) {
DeviceStatusList::Instance()->AllowRevokedDevices(system_id_list);
}
util::Status SessionImpl::Create(const std::string& signed_license_request,
util::Status SessionImpl::Create(const DrmRootCertificate* root_cert,
const std::string& signed_license_request,
SessionImpl** session) {
util::Status status(util::OkStatus());
if (!is_service_certificate_loaded_) {
status = util::Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND, "");
return status;
return util::Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND, "");
}
return SessionImpl::Create(signed_license_request, session, nullptr);
return SessionImpl::Create(root_cert, signed_license_request, session,
nullptr);
}
util::Status SessionImpl::Create(const std::string& signed_license_request,
util::Status SessionImpl::Create(const DrmRootCertificate* root_cert,
const std::string& signed_license_request,
SessionImpl** session,
LicenseRequest* parsed_request_out) {
util::Status status(util::OkStatus());
CHECK(root_cert);
DCHECK(session);
DCHECK(*session == NULL);
util::Status status;
LicenseRequest* request_ptr = new LicenseRequest();
SignedMessage* signed_message_ptr = new SignedMessage();
@@ -181,7 +193,7 @@ util::Status SessionImpl::Create(const std::string& signed_license_request,
std::unique_ptr<SessionImpl> new_session(
new SessionImpl(signed_message.release(), request.release(),
has_key_control_nonce, key_control_nonce, nullptr));
status = new_session->Init();
status = new_session->Init(root_cert);
if (status.ok()) {
*session = new_session.release();
}
@@ -189,7 +201,7 @@ util::Status SessionImpl::Create(const std::string& signed_license_request,
}
util::Status SessionImpl::CreateForProxy(
const std::string& signed_license_request,
const DrmRootCertificate* root_cert, const std::string& signed_license_request,
const PlatformVerificationStatus platform_verification_status,
const ClientIdentification* client_id, SessionImpl** session,
LicenseRequest* parsed_request_out) {
@@ -229,7 +241,7 @@ util::Status SessionImpl::CreateForProxy(
has_key_control_nonce, key_control_nonce, nullptr));
if (platform_verification_status != PLATFORM_NO_VERIFICATION) {
}
status = new_session->Init();
status = new_session->Init(root_cert);
if (status.ok()) {
*session = new_session.release();
}
@@ -335,13 +347,13 @@ SessionImpl::SessionImpl(SignedMessage* message, LicenseRequest* request,
SessionImpl::~SessionImpl() {}
util::Status SessionImpl::Init() {
util::Status SessionImpl::Init(const DrmRootCertificate* root_cert) {
if (license_request_->has_client_id()) {
// Check the client token and verify the message signature.
ClientCert* client_cert_ptr = NULL;
util::Status status = ClientCert::Create(
license_request_->client_id().type(),
root_cert, license_request_->client_id().type(),
license_request_->client_id().token(), &client_cert_ptr);
client_cert_.reset(client_cert_ptr);
if (!status.ok()) {
@@ -369,10 +381,11 @@ util::Status SessionImpl::Init() {
// Generate/Derive a new signing key if one does not previously exist.
client_cert_->GenerateSigningKey(signed_message_->msg(),
license_request_->protocol_version());
if (!client_cert_->VerifySignature(
signed_message_->msg(), signed_message_->signature(),
license_request_->protocol_version())) {
return client_cert_->status();
util::Status status = client_cert_->VerifySignature(
signed_message_->msg(), signed_message_->signature(),
license_request_->protocol_version());
if (!status.ok()) {
return status;
}
}
}
@@ -829,13 +842,14 @@ util::Status SessionImpl::GenerateSignedLicense(
if (provisioned_device_info_) {
system_id = provisioned_device_info_->system_id();
}
std::unique_ptr<RsaPublicKey> rsa_public_key;
if (client_cert_ != nullptr) {
rsa_public_key.reset(RsaPublicKey::Create(client_cert_->public_key()));
if (client_cert_->type() == ClientIdentification::DRM_DEVICE_CERTIFICATE &&
rsa_public_key == nullptr) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
"create-device-public-key-failed");
if (client_cert_->type() == ClientIdentification::DRM_DEVICE_CERTIFICATE) {
std::unique_ptr<RsaPublicKey> rsa_public_key;
rsa_public_key.reset(RsaPublicKey::Create(client_cert_->public_key()));
if (rsa_public_key == nullptr) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
"create-device-public-key-failed");
}
}
}
for (int i = 0; i < license.key_size(); ++i) {
@@ -946,10 +960,6 @@ bool SessionImpl::GenerateErrorResponse(const util::Status& status,
return widevine::GenerateErrorResponse(status, signed_message_bytes);
}
std::string SessionImpl::GetRootCertificateDigest(CertificateType cert_type) {
return DrmRootCertificate::GetDigest(cert_type);
}
std::string SessionImpl::GetSdkVersionString() {
return absl::StrCat(kMajorVersion, ".", kMinorVersion, ".", kRelease);
}

View File

@@ -17,7 +17,6 @@
#include <cstdint>
#include "base/macros.h"
#include "util/status.h"
#include "common/certificate_type.h"
#include "absl/synchronization/mutex.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/license_protocol.pb.h"
@@ -28,6 +27,7 @@ namespace widevine {
class ClientCert;
class ClientIdentification;
class DrmRootCertificate;
class ContentInfo;
class SessionInit;
class SessionState;
@@ -53,19 +53,20 @@ std::string GetProviderClientToken(const SessionInit& session_init,
// license request. Example usage:
// Session::SetPreProvisioningKeys(<hex formatted std::string>);
// Session::SetCertificateStatusList(
// widevine::sdk::kCertificateTypeProduction,
// root_cert,
// cert_status_list,
// expiration_period_seconds,
// /* allow unknown device */ false);
// Session::AddDrmServiceCertificate(
// widevine::sdk::kCertificateTypeProduction,
// root_cert,
// service_certificate,
// service_private_key,
// service_private_key_passphrase);
// std::string signed_license_request;
// // assign signed_license_request to incoming license request.
// Session* session = NULL;
// util::Status status = Session::Create(signed_license_request, &session);
// util::Status status = Session::Create(root_cert, signed_license_request,
// &session);
// if (!status.ok()) {
// std::string error_license;
// if (Session::GenerateErrorResponse(status, &error_license)) {
@@ -105,25 +106,26 @@ class SessionImpl {
static void SetPreProvisioningKeys(const std::map<uint32_t, std::string>& keys);
static void SetPreProvisioningKeys(const std::multimap<uint32_t, std::string>& keys);
// Set the certificate status list system-wide. |cert_type| specifies
// whether to use development or production root certificates.
// Set the certificate status list system-wide. |root_cert| is the root
// certificate which signed the DCSL.
// |expiration_period| is the number of seconds until the
// certificate_status_list expires after its creation time
// (creation_time_seconds). If |allow_unknown_devices| is false, an error is
// returned if the device does not appear in the certificate_status_list.
static util::Status SetCertificateStatusList(
CertificateType cert_type, const std::string& certificate_status_list,
uint32_t expiration_period_seconds, bool allow_unknown_devices);
const DrmRootCertificate* root_cert,
const std::string& certificate_status_list, uint32_t expiration_period_seconds,
bool allow_unknown_devices);
// Add a service certificate system-wide. |cert_type| indicates the type of
// root certificate used to sign the service certificate;
// Add a service certificate system-wide. |root_cert| is the root certificate
// which signed the service certificate;
// |service_certificate| is a Google-generated certificate used to
// authenticate the service provider for purposes of device privacy;
// |service_private_key| is the encrypted PKCS#8 private RSA key corresponding
// to the service certificate; and |service_private_key_passphrase| is the
// password required to decrypt |service_private_key|.
static util::Status AddDrmServiceCertificate(
CertificateType cert_type, const std::string& service_certificate,
const DrmRootCertificate* root_cert, const std::string& service_certificate,
const std::string& service_private_key,
const std::string& service_private_key_passphrase);
@@ -146,7 +148,8 @@ class SessionImpl {
// Session::GenerateErrorResponse should be invoked.
// Example usage:
// Session* session = NULL;
// util::Status status = Session::Create(request_from_client, &session);
// util::Status status = Session::Create(root_cert, request_from_client,
// &session);
// if (!status.ok()) {
// std::string error_license;
// if (Session::GenerateErrorResponse(status, &error_license)) {
@@ -157,12 +160,14 @@ class SessionImpl {
// return ...
// }
// // Create license, invoke GenerateSignedLicense, etc.
static util::Status Create(const std::string& signed_license_request,
static util::Status Create(const DrmRootCertificate* root_cert,
const std::string& signed_license_request,
SessionImpl** session);
// Variation of Session::Create which also fills in the parsed LicenseRequest,
// for use in logging or debugging.
static util::Status Create(const std::string& signed_license_request,
static util::Status Create(const DrmRootCertificate* root_cert,
const std::string& signed_license_request,
SessionImpl** session,
LicenseRequest* parsed_request_out);
@@ -180,7 +185,7 @@ class SessionImpl {
// clear client identification in |client_id| and the platform verification
// in |platform_verification_status|.
static util::Status CreateForProxy(
const std::string& signed_license_request,
const DrmRootCertificate* root_cert, const std::string& signed_license_request,
const PlatformVerificationStatus platform_verification_status,
const ClientIdentification* client_id, SessionImpl** session,
LicenseRequest* parsed_request_out);
@@ -203,11 +208,6 @@ class SessionImpl {
static std::string DeriveKey(const std::string& key, const std::string& label,
const std::string& context, const uint32_t size_bits);
// Returns a std::string containing the hex-encoded SHA-256 digest of the root
// certificate specified by |cert_type|. Used for purposes of root certificate
// verification.
static std::string GetRootCertificateDigest(CertificateType cert_type);
// Returns a std::string containing the Widevine License Server SDK version in the
// form <major_version>.<minor_version>.<release> <build date> <build time> .
static std::string GetSdkVersionString();
@@ -310,7 +310,7 @@ class SessionImpl {
uint32_t nonce, widevine::ProvisionedDeviceInfo* device_info);
// Called by the SessionImpl::Create factory to initialize a new session.
util::Status Init();
util::Status Init(const DrmRootCertificate* root_cert);
util::Status GenerateNewLicenseInfo(/*IN*/ const SessionInit* session_init,
/*OUT*/ LicenseIdentification* new_id,
/*OUT*/ std::string* renewal_signing_key,

File diff suppressed because it is too large Load Diff