diff --git a/WORKSPACE b/WORKSPACE index e44365d..7f8ac0e 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,4 +1,5 @@ workspace(name = "media_cas_proxy_sdk") +load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository", "git_repository") # CCTZ (Time-zone framework), needed by abseil. git_repository( @@ -24,7 +25,7 @@ git_repository( git_repository( name = "boringssl_repo", - commit = "27ae6cadd74fd054208730827a8de3fe9bc648f0", # 2017-12-07 + commit = "14164f6fef47b7ebd97cdb0cea1624eabd6fe6b8", # 2018-11-26 remote = "https://github.com/google/boringssl.git", ) @@ -48,7 +49,7 @@ maven_jar( new_git_repository( name = "glog_repo", - build_file = "glog.BUILD", + build_file = "@//:glog.BUILD", commit = "0472b91c5defdf90cff7292e3bf7bd86770a9a0a", # 2016-07-13 remote = "https://github.com/google/glog.git", ) @@ -58,21 +59,11 @@ bind( actual = "@com_google_protobuf//:protobuf", ) -bind( - name = "com_google_protobuf_cc", - actual = "@com_google_protobuf//:protobuf", -) - bind( name = "protobuf_java", actual = "@com_google_protobuf//:protobuf_java", ) -bind( - name = "com_google_protobuf_java", - actual = "@com_google_protobuf//:protobuf_java", -) - bind( name = "openssl", actual = "@boringssl_repo//:crypto", diff --git a/common/BUILD b/common/BUILD index 28b097e..4eaf5ad 100644 --- a/common/BUILD +++ b/common/BUILD @@ -31,6 +31,98 @@ cc_library( hdrs = ["certificate_type.h"], ) +cc_library( + name = "client_cert", + srcs = ["client_cert.cc"], + hdrs = ["client_cert.h"], + deps = [ + ":crypto_util", + ":drm_root_certificate", + ":error_space", + ":random_util", + ":rsa_key", + ":signing_key_util", + ":wvm_token_handler", + "//base", + "//strings", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "@abseil_repo//absl/time", + "//util/gtl:map_util", + "//util:status", + "//protos/public:client_identification_proto", + "//protos/public:drm_certificate_proto", + "//protos/public:errors_proto", + "//protos/public:license_protocol_proto", + "//protos/public:signed_drm_certificate_proto", + ], +) + +cc_test( + name = "client_cert_test", + srcs = ["client_cert_test.cc"], + deps = [ + ":client_cert", + ":drm_root_certificate", + ":error_space", + ":wvm_test_keys", + "//base", + "//strings", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "@abseil_repo//absl/time", + "//common:rsa_key", + "//common:rsa_test_keys", + "//protos/public:drm_certificate_proto", + "//protos/public:errors_proto", + "//protos/public:signed_drm_certificate_proto", + ], +) + +cc_library( + name = "device_status_list", + srcs = ["device_status_list.cc"], + hdrs = ["device_status_list.h"], + deps = [ + ":client_cert", + ":crypto_util", + ":drm_root_certificate", + ":error_space", + ":random_util", + ":rsa_key", + ":signing_key_util", + "//base", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//util/gtl:map_util", + "//util:status", + "//protos/public:client_identification_proto", + "//protos/public:device_certificate_status_proto", + "//protos/public:errors_proto", + "//protos/public:provisioned_device_info_proto", + ], +) + +cc_test( + name = "device_status_list_test", + timeout = "short", + srcs = ["device_status_list_test.cc"], + deps = [ + ":client_cert", + ":device_status_list", + "//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_library( name = "drm_root_certificate", srcs = ["drm_root_certificate.cc"], @@ -39,8 +131,11 @@ cc_library( ":certificate_type", ":error_space", ":rsa_key", + ":sha_util", "//base", + "@abseil_repo//absl/memory", "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", "//external:openssl", "//util:status", "//protos/public:drm_certificate_proto", @@ -55,9 +150,12 @@ cc_test( srcs = ["drm_root_certificate_test.cc"], deps = [ ":drm_root_certificate", + ":error_space", ":rsa_key", ":rsa_test_keys", + ":test_drm_certificates", "//base", + "//external:protobuf", "//testing:gunit_main", "//protos/public:drm_certificate_proto", "//protos/public:errors_proto", @@ -65,22 +163,6 @@ cc_test( ], ) -cc_library( - name = "certificate_util", - srcs = ["certificate_util.cc"], - hdrs = ["certificate_util.h"], - deps = [ - ":certificate_type", - ":drm_root_certificate", - ":drm_service_certificate", - ":verified_media_pipeline", - ":vmp_checker", - "//base", - "//util:status", - "//license_server_sdk/internal:sdk", - ], -) - cc_library( name = "client_id_util", srcs = ["client_id_util.cc"], @@ -342,10 +424,10 @@ cc_test( ) cc_library( - name = "test_certificates", + name = "test_drm_certificates", testonly = 1, - srcs = ["test_certificates.cc"], - hdrs = ["test_certificates.h"], + srcs = ["test_drm_certificates.cc"], + hdrs = ["test_drm_certificates.h"], deps = [ "//base", "@abseil_repo//absl/strings", @@ -454,11 +536,12 @@ cc_test( srcs = ["drm_service_certificate_test.cc"], deps = [ ":aes_cbc_util", + ":drm_root_certificate", ":drm_service_certificate", ":rsa_key", ":rsa_test_keys", ":rsa_util", - ":test_certificates", + ":test_drm_certificates", "//base", "//external:protobuf", "//testing:gunit_main", diff --git a/common/certificate_util.cc b/common/certificate_util.cc deleted file mode 100644 index 356ff69..0000000 --- a/common/certificate_util.cc +++ /dev/null @@ -1,55 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright 2018 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/certificate_util.h" - -#include "common/certificate_type.h" -#include "common/drm_root_certificate.h" -#include "common/drm_service_certificate.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" - -namespace widevine { -util::Status SetCertificateStatusList( - CertificateType cert_type, const std::string& signed_certificate_status_list, - uint32_t expiration_period_seconds, bool allow_unknown_devices) { - util::Status status = - VmpChecker::Instance()->SelectDrmCertificateType(cert_type); - if (!status.ok()) return status; - - std::unique_ptr root_cert; - status = DrmRootCertificate::CreateByType(cert_type, &root_cert); - if (!status.ok()) { - return status; - } - status = CertificateClientCert::SetDrmRootCertificatePublicKey( - root_cert->public_key()); - if (!status.ok()) { - return status; - } - DeviceStatusList::Instance()->set_allow_unknown_devices( - allow_unknown_devices); - return DeviceStatusList::Instance()->UpdateStatusList( - root_cert->public_key(), signed_certificate_status_list, - expiration_period_seconds); -} - -util::Status AddDrmServiceCertificate( - CertificateType cert_type, const std::string& service_certificate, - const std::string& service_private_key, - const std::string& service_private_key_passphrase) { - util::Status status = - VmpChecker::Instance()->SelectDrmCertificateType(cert_type); - if (!status.ok()) return status; - return DrmServiceCertificate::AddDrmServiceCertificate( - cert_type, service_certificate, service_private_key, - service_private_key_passphrase); -} -} // namespace widevine diff --git a/common/certificate_util.h b/common/certificate_util.h deleted file mode 100644 index b6fe1d0..0000000 --- a/common/certificate_util.h +++ /dev/null @@ -1,42 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright 2018 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 COMMON_CERTIFICATE_UTIL_H_ -#define COMMON_CERTIFICATE_UTIL_H_ - -#include - -#include "util/status.h" -#include "common/certificate_type.h" - -namespace widevine { -// Set the certificate status list system-wide. |cert_type| specifies -// whether to use development or production root certificates. -// |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. -util::Status SetCertificateStatusList(CertificateType cert_type, - 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; -// |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|. -util::Status AddDrmServiceCertificate( - CertificateType cert_type, const std::string& service_certificate, - const std::string& service_private_key, - const std::string& service_private_key_passphrase); -} // namespace widevine - -#endif // COMMON_CERTIFICATE_UTIL_H_ diff --git a/common/client_cert.cc b/common/client_cert.cc new file mode 100644 index 0000000..da0d33e --- /dev/null +++ b/common/client_cert.cc @@ -0,0 +1,260 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "util/status.h" +#include "common/crypto_util.h" +#include "common/drm_root_certificate.h" +#include "common/error_space.h" +#include "common/random_util.h" +#include "common/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" + +// TODO(user): Get rid of this horror. + +namespace widevine { +namespace { + +const int kKeyboxSizeBytes = 72; + +} // namespace + +// TODO(user): change to util::StatusOr> +// instead of ClientCert** to explicitly assigning ownership of the created +// object to the caller. + +util::Status ClientCert::Create(const DrmRootCertificate* root_certificate, + ClientIdentification::TokenType token_type, + const std::string& token, ClientCert** client_cert) { + 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::CreateWithKeybox(token, client_cert); + } else if (token_type == ClientIdentification::DRM_DEVICE_CERTIFICATE) { + return CreateWithDrmCertificate(root_certificate, token, client_cert); + } else { + return util::Status(error_space, util::error::UNIMPLEMENTED, + "client-type-not-implemented"); + } +} + +util::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); + util::Status status = new_client_cert->Initialize(keybox_token); + if (!status.ok()) { + return status; + } + + *client_cert = new_client_cert.release(); + return util::OkStatus(); +} + +util::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); + util::Status status = + new_client_cert->Initialize(root_certificate, drm_certificate); + if (!status.ok()) { + return status; + } + + *client_cert = new_client_cert.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() {} + +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); +} + +util::Status KeyboxClientCert::Initialize(const std::string& keybox_bytes) { + if (keybox_bytes.size() < kKeyboxSizeBytes) { + return util::Status(error_space, INVALID_KEYBOX_TOKEN, + "keybox-token-is-too-short"); + } + + 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; + return util::Status(error_space, new_code, status.error_message()); + } + return util::OkStatus(); +} + +util::Status KeyboxClientCert::VerifySignature( + const std::string& message, const std::string& signature, + ProtocolVersion protocol_version) { + DCHECK(!signing_key().empty()); + using crypto_util::VerifySignatureHmacSha256; + if (!VerifySignatureHmacSha256( + GetClientSigningKey(signing_key(), protocol_version), signature, + message)) { + return util::Status(error_space, INVALID_SIGNATURE, "invalid-keybox-mac"); + } + return util::OkStatus(); +} + +CertificateClientCert::CertificateClientCert() {} + +CertificateClientCert::~CertificateClientCert() {} + +util::Status CertificateClientCert::Initialize( + const DrmRootCertificate* drm_root_certificate, + const std::string& serialized_certificate) { + CHECK(drm_root_certificate); + + SignedDrmCertificate signed_device_cert; + DrmCertificate device_cert; + util::Status status = drm_root_certificate->VerifyCertificate( + 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 util::Status(error_space, INVALID_DRM_CERTIFICATE, + "drm-certificate-invalid-signer"); + } + if (!model_certificate.has_serial_number()) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-signer-serial-number"); + } + // 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 util::Status(error_space, INVALID_DRM_CERTIFICATE, + "model-certificate-invalid-signer"); + } + if (provisioner_certificate.type() == DrmCertificate::PROVISIONER) { + set_signed_by_provisioner(true); + } else { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "expected-provisioning-provider-certificate-type"); + } + 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()); + } + set_signer_serial_number(model_certificate.serial_number()); + set_signer_creation_time_seconds(model_certificate.creation_time_seconds()); + if (!model_certificate.has_system_id()) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "model-certificate-missing-system-id"); + } + set_system_id(model_certificate.system_id()); + set_serial_number(device_cert.serial_number()); + set_public_key(device_cert.public_key()); + rsa_public_key_.reset(RsaPublicKey::Create(public_key())); + if (rsa_public_key_ == nullptr) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "drm-certificate-public-key-failed"); + } + + // TODO(user): Move this somewhere else. It is license protocol. + set_key(Random16Bytes()); + if (!rsa_public_key_->Encrypt(key(), &encrypted_session_key_)) { + return util::Status(error_space, ENCRYPT_ERROR, + "drm-certificate-failed-encrypt-session-key"); + } + + return util::OkStatus(); +} + +util::Status CertificateClientCert::VerifySignature( + const std::string& message, const std::string& signature, + ProtocolVersion protocol_version) { + CHECK(rsa_public_key_); + + if (!rsa_public_key_->VerifySignature(message, signature)) { + return util::Status(error_space, INVALID_SIGNATURE, ""); + } + return util::OkStatus(); +} + +} // namespace widevine diff --git a/license_server_sdk/internal/client_cert.h b/common/client_cert.h similarity index 63% rename from license_server_sdk/internal/client_cert.h rename to common/client_cert.h index 6e27b0d..11812af 100644 --- a/license_server_sdk/internal/client_cert.h +++ b/common/client_cert.h @@ -6,8 +6,8 @@ // widevine-licensing@google.com. //////////////////////////////////////////////////////////////////////////////// -#ifndef LICENSE_SERVER_SDK_INTERNAL_CLIENT_CERT_H__ -#define LICENSE_SERVER_SDK_INTERNAL_CLIENT_CERT_H__ +#ifndef COMMON_CLIENT_CERT_H__ +#define COMMON_CLIENT_CERT_H__ #include #include @@ -20,6 +20,7 @@ namespace widevine { +class DrmRootCertificate; class SignedDrmCertificate; // Handler class for LicenseRequests; validates requests and encrypts licenses. @@ -29,22 +30,25 @@ class ClientCert { public: virtual ~ClientCert() {} static util::Status Create( + const DrmRootCertificate* root_certificate, 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); + static util::Status CreateWithKeybox(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); + static util::Status CreateWithDrmCertificate( + const DrmRootCertificate* root_certificate, 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 + // classes information and the passed in message. Returns OK if signature // is valid. - virtual bool VerifySignature(const std::string& message, const std::string& signature, - ProtocolVersion protocol_version) = 0; + virtual util::Status 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, @@ -67,7 +71,6 @@ class ClientCert { 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_; } @@ -79,7 +82,6 @@ class ClientCert { 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; } @@ -96,7 +98,6 @@ class ClientCert { private: uint32_t system_id_ = 0; std::string signing_key_; - util::Status status_; std::string service_id_; DISALLOW_COPY_AND_ASSIGN(ClientCert); @@ -117,8 +118,10 @@ class KeyboxClientCert : public ClientCert { 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; + util::Status Initialize(const std::string& keybox_bytes); + + util::Status 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_; } @@ -127,14 +130,15 @@ class KeyboxClientCert : public ClientCert { } private: + KeyboxClientCert(); + 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); + DISALLOW_COPY_AND_ASSIGN(KeyboxClientCert); }; // This class implements the device certificate operations based on RSA keys. // It will unpack token and perform all the crypto operations for securing @@ -143,11 +147,9 @@ 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; + + util::Status 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 { @@ -160,9 +162,8 @@ class CertificateClientCert : public ClientCert { protected: friend class ClientCert; friend class MockCertificateClientCert; - explicit CertificateClientCert(const std::string& signed_drm_certificate_bytes); - util::Status ValidateCertificate( - const SignedDrmCertificate& signed_drm_certificate); + util::Status Initialize(const DrmRootCertificate* drm_root_certificate, + const std::string& serialized_certificate); virtual void set_public_key(const std::string& public_key) { public_key_ = public_key; } @@ -172,45 +173,16 @@ class CertificateClientCert : public ClientCert { 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 rsa_public_key_; private: - DISALLOW_IMPLICIT_CONSTRUCTORS(CertificateClientCert); + CertificateClientCert(); + + DISALLOW_COPY_AND_ASSIGN(CertificateClientCert); }; } // namespace widevine -#endif // LICENSE_SERVER_SDK_INTERNAL_CLIENT_CERT_H__ +#endif // COMMON_CLIENT_CERT_H__ diff --git a/license_server_sdk/internal/client_cert_test.cc b/common/client_cert_test.cc similarity index 82% rename from license_server_sdk/internal/client_cert_test.cc rename to common/client_cert_test.cc index 0ced7e3..4321f30 100644 --- a/license_server_sdk/internal/client_cert_test.cc +++ b/common/client_cert_test.cc @@ -6,7 +6,7 @@ // widevine-licensing@google.com. //////////////////////////////////////////////////////////////////////////////// -#include "license_server_sdk/internal/client_cert.h" +#include "common/client_cert.h" #include #include @@ -47,8 +47,8 @@ class ClientCertTest : public ::testing::Test { wvm_test_keys::GetPreprovKeyMultimap()); setup_preprov_keys_ = true; } - CHECK_OK(CertificateClientCert::SetDrmRootCertificatePublicKey( - test_keys_.public_test_key_1_3072_bits())); + ASSERT_OK( + DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert_)); } protected: @@ -112,6 +112,7 @@ class ClientCertTest : public ::testing::Test { const std::string& serial_number); RsaTestKeys test_keys_; + std::unique_ptr root_cert_; static bool setup_preprov_keys_; }; bool ClientCertTest::setup_preprov_keys_(false); @@ -126,11 +127,12 @@ void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation, // 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); + status = + ClientCert::Create(root_cert_.get(), ClientIdentification::KEYBOX, + expectation.token_, &client_cert_ptr); } else { status = - ClientCert::CreateWithToken(expectation.token_, &client_cert_ptr); + ClientCert::CreateWithKeybox(expectation.token_, &client_cert_ptr); } std::unique_ptr keybox_cert(client_cert_ptr); if (expect_success) { @@ -151,10 +153,16 @@ void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation, void ClientCertTest::TestBasicValidationDrmCertificate( const TestCertificateAndData& expectation, const bool compare_data) { + // Reset DRM certificate signature cache since some certificates get + // re-generated. + ASSERT_OK( + DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert_)); + // Test validation of a valid request. util::Status status; ClientCert* client_cert_ptr = nullptr; - status = ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE, + status = ClientCert::Create(root_cert_.get(), + ClientIdentification::DRM_DEVICE_CERTIFICATE, expectation.certificate_, &client_cert_ptr); std::unique_ptr drm_certificate_cert(client_cert_ptr); ASSERT_EQ(expectation.expected_status_, status); @@ -244,7 +252,7 @@ DrmCertificate* ClientCertTest::GenerateProvisionerCertificate( 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()); + test_keys_.public_test_key_1_3072_bits()); provisioner_certificate->set_system_id(system_id); provisioner_certificate->set_provider_id(provider_id); provisioner_certificate->set_creation_time_seconds(1234); @@ -343,13 +351,6 @@ TEST_F(ClientCertTest, InvalidCertificate) { 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 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"); @@ -383,6 +384,15 @@ TEST_F(ClientCertTest, InvalidCertificate) { GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn), system_id, device_sn)); bad_device_signature->set_signature("bad-signature"); + // Missing model system ID. + dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); + signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn)); + signer_cert->clear_system_id(); + std::unique_ptr missing_model_sn(SignCertificate( + *dev_cert, + SignCertificate(*signer_cert, nullptr, + test_keys_.private_test_key_1_3072_bits()), + test_keys_.private_test_key_2_2048_bits())); // Missing signer serial number. dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn)); @@ -404,36 +414,32 @@ TEST_F(ClientCertTest, InvalidCertificate) { const TestCertificateAndData kInvalidCertificate[] = { TestCertificateAndData("f", "", 0, util::Status(error_space, INVALID_DRM_CERTIFICATE, - "device-certificate-invalid-token")), + "invalid-signed-drm-certificate")), 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")), + "invalid-drm-certificate")), + TestCertificateAndData(bad_device_public_key->SerializeAsString(), "", 0, + util::Status(error_space, INVALID_DRM_CERTIFICATE, + "drm-certificate-public-key-failed")), TestCertificateAndData(invalid_signer->SerializeAsString(), "", 0, util::Status(error_space, INVALID_DRM_CERTIFICATE, - "device-certificate-invalid-signer")), + "invalid-signer-certificate")), + TestCertificateAndData(bad_signer_public_key->SerializeAsString(), "", 0, + util::Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-leaf-signer-public-key")), + TestCertificateAndData(bad_device_signature->SerializeAsString(), "", 0, + util::Status(error_space, INVALID_SIGNATURE, + "cache-miss-invalid-signature")), TestCertificateAndData( - bad_signer_public_key->SerializeAsString(), "", 0, + missing_model_sn->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")), + "model-certificate-missing-system-id")), 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")), + TestCertificateAndData(bad_signer_signature->SerializeAsString(), "", 0, + util::Status(error_space, INVALID_SIGNATURE, + "cache-miss-invalid-signature")), }; for (size_t i = 0; i < ABSL_ARRAYSIZE(kInvalidCertificate); ++i) { @@ -441,41 +447,6 @@ TEST_F(ClientCertTest, InvalidCertificate) { } } -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 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( @@ -483,12 +454,12 @@ TEST_F(ClientCertTest, MissingPreProvKey) { "beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9" "2517a12f4922953e")); ClientCert* client_cert_ptr = nullptr; - util::Status status = ClientCert::CreateWithToken(token, &client_cert_ptr); + util::Status status = ClientCert::CreateWithKeybox(token, &client_cert_ptr); ASSERT_EQ(MISSING_PRE_PROV_KEY, status.error_code()); } TEST_F(ClientCertTest, ValidProvisionerDeviceCert) { - const uint32_t system_id = 4890; + const uint32_t system_id = 5000; 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"); @@ -511,7 +482,8 @@ TEST_F(ClientCertTest, ValidProvisionerDeviceCert) { signed_device_cert->SerializeToString(&serialized_cert); ClientCert* client_cert_ptr = nullptr; - EXPECT_OK(ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE, + EXPECT_OK(ClientCert::Create(root_cert_.get(), + ClientIdentification::DRM_DEVICE_CERTIFICATE, serialized_cert, &client_cert_ptr)); ASSERT_TRUE(client_cert_ptr != nullptr); std::unique_ptr drm_cert(client_cert_ptr); @@ -522,7 +494,7 @@ TEST_F(ClientCertTest, ValidProvisionerDeviceCert) { EXPECT_EQ(system_id, drm_cert->system_id()); } -TEST_F(ClientCertTest, InValidProvisionerDeviceCertEmptyServiceId) { +TEST_F(ClientCertTest, InvalidProvisionerDeviceCertEmptyServiceId) { const uint32_t system_id = 4890; const std::string service_id(""); const std::string device_serial_number("device-serial-number"); @@ -547,13 +519,14 @@ TEST_F(ClientCertTest, InValidProvisionerDeviceCertEmptyServiceId) { ClientCert* client_cert_ptr = nullptr; EXPECT_EQ("missing-provisioning-service-id", - ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE, + ClientCert::Create(root_cert_.get(), + ClientIdentification::DRM_DEVICE_CERTIFICATE, serialized_cert, &client_cert_ptr) .error_message()); EXPECT_FALSE(client_cert_ptr); } -TEST_F(ClientCertTest, InValidProvisionerDeviceCertChain) { +TEST_F(ClientCertTest, InvalidProvisionerDeviceCertChain) { const uint32_t system_id = 4890; const uint32_t system_id2 = 4892; const std::string service_id("widevine_test.com"); @@ -580,28 +553,14 @@ TEST_F(ClientCertTest, InValidProvisionerDeviceCertChain) { signed_device_cert->SerializeToString(&serialized_cert); ClientCert* client_cert_ptr = nullptr; - ASSERT_EQ("expected-provisioning-provider-certificate-type", - ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE, + // TODO(user): Fix this test. It is failing for the right reasons, but the + // certificate chain is broken (intermediate signature does not match signer). + ASSERT_EQ("cache-miss-invalid-signature", + ClientCert::Create(root_cert_.get(), + 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 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 diff --git a/license_server_sdk/internal/device_status_list.cc b/common/device_status_list.cc similarity index 72% rename from license_server_sdk/internal/device_status_list.cc rename to common/device_status_list.cc index 55b2325..1e6de42 100644 --- a/license_server_sdk/internal/device_status_list.cc +++ b/common/device_status_list.cc @@ -8,24 +8,32 @@ // Implements the DeviceStatusList class. -#include "license_server_sdk/internal/device_status_list.h" +#include "common/device_status_list.h" #include #include #include "glog/logging.h" +#include "absl/strings/escaping.h" +#include "absl/strings/numbers.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/client_cert.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 { +namespace { +const char kSignedListTerminator[] = "}"; +const char kSignedList[] = "signedList\":"; +const std::size_t kSignedListLen = strlen(kSignedList); +} // namespace + 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 @@ -238,4 +246,76 @@ bool DeviceStatusList::IsRevokedSystemIdAllowed(uint32_t system_id) { return it; } +util::Status DeviceStatusList::ExtractFromProvisioningServiceResponse( + const std::string& certificate_provisioning_service_response, + std::string* signed_certificate_status_list, std::string* certificate_status_list) { + util::Status status = util::OkStatus(); + size_t signed_list_start = + certificate_provisioning_service_response.find(kSignedList); + if (signed_list_start != std::string::npos) { + size_t signed_list_end = certificate_provisioning_service_response.find( + kSignedListTerminator, signed_list_start); + if (signed_list_end == std::string::npos) { + return util::Status( + error_space, util::error::INVALID_ARGUMENT, + "Unable to parse the certificate_provisioning_service_response. " + "SignedList not terminated."); + } + std::string signed_list( + certificate_provisioning_service_response.begin() + signed_list_start + + kSignedListLen, + certificate_provisioning_service_response.begin() + signed_list_end); + + // Strip off quotes. + signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\"'), + signed_list.end()); + // Strip off spaces. + signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), ' '), + signed_list.end()); + + // Strip off newlines. + signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\n'), + signed_list.end()); + + // Strip off carriage return (the control-M character) + signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\r'), + signed_list.end()); + if (!absl::WebSafeBase64Unescape(signed_list, + signed_certificate_status_list)) { + if (!absl::Base64Unescape(signed_list, signed_certificate_status_list)) { + return util::Status(error_space, util::error::INVALID_ARGUMENT, + "Base64 decode of signedlist failed."); + } + } + } else { + // certificate_provisioning_service_response is the signed list and not a + // JSON message. + if (!absl::WebSafeBase64Unescape(certificate_provisioning_service_response, + signed_certificate_status_list)) { + if (!absl::Base64Unescape(certificate_provisioning_service_response, + signed_certificate_status_list)) { + return util::Status(error_space, util::error::INVALID_ARGUMENT, + "Base64 decode of certList failed."); + } + } + } + SignedDeviceCertificateStatusList signed_status_list; + if (!signed_status_list.ParseFromString(*signed_certificate_status_list)) { + return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, + "signed-certificate-status-list-parse-error"); + } + if (!signed_status_list.has_certificate_status_list()) { + return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, + "missing-status-list"); + } + DeviceCertificateStatusList device_certificate_status_list; + if (!device_certificate_status_list.ParseFromString( + signed_status_list.certificate_status_list())) { + return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, + "certificate-status-list-parse-error"); + } + *certificate_status_list = signed_status_list.certificate_status_list(); + return util::OkStatus(); +} + } // namespace widevine diff --git a/license_server_sdk/internal/device_status_list.h b/common/device_status_list.h similarity index 85% rename from license_server_sdk/internal/device_status_list.h rename to common/device_status_list.h index d1047fb..5bdd4c0 100644 --- a/license_server_sdk/internal/device_status_list.h +++ b/common/device_status_list.h @@ -8,8 +8,8 @@ // DeviceStatusList class header. -#ifndef LICENSE_SERVER_SDK_INTERNAL_DEVICE_STATUS_LIST_H__ -#define LICENSE_SERVER_SDK_INTERNAL_DEVICE_STATUS_LIST_H__ +#ifndef COMMON_DEVICE_STATUS_LIST_H__ +#define COMMON_DEVICE_STATUS_LIST_H__ #include #include @@ -36,6 +36,7 @@ class DeviceStatusList { 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. @@ -76,6 +77,19 @@ class DeviceStatusList { // a comma separated list of systems Ids to allow even if revoked. virtual void AllowRevokedDevices(const std::string& system_id_list); + /** + * Parses signed device certificate status list and certificate status list + * from certificateProvisoningServer response. + * + * @param certificate_provisioning_service_response + * @param signed_certificate_status_list + * @param certificate_status_list + * @return WvPLStatus - Status::OK if success, else error. + */ + static util::Status ExtractFromProvisioningServiceResponse( + const std::string& certificate_provisioning_service_response, + std::string* signed_certificate_status_list, std::string* certificate_status_list); + private: // Returns true if the system ID is allowed to be revoked. // Caller owns |system_id|. They must not be null. @@ -96,4 +110,4 @@ class DeviceStatusList { }; } // namespace widevine -#endif // LICENSE_SERVER_SDK_INTERNAL_DEVICE_STATUS_LIST_H__ +#endif // COMMON_DEVICE_STATUS_LIST_H__ diff --git a/license_server_sdk/internal/device_status_list_test.cc b/common/device_status_list_test.cc similarity index 98% rename from license_server_sdk/internal/device_status_list_test.cc rename to common/device_status_list_test.cc index 5037cc1..b76ce55 100644 --- a/license_server_sdk/internal/device_status_list_test.cc +++ b/common/device_status_list_test.cc @@ -6,7 +6,7 @@ // widevine-licensing@google.com. //////////////////////////////////////////////////////////////////////////////// -#include "license_server_sdk/internal/device_status_list.h" +#include "common/device_status_list.h" #include #include @@ -17,9 +17,9 @@ #include "testing/gmock.h" #include "testing/gunit.h" #include "absl/strings/str_cat.h" +#include "common/client_cert.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" @@ -51,7 +51,7 @@ const uint32_t kDefaultExpirePeriod = 0; class MockCertificateClientCert : public CertificateClientCert { public: - explicit MockCertificateClientCert() : CertificateClientCert("token-bytes") {} + MockCertificateClientCert() {} MOCK_CONST_METHOD0(system_id, uint32_t()); MOCK_CONST_METHOD0(signer_serial_number, std::string &()); MOCK_CONST_METHOD0(signer_creation_time_seconds, uint32_t()); @@ -61,7 +61,7 @@ class MockCertificateClientCert : public CertificateClientCert { class MockKeyboxClientCert : public KeyboxClientCert { public: - explicit MockKeyboxClientCert() : KeyboxClientCert("token-bytes") {} + MockKeyboxClientCert() {} MOCK_CONST_METHOD0(system_id, uint32_t()); MOCK_CONST_METHOD0(type, ClientIdentification::TokenType()); }; diff --git a/common/drm_root_certificate.cc b/common/drm_root_certificate.cc index dc42f66..9e52ad9 100644 --- a/common/drm_root_certificate.cc +++ b/common/drm_root_certificate.cc @@ -6,23 +6,34 @@ // widevine-licensing@google.com. //////////////////////////////////////////////////////////////////////////////// +// common_typos_disable. Successful / successfull. + #include "common/drm_root_certificate.h" #include #include "glog/logging.h" +#include "absl/memory/memory.h" #include "absl/strings/escaping.h" -#include "openssl/sha.h" -#include "common/certificate_type.h" +#include "absl/synchronization/mutex.h" #include "common/error_space.h" #include "common/rsa_key.h" +#include "common/sha_util.h" #include "protos/public/drm_certificate.pb.h" #include "protos/public/errors.pb.h" #include "protos/public/signed_drm_certificate.pb.h" namespace widevine { -// From common::TestCertificates. +namespace { + +const char kDevelopmentString[] = "dev"; // QA systems. +const char kProductionString[] = "prod"; // Production. +const char kTestingString[] = "test"; // Code development / unit tests. + +const bool kUseCache = true; + +// From common::TestDrmCertificates. // TODO(user): common::test_certificates is a testonly target, consider // how to use instead of dupliciating the test cert here. static const unsigned char kTestRootCertificate[] = { @@ -231,14 +242,145 @@ static const unsigned char kProdRootCertificate[] = { 0x1f, 0x17, 0x25, 0xce, 0x90, 0xb9, 0x6d, 0xcd, 0xc4, 0x46, 0xf5, 0xa3, 0x62, 0x13, 0x74, 0x02, 0xa7, 0x62, 0xa4, 0xfa, 0x55, 0xd9, 0xde, 0xcf, 0xa2, 0xe6, 0x80, 0x74, 0x55, 0x06, 0x49, 0xd5, 0x02, 0x0c}; +} // namespace -util::Status DrmRootCertificate::Create( - const std::string& signed_drm_certificate, - std::unique_ptr* cert) { +// Caches an individual signature for a certificate with a specific serial +// number (signer). +struct VerifiedCertSignature { + VerifiedCertSignature(const std::string& cert, const std::string& sig, + const std::string& signer_sn) + : signed_cert(cert), signature(sig), signer_serial(signer_sn) {} + + std::string signed_cert; + std::string signature; + std::string signer_serial; +}; + +// Map of certificate serial number to its signature. +typedef std::map VerifiedCertSignatures; +class VerifiedCertSignatureCache { + public: + explicit VerifiedCertSignatureCache(const RsaKeyFactory* key_factory) + : key_factory_(key_factory) {} + + // Checks cache, on miss, uses public key. If successful, adds to + // cache. + util::Status VerifySignature(const std::string& cert, const std::string& serial_number, + const std::string& signature, + const std::string& signer_public_key, + const std::string& signer_serial_number) { + { + VerifiedCertSignatures::iterator cached_signature; + absl::ReaderMutexLock read_lock(&signature_cache_mutex_); + cached_signature = signature_cache_.find(serial_number); + if (cached_signature != signature_cache_.end()) { + // TODO(user): Log which of the following three conditions occurs. + if ((cert != cached_signature->second.signed_cert) || + (signature != cached_signature->second.signature) || + (signer_serial_number != cached_signature->second.signer_serial)) { + // Cached signature mismatch. + return util::Status(error_space, INVALID_SIGNATURE, + "cached-signature-mismatch"); + } + // Cached signature match. + return util::OkStatus(); + } + } + + // Cache miss. Verify signature. + std::unique_ptr signer_key( + key_factory_->CreateFromPkcs1PublicKey(signer_public_key)); + if (!signer_key) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-signer-public-key"); + } + if (!signer_key->VerifySignature(cert, signature)) { + return util::Status(error_space, INVALID_SIGNATURE, + "cache-miss-invalid-signature"); + } + + // Add signature to cache. + absl::WriterMutexLock write_lock(&signature_cache_mutex_); + signature_cache_.emplace( + serial_number, + VerifiedCertSignature(cert, signature, signer_serial_number)); + return util::OkStatus(); + } + + private: + VerifiedCertSignatures signature_cache_ GUARDED_BY(&signature_cache_mutex_); + absl::Mutex signature_cache_mutex_; + const RsaKeyFactory* key_factory_; +}; + +util::Status DrmRootCertificate::CreateByType( + CertificateType cert_type, std::unique_ptr* cert) { CHECK(cert); + return Create(cert_type, absl::make_unique(), cert); +} + +std::unique_ptr DrmRootCertificate::CreateByType( + CertificateType cert_type, util::Status* status) { + CHECK(status); + + std::unique_ptr new_root_cert; + *status = CreateByType(cert_type, &new_root_cert); + return new_root_cert; +} + +util::Status DrmRootCertificate::CreateByTypeString( + const std::string& cert_type_string, std::unique_ptr* cert) { + CHECK(cert); + + CertificateType cert_type; + if (cert_type_string == kDevelopmentString) { + cert_type = kCertificateTypeDevelopment; + } else if (cert_type_string == kProductionString) { + cert_type = kCertificateTypeProduction; + } else if (cert_type_string == kTestingString) { + cert_type = kCertificateTypeTesting; + } else { + return util::Status( + error_space, INVALID_PARAMETER, + absl::StrCat("invalid-certificate-type ", cert_type_string)); + } + + return CreateByType(cert_type, cert); +} + +util::Status DrmRootCertificate::Create( + CertificateType cert_type, std::unique_ptr key_factory, + std::unique_ptr* cert) { + DCHECK(cert); + + std::string serialized_certificate; + switch (cert_type) { + case kCertificateTypeProduction: { + serialized_certificate.assign( + kProdRootCertificate, + kProdRootCertificate + sizeof(kProdRootCertificate)); + break; + } + case kCertificateTypeDevelopment: { + serialized_certificate.assign( + kDevRootCertificate, + kDevRootCertificate + sizeof(kDevRootCertificate)); + break; + } + case kCertificateTypeTesting: { + serialized_certificate.assign( + kTestRootCertificate, + kTestRootCertificate + sizeof(kTestRootCertificate)); + break; + } + default: + return util::Status(error_space, INVALID_PARAMETER, + "invalid-certificate-type"); + } + SignedDrmCertificate signed_root_cert; - if (!signed_root_cert.ParseFromString(signed_drm_certificate)) { + if (!signed_root_cert.ParseFromString(serialized_certificate)) { return util::Status(error_space, INVALID_DRM_CERTIFICATE, "signed-root-cert-deserialize-fail"); } @@ -259,8 +401,9 @@ util::Status DrmRootCertificate::Create( return util::Status(error_space, INVALID_DRM_CERTIFICATE, "missing-root-certificate-signature"); } + std::unique_ptr public_key( - RsaPublicKey::Create(root_cert.public_key())); + key_factory->CreateFromPkcs1PublicKey(root_cert.public_key())); if (!public_key) { return util::Status(error_space, INVALID_DRM_CERTIFICATE, "invalid-root-public-key"); @@ -270,51 +413,123 @@ util::Status DrmRootCertificate::Create( return util::Status(error_space, INVALID_DRM_CERTIFICATE, "invalid-root-certificate-signature"); } - cert->reset(new DrmRootCertificate(root_cert.public_key())); + + cert->reset(new DrmRootCertificate( + cert_type, serialized_certificate, root_cert.serial_number(), + root_cert.public_key(), std::move(key_factory))); return util::OkStatus(); } -util::Status DrmRootCertificate::CreateByType( - CertificateType cert_type, std::unique_ptr* cert) { - CHECK(cert); - return Create(GetDrmRootCertificate(cert_type), cert); +DrmRootCertificate::DrmRootCertificate( + CertificateType type, const std::string& serialized_certificate, + const std::string& serial_number, const std::string& public_key, + std::unique_ptr key_factory) + : type_(type), + serialized_certificate_(serialized_certificate), + serial_number_(serial_number), + public_key_(public_key), + key_factory_(std::move(key_factory)), + signature_cache_(new VerifiedCertSignatureCache(key_factory_.get())) {} + +DrmRootCertificate::~DrmRootCertificate() {} + +std::string DrmRootCertificate::GetDigest() const { + return absl::BytesToHexString(Sha256_Hash(serialized_certificate_)); } -std::string DrmRootCertificate::GetDrmRootCertificate(CertificateType cert_type) { - std::string root_cert; - switch (cert_type) { - case kCertificateTypeProduction: { - root_cert.assign(kProdRootCertificate, - kProdRootCertificate + sizeof(kProdRootCertificate)); - break; - } - case kCertificateTypeDevelopment: { - root_cert.assign(kDevRootCertificate, - kDevRootCertificate + sizeof(kDevRootCertificate)); - break; - } - case kCertificateTypeTesting: { - root_cert.assign(kTestRootCertificate, - kTestRootCertificate + sizeof(kTestRootCertificate)); - break; - } - default: - // TODO(user): Consider returning util::Status indicating unsupported - // cert type. - break; +util::Status DrmRootCertificate::VerifyCertificate( + const std::string& serialized_certificate, + SignedDrmCertificate* signed_certificate, + DrmCertificate* certificate) const { + std::unique_ptr local_signed_certificate; + if (!signed_certificate) { + local_signed_certificate = absl::make_unique(); + signed_certificate = local_signed_certificate.get(); } - return root_cert; + if (!signed_certificate->ParseFromString(serialized_certificate)) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-signed-drm-certificate"); + } + + std::unique_ptr local_certificate; + if (!certificate) { + local_certificate = absl::make_unique(); + certificate = local_certificate.get(); + } + if (signed_certificate->drm_certificate().empty() || + !certificate->ParseFromString(signed_certificate->drm_certificate())) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-drm-certificate"); + } + if (certificate->serial_number().empty()) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-serial-number"); + } + if (!certificate->has_creation_time_seconds()) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-creation-time"); + } + if (certificate->public_key().empty()) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-public-key"); + } + + // Verify signature chain, but do not use cache for leaf certificates. + return VerifySignatures(*signed_certificate, certificate->serial_number(), + !kUseCache); } -std::string DrmRootCertificate::GetDigest(CertificateType cert_type) { - std::string cert(GetDrmRootCertificate(cert_type)); - if (cert.empty()) { - return std::string(); +// Recursively verifies certificates with their signing certs or the root. +// use_cache should be false when initially called so that signatures do not +// cached leaf certificates not signed with the root certificate, such as for +// the case of device-unique device certificates. +// Signatures for root-signed certificates are always cached, even if they are +// leaf certificates. For example service, and provisioner certificates. +util::Status DrmRootCertificate::VerifySignatures( + const SignedDrmCertificate& signed_cert, const std::string& cert_serial_number, + bool use_cache) const { + if (!signed_cert.has_signer()) { + // Always use cache for root-signed certificates. + return signature_cache_->VerifySignature( + signed_cert.drm_certificate(), cert_serial_number, + signed_cert.signature(), public_key(), serial_number_); } - std::string hash(SHA256_DIGEST_LENGTH, 0); - SHA256(reinterpret_cast(cert.data()), cert.size(), - reinterpret_cast(&hash[0])); - return absl::BytesToHexString(hash); + + DrmCertificate signer; + if (!signer.ParseFromString(signed_cert.signer().drm_certificate())) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-signer-certificate"); + } + + // Verify the signer before verifying signed_cert. + util::Status status = + VerifySignatures(signed_cert.signer(), signer.serial_number(), kUseCache); + if (!status.ok()) { + return status; + } + + if (use_cache) { + status = signature_cache_->VerifySignature( + signed_cert.drm_certificate(), cert_serial_number, + signed_cert.signature(), signer.public_key(), signer.serial_number()); + if (!status.ok()) { + return status; + } + } else { + std::unique_ptr signer_public_key( + key_factory_->CreateFromPkcs1PublicKey(signer.public_key())); + if (!signer_public_key) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-leaf-signer-public-key"); + } + if (!signer_public_key->VerifySignature(signed_cert.drm_certificate(), + signed_cert.signature())) { + return util::Status(error_space, INVALID_SIGNATURE, + "cache-miss-invalid-signature"); + } + } + + return util::OkStatus(); } } // namespace widevine diff --git a/common/drm_root_certificate.h b/common/drm_root_certificate.h index fdea567..a18ae44 100644 --- a/common/drm_root_certificate.h +++ b/common/drm_root_certificate.h @@ -13,6 +13,8 @@ #ifndef COMMON_DRM_ROOT_CERTIFICATE_H_ #define COMMON_DRM_ROOT_CERTIFICATE_H_ +// common_typos_disable. Successful / successfull. + #include #include @@ -23,41 +25,82 @@ namespace widevine { +class DrmCertificate; +class RsaKeyFactory; +class RsaPublicKey; +class SignedDrmCertificate; +class VerifiedCertSignatureCache; + +// Root certificate and certificate chain verifier with internal caching. +// This object is thread-safe. class DrmRootCertificate { public: - virtual ~DrmRootCertificate() {} + virtual ~DrmRootCertificate(); + // Creates a DrmRootCertificate object given a certificate type. // |cert| may not be nullptr, and it points to a // std::unique_ptr which will be used to return a newly - // created DrmRootCertificate* if successful. The caller assumes ownership of - // the new DrmRootCertificate. This method returns util::Status::OK on - // success, or appropriate error status otherwise. + // created const DrmRootCertificate* if successful. The caller assumes + // ownership of the new DrmRootCertificate. This method returns + // util::Status::OK on success, or appropriate error status otherwise. static util::Status CreateByType(CertificateType cert_type, std::unique_ptr* cert); - // Returns the hex-encoded SHA-256 digest for the specified root certificate. - static std::string GetDigest(CertificateType cert_type); - // Given |cert_type|, the appropiate root certificate is returned as - // a serialized SignedDrmCertificates. - static std::string GetDrmRootCertificate(CertificateType cert_type); + + // Variant on the method above to make CLIF happy until b/110539622 is fixed. + static std::unique_ptr CreateByType( + CertificateType cert_type, util::Status* status); + + // Creates a DrmRootCertificate object given a certificate type std::string, which + // must be one of "prod", "qa", or "test". + // |cert| may not be nullptr, and it points to a + // std::unique_ptr which will be used to return a newly + // created const DrmRootCertificate* if successful. The caller assumes + // ownership of the new DrmRootCertificate. This method returns + // util::Status::OK on success, or appropriate error status otherwise. + static util::Status CreateByTypeString( + const std::string& cert_type_string, + std::unique_ptr* cert); + + // |certificate| will contgain the DRM certificate upon successful return. + // May be null. + // Returns util::Status::OK if successful, or an appropriate error code + // otherwise. + virtual util::Status VerifyCertificate( + const std::string& serialized_certificate, + SignedDrmCertificate* signed_certificate, + DrmCertificate* certificate) const; + + // Returns the hex-encoded SHA-256 digest for this certificate. + virtual std::string GetDigest() const; + + const CertificateType type() const { return type_; } + const std::string& public_key() const { return public_key_; } - // Verifies a DRM certificate. + protected: + DrmRootCertificate(CertificateType cert_type, + const std::string& serialized_certificate, + const std::string& serial_number, const std::string& public_key, + std::unique_ptr key_factory); + private: friend class DrmRootCertificateTest; - // Creates a DrmRootCertificate object given a serialized - // SignedDrmCertificate. |cert| may not be nullptr, and it points to a - // std::unique_ptr which will be used to return a newly - // created DrmRootCertificate* if successful. The caller assumes ownership of - // the new DrmRootCertificate. This method returns util::Status::OK on - // success, or appropriate error status otherwise. - // TODO(user): Consider moving to private. - static util::Status Create(const std::string& signed_drm_certificate, + static util::Status Create(CertificateType cert_type, + std::unique_ptr key_factory, std::unique_ptr* cert); - explicit DrmRootCertificate(const std::string& public_key) - : public_key_(public_key) {} + util::Status VerifySignatures(const SignedDrmCertificate& signed_cert, + const std::string& cert_serial_number, + bool use_cache) const; + + CertificateType type_; + std::string serialized_certificate_; + std::string serial_number_; std::string public_key_; + std::unique_ptr key_factory_; + mutable std::unique_ptr signature_cache_; + DISALLOW_IMPLICIT_CONSTRUCTORS(DrmRootCertificate); }; diff --git a/common/drm_root_certificate_test.cc b/common/drm_root_certificate_test.cc index f5029d9..5b192ce 100644 --- a/common/drm_root_certificate_test.cc +++ b/common/drm_root_certificate_test.cc @@ -9,92 +9,256 @@ // Description: // Unit tests for drm_root_certificate.cc +#include "common/drm_root_certificate.h" + #include +#include "google/protobuf/util/message_differencer.h" +#include "testing/gmock.h" #include "testing/gunit.h" -#include "common/drm_root_certificate.h" +#include "common/error_space.h" #include "common/rsa_key.h" #include "common/rsa_test_keys.h" +#include "common/test_drm_certificates.h" #include "protos/public/drm_certificate.pb.h" #include "protos/public/errors.pb.h" #include "protos/public/signed_drm_certificate.pb.h" +using google::protobuf::util::MessageDifferencer; + namespace widevine { +TEST(DrmRootCertificateCreateTest, TestCertificate) { + const std::string kTestCertificateHash( + "49f917b1bdfed78002a58e799a58e940" + "1fffaaed9d8d80752782b066757e2c8c"); + std::unique_ptr root_cert; + ASSERT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType( + kCertificateTypeTesting, &root_cert)); + ASSERT_TRUE(root_cert != nullptr); + EXPECT_EQ(kTestCertificateHash, root_cert->GetDigest()); +} + +TEST(DrmRootCertificateCreateTest, DevCertificate) { + const std::string kDevelopmentCertificateHash( + "0e25ee95476a770f30b98ac5ef778b3f" + "137b66c29385b84f547a361b4724b17d"); + std::unique_ptr root_cert; + ASSERT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType( + kCertificateTypeDevelopment, &root_cert)); + ASSERT_TRUE(root_cert != nullptr); + EXPECT_EQ(kDevelopmentCertificateHash, root_cert->GetDigest()); +} + +TEST(DrmRootCertificateCreateTest, ProdCertificate) { + const std::string kProductionCertificateHash( + "d62fdabc9286648a81f7d3bedaf2f5a5" + "27bbad39bc38da034ba98a21569adb9b"); + std::unique_ptr root_cert; + ASSERT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType( + kCertificateTypeProduction, &root_cert)); + ASSERT_TRUE(root_cert != nullptr); + EXPECT_EQ(kProductionCertificateHash, root_cert->GetDigest()); +} + +TEST(DrmRootCertificateTestCertificatesTest, Success) { + TestDrmCertificates test_certs; + std::unique_ptr root_cert; + ASSERT_TRUE( + DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert) + .ok()); + EXPECT_TRUE(root_cert + ->VerifyCertificate(test_certs.test_root_certificate(), + nullptr, nullptr) + .ok()); + EXPECT_TRUE( + root_cert + ->VerifyCertificate(test_certs.test_intermediate_certificate(), + nullptr, nullptr) + .ok()); + EXPECT_TRUE(root_cert + ->VerifyCertificate(test_certs.test_user_device_certificate(), + nullptr, nullptr) + .ok()); + EXPECT_TRUE(root_cert + ->VerifyCertificate(test_certs.test_service_certificate(), + nullptr, nullptr) + .ok()); +} + class DrmRootCertificateTest : public testing::Test { protected: - DrmRootCertificateTest() {} - util::Status DrmRootCertificateCreate( - const std::string& signed_drm_certificate, - std::unique_ptr* cert) { - return DrmRootCertificate::Create(signed_drm_certificate, cert); + DrmRootCertificateTest() { + private_keys_.emplace_back( + RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())); + private_keys_.emplace_back( + RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits())); + private_keys_.emplace_back( + RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits())); } + + void SetUp() override { + drm_certificates_[0].set_serial_number("level 0"); + drm_certificates_[0].set_creation_time_seconds(0); + drm_certificates_[0].set_public_key( + test_keys_.public_test_key_1_3072_bits()); + drm_certificates_[1].set_serial_number("level 1"); + drm_certificates_[1].set_creation_time_seconds(1); + drm_certificates_[1].set_public_key( + test_keys_.public_test_key_2_2048_bits()); + drm_certificates_[2].set_serial_number("level 2"); + drm_certificates_[2].set_creation_time_seconds(2); + drm_certificates_[2].set_public_key( + test_keys_.public_test_key_3_2048_bits()); + + ASSERT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType( + kCertificateTypeTesting, &root_cert_)); + } + + void GenerateSignedDrmCertificate() { + SignedDrmCertificate* current_sc(&signed_drm_certificate_); + ASSERT_TRUE(drm_certificates_[2].SerializeToString( + current_sc->mutable_drm_certificate())); + ASSERT_TRUE(private_keys_[1]->GenerateSignature( + current_sc->drm_certificate(), current_sc->mutable_signature())); + + current_sc = current_sc->mutable_signer(); + ASSERT_TRUE(drm_certificates_[1].SerializeToString( + current_sc->mutable_drm_certificate())); + ASSERT_TRUE(private_keys_[0]->GenerateSignature( + current_sc->drm_certificate(), current_sc->mutable_signature())); + + current_sc = current_sc->mutable_signer(); + ASSERT_TRUE(drm_certificates_[0].SerializeToString( + current_sc->mutable_drm_certificate())); + ASSERT_TRUE(private_keys_[0]->GenerateSignature( + current_sc->drm_certificate(), current_sc->mutable_signature())); + } + + RsaTestKeys test_keys_; + std::vector> private_keys_; + SignedDrmCertificate signed_drm_certificate_; + DrmCertificate drm_certificates_[3]; + std::unique_ptr root_cert_; }; -TEST_F(DrmRootCertificateTest, DrmRootCertificateCreation) { - RsaTestKeys test_keys; - std::unique_ptr root_cert; - - // First, invalid serialized cert. Should fail. - EXPECT_EQ(INVALID_DRM_CERTIFICATE, - DrmRootCertificateCreate("bad_cert", &root_cert).error_code()); - SignedDrmCertificate signed_cert; - std::string serialized; - // Serialized empty cert. Should fail. - ASSERT_TRUE(signed_cert.SerializeToString(&serialized)); - EXPECT_NE(util::OkStatus(), - DrmRootCertificateCreate(serialized, &root_cert)); - // Add public key. Should still fail. - DrmCertificate drm_cert; - drm_cert.set_public_key(test_keys.public_test_key_1_3072_bits()); - ASSERT_TRUE( - drm_cert.SerializeToString(signed_cert.mutable_drm_certificate())); - ASSERT_TRUE(signed_cert.SerializeToString(&serialized)); - EXPECT_EQ(INVALID_DRM_CERTIFICATE, - DrmRootCertificateCreate(serialized, &root_cert).error_code()); - // Now self-sign the cert. Should succeed. - std::unique_ptr private_key( - RsaPrivateKey::Create(test_keys.private_test_key_1_3072_bits())); - ASSERT_TRUE(private_key.get()); - ASSERT_TRUE(private_key->GenerateSignature(signed_cert.drm_certificate(), - signed_cert.mutable_signature())); - ASSERT_TRUE(signed_cert.SerializeToString(&serialized)); - EXPECT_EQ(util::OkStatus(), - DrmRootCertificateCreate(serialized, &root_cert)); - ASSERT_TRUE(root_cert); - // Verify the public key. - EXPECT_EQ(test_keys.public_test_key_1_3072_bits(), root_cert->public_key()); +TEST_F(DrmRootCertificateTest, SuccessNoOutput) { + GenerateSignedDrmCertificate(); + ASSERT_EQ(util::OkStatus(), + root_cert_->VerifyCertificate( + signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); } -TEST_F(DrmRootCertificateTest, DrmRootCertificateCreationByType) { - std::unique_ptr root_cert; - EXPECT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType( - kCertificateTypeTesting, &root_cert)); - ASSERT_TRUE(root_cert != nullptr); - EXPECT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType( - kCertificateTypeDevelopment, &root_cert)); - ASSERT_TRUE(root_cert != nullptr); - EXPECT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType( - kCertificateTypeProduction, &root_cert)); - ASSERT_TRUE(root_cert != nullptr); +TEST_F(DrmRootCertificateTest, SuccessWithOutput) { + GenerateSignedDrmCertificate(); + SignedDrmCertificate out_signed_cert; + DrmCertificate out_cert; + ASSERT_EQ(util::OkStatus(), root_cert_->VerifyCertificate( + signed_drm_certificate_.SerializeAsString(), + &out_signed_cert, &out_cert)); + EXPECT_TRUE( + MessageDifferencer::Equals(out_signed_cert, signed_drm_certificate_)); + EXPECT_TRUE(MessageDifferencer::Equals(out_cert, drm_certificates_[2])); } -TEST_F(DrmRootCertificateTest, DrmRootCertificateDigest) { - const std::string test_cert_hash( - "49f917b1bdfed78002a58e799a58e940" - "1fffaaed9d8d80752782b066757e2c8c"); - const std::string dev_cert_hash( - "0e25ee95476a770f30b98ac5ef778b3f" - "137b66c29385b84f547a361b4724b17d"); - const std::string prod_cert_hash( - "d62fdabc9286648a81f7d3bedaf2f5a5" - "27bbad39bc38da034ba98a21569adb9b"); - EXPECT_EQ(test_cert_hash, - DrmRootCertificate::GetDigest(kCertificateTypeTesting)); - EXPECT_EQ(dev_cert_hash, - DrmRootCertificate::GetDigest(kCertificateTypeDevelopment)); - EXPECT_EQ(prod_cert_hash, - DrmRootCertificate::GetDigest(kCertificateTypeProduction)); +TEST_F(DrmRootCertificateTest, InvalidSignedDrmCertificate) { + EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-signed-drm-certificate"), + root_cert_->VerifyCertificate("pure garbage", nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, InvalidSignerCertificate) { + GenerateSignedDrmCertificate(); + signed_drm_certificate_.mutable_signer()->set_drm_certificate("more garbage"); + EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-signer-certificate"), + root_cert_->VerifyCertificate( + signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, MissingDrmCertificate) { + GenerateSignedDrmCertificate(); + signed_drm_certificate_.clear_drm_certificate(); + EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-drm-certificate"), + root_cert_->VerifyCertificate( + signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, InvalidDrmCertificate) { + GenerateSignedDrmCertificate(); + signed_drm_certificate_.set_drm_certificate("junk"); + EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-drm-certificate"), + root_cert_->VerifyCertificate( + signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, InvalidPublicKey) { + drm_certificates_[0].set_public_key("rubbish"); + GenerateSignedDrmCertificate(); + EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-signer-public-key"), + root_cert_->VerifyCertificate( + signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, MissingPublicKey) { + drm_certificates_[2].clear_public_key(); + GenerateSignedDrmCertificate(); + EXPECT_EQ( + util::Status(error_space, INVALID_DRM_CERTIFICATE, "missing-public-key"), + root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(), + nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, MissingCreationTime) { + drm_certificates_[2].clear_creation_time_seconds(); + GenerateSignedDrmCertificate(); + EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-creation-time"), + root_cert_->VerifyCertificate( + signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, MissingSerialNumber) { + drm_certificates_[2].set_serial_number(""); + GenerateSignedDrmCertificate(); + EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-serial-number"), + root_cert_->VerifyCertificate( + signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, InvalidSignatureWithNoCache) { + GenerateSignedDrmCertificate(); + signed_drm_certificate_.mutable_signer()->set_signature( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + EXPECT_EQ(util::Status(error_space, INVALID_SIGNATURE, + "cache-miss-invalid-signature"), + root_cert_->VerifyCertificate( + signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, InvalidSignatureWithCache) { + GenerateSignedDrmCertificate(); + // Verify and cache. + ASSERT_EQ(util::OkStatus(), + root_cert_->VerifyCertificate( + signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); + + // Verify success using cache. + ASSERT_EQ(util::OkStatus(), + root_cert_->VerifyCertificate( + signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); + + // Verify failure using cache. + signed_drm_certificate_.mutable_signer()->set_signature( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + EXPECT_EQ( + util::Status(error_space, INVALID_SIGNATURE, "cached-signature-mismatch"), + root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(), + nullptr, nullptr)); } } // namespace widevine diff --git a/common/drm_service_certificate.cc b/common/drm_service_certificate.cc index 75c0047..466ee31 100644 --- a/common/drm_service_certificate.cc +++ b/common/drm_service_certificate.cc @@ -108,49 +108,24 @@ DrmServiceCertificateMap* DrmServiceCertificateMap::GetInstance() { } // namespace util::Status DrmServiceCertificate::AddDrmServiceCertificate( - const std::string& root_public_key, 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) { - std::unique_ptr root_key(RsaPublicKey::Create(root_public_key)); - if (root_key == nullptr) { - return util::Status(error_space, INVALID_DRM_CERTIFICATE, - "root-certificate-rsa-public-key-failed"); - } - SignedDrmCertificate signed_cert; - if (!signed_cert.ParseFromString(service_certificate)) { - return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, - "signed-certificate-parse-failed"); - } - if (!root_key->VerifySignature(signed_cert.drm_certificate(), - signed_cert.signature())) { - return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, - "certificate-signature-verification-failed"); - } DrmCertificate drm_cert; - if (!drm_cert.ParseFromString(signed_cert.drm_certificate())) { - return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, - "certificate-parse-failed"); + util::Status status = + root_cert->VerifyCertificate(service_certificate, nullptr, &drm_cert); + if (!status.ok()) { + return status; } + if (drm_cert.type() != DrmCertificate::SERVICE) { return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, "not-service-certificate"); } - if (drm_cert.serial_number().empty()) { - return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, - "missing-certificate-serial-number"); - } if (drm_cert.provider_id().empty()) { return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, "missing-certificate-service-id"); } - if (!drm_cert.has_creation_time_seconds()) { - return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, - "missing-certificate-creation-time"); - } - if (drm_cert.public_key().empty()) { - return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, - "missing-certificate-public-key"); - } std::unique_ptr public_key( RsaPublicKey::Create(drm_cert.public_key())); if (!public_key) { @@ -178,21 +153,6 @@ util::Status DrmServiceCertificate::AddDrmServiceCertificate( return util::OkStatus(); } -util::Status DrmServiceCertificate::AddDrmServiceCertificate( - CertificateType root_cert_type, const std::string& service_certificate, - const std::string& service_private_key, - const std::string& service_private_key_passphrase) { - std::unique_ptr root_cert; - util::Status status = - DrmRootCertificate::CreateByType(root_cert_type, &root_cert); - if (!status.ok()) { - return status; - } - return AddDrmServiceCertificate(root_cert->public_key(), service_certificate, - service_private_key, - service_private_key_passphrase); -} - const DrmServiceCertificate* DrmServiceCertificate::GetDefaultDrmServiceCertificate() { return DrmServiceCertificateMap::GetInstance()->GetDefaultCert(); @@ -212,30 +172,15 @@ const DrmServiceCertificate* DrmServiceCertificate::GetDrmServiceCertificate( } util::Status DrmServiceCertificate::SetDefaultDrmServiceCertificate( - const std::string& root_public_key, const std::string& service_certificate, + const DrmRootCertificate* root_drm_cert, const std::string& service_certificate, const std::string& service_private_key, const std::string& service_private_key_passphrase) { DrmServiceCertificateMap::GetInstance()->ClearDefaultDrmServiceCertificate(); - return AddDrmServiceCertificate(root_public_key, service_certificate, + return AddDrmServiceCertificate(root_drm_cert, service_certificate, service_private_key, service_private_key_passphrase); } -util::Status DrmServiceCertificate::SetDefaultDrmServiceCertificate( - CertificateType root_cert_type, const std::string& service_certificate, - const std::string& service_private_key, - const std::string& service_private_key_passphrase) { - std::unique_ptr root_cert; - util::Status status = - DrmRootCertificate::CreateByType(root_cert_type, &root_cert); - if (!status.ok()) { - return status; - } - return SetDefaultDrmServiceCertificate( - root_cert->public_key(), service_certificate, service_private_key, - service_private_key_passphrase); -} - util::Status DrmServiceCertificate::DecryptClientIdentification( const EncryptedClientIdentification& encrypted_client_id, ClientIdentification* client_id) { diff --git a/common/drm_service_certificate.h b/common/drm_service_certificate.h index 88a2161..69a3036 100644 --- a/common/drm_service_certificate.h +++ b/common/drm_service_certificate.h @@ -29,13 +29,16 @@ class RequestInspectorTest; namespace widevine { class ClientIdentification; +class DrmRootCertificate; class EncryptedClientIdentification; +// TODO(user): Add a DrmCertificateList class to provide the static method +// functionality. class DrmServiceCertificate { public: // Create a new DrmServiceCertificate object and add it to the list of valid - // service certificates. |root_cert_type| indicates which root public key to - // use to verify |service_certificate|, |service_certificate| is a + // service certificates. |drm_root_cert| is the root certificate for the type + // of certifiate being added. |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, @@ -46,16 +49,16 @@ class DrmServiceCertificate { // used as the default service certificate. // This method is thread-safe. static util::Status AddDrmServiceCertificate( - CertificateType root_cert_type, const std::string& service_certificate, - const std::string& service_private_key, + const DrmRootCertificate* root_drm_cert, + const std::string& service_certificate, const std::string& service_private_key, const std::string& service_private_key_passphrase); // Same as AddDrmServiceCertificate(), but will clear the default service // certificate if it's set. This will result in this service certificate // being set as the default service certificate. static util::Status SetDefaultDrmServiceCertificate( - CertificateType root_cert_type, const std::string& service_certificate, - const std::string& service_private_key, + const DrmRootCertificate* root_drm_cert, + const std::string& service_certificate, const std::string& service_private_key, const std::string& service_private_key_passphrase); // Returns the default service certificate. Will return null if no default diff --git a/common/drm_service_certificate_test.cc b/common/drm_service_certificate_test.cc index fbd02bb..b2b8628 100644 --- a/common/drm_service_certificate_test.cc +++ b/common/drm_service_certificate_test.cc @@ -6,6 +6,8 @@ // widevine-licensing@google.com. //////////////////////////////////////////////////////////////////////////////// +#include "common/drm_service_certificate.h" + #include #include "glog/logging.h" @@ -14,11 +16,11 @@ #include "testing/gunit.h" #include "absl/strings/escaping.h" #include "common/aes_cbc_util.h" -#include "common/drm_service_certificate.h" +#include "common/drm_root_certificate.h" #include "common/rsa_key.h" #include "common/rsa_test_keys.h" #include "common/rsa_util.h" -#include "common/test_certificates.h" +#include "common/test_drm_certificates.h" #include "protos/public/client_identification.pb.h" #include "protos/public/drm_certificate.pb.h" #include "protos/public/errors.pb.h" // IWYU pragma: keep @@ -38,7 +40,9 @@ class DrmServiceCertificateTest : public ::testing::Test { iv_(absl::HexStringToBytes(kIv)), root_private_key_( RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())) { - CHECK(root_private_key_ != nullptr); + EXPECT_TRUE(root_private_key_); + EXPECT_OK( + DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert_)); client_id_.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE); client_id_.set_token(test_certs_.test_user_device_certificate()); } @@ -78,8 +82,7 @@ class DrmServiceCertificateTest : public ::testing::Test { return util::Status(util::error::INTERNAL, ""); } return DrmServiceCertificate::SetDefaultDrmServiceCertificate( - test_keys_.public_test_key_1_3072_bits(), signed_cert, - encrypted_private_key, kPassphrase); + root_cert_.get(), signed_cert, encrypted_private_key, kPassphrase); } util::Status AddDrmServiceCertificate(const std::string& serial_number, @@ -95,8 +98,7 @@ class DrmServiceCertificateTest : public ::testing::Test { return util::Status(util::error::INTERNAL, ""); } return DrmServiceCertificate::AddDrmServiceCertificate( - test_keys_.public_test_key_1_3072_bits(), signed_cert, - encrypted_private_key, kPassphrase); + root_cert_.get(), signed_cert, encrypted_private_key, kPassphrase); } void EncryptClientIdentification( @@ -118,10 +120,11 @@ class DrmServiceCertificateTest : public ::testing::Test { } RsaTestKeys test_keys_; - TestCertificates test_certs_; + TestDrmCertificates test_certs_; std::string privacy_key_; std::string iv_; std::unique_ptr root_private_key_; + std::unique_ptr root_cert_; ClientIdentification client_id_; }; @@ -201,6 +204,7 @@ TEST_F(DrmServiceCertificateTest, MultipleCertsPerService) { std::string serial_number1("serial_number1"); std::string serial_number2("serial_number2"); std::string serial_number3("serial_number3"); + std::string serial_number4("serial_number4"); std::string provider_id("someservice.com"); uint32_t creation_time_seconds(1234); @@ -249,13 +253,13 @@ TEST_F(DrmServiceCertificateTest, MultipleCertsPerService) { ASSERT_TRUE(drm_cert.ParseFromString(signed_cert.drm_certificate())); EXPECT_EQ(serial_number1, drm_cert.serial_number()); - EXPECT_OK(SetDefaultDrmServiceCertificate(serial_number2, provider_id, + EXPECT_OK(SetDefaultDrmServiceCertificate(serial_number4, provider_id, creation_time_seconds)); default_cert = DrmServiceCertificate::GetDefaultDrmServiceCertificate(); ASSERT_TRUE(default_cert); ASSERT_TRUE(signed_cert.ParseFromString(default_cert->certificate())); ASSERT_TRUE(drm_cert.ParseFromString(signed_cert.drm_certificate())); - EXPECT_EQ(serial_number2, drm_cert.serial_number()); + EXPECT_EQ(serial_number4, drm_cert.serial_number()); } TEST_F(DrmServiceCertificateTest, DrmServiceCertificateNotFound) { diff --git a/common/mock_rsa_key.h b/common/mock_rsa_key.h index dd8fa9a..e221785 100644 --- a/common/mock_rsa_key.h +++ b/common/mock_rsa_key.h @@ -54,14 +54,14 @@ class MockRsaKeyFactory : public RsaKeyFactory { MockRsaKeyFactory() {} ~MockRsaKeyFactory() override {} - MOCK_METHOD1(CreateFromPkcs1PrivateKey, - std::unique_ptr(const std::string& private_key)); - MOCK_METHOD2( + MOCK_CONST_METHOD1(CreateFromPkcs1PrivateKey, + std::unique_ptr(const std::string& private_key)); + MOCK_CONST_METHOD2( CreateFromPkcs8PrivateKey, std::unique_ptr(const std::string& private_key, const std::string& private_key_passphrase)); - MOCK_METHOD1(CreateFromPkcs1PublicKey, - std::unique_ptr(const std::string& public_key)); + MOCK_CONST_METHOD1(CreateFromPkcs1PublicKey, + std::unique_ptr(const std::string& public_key)); private: MockRsaKeyFactory(const MockRsaKeyFactory&) = delete; diff --git a/common/openssl_util.h b/common/openssl_util.h index b715c8b..824aa45 100644 --- a/common/openssl_util.h +++ b/common/openssl_util.h @@ -14,6 +14,7 @@ #include "openssl/bio.h" #include "openssl/evp.h" +#include "openssl/pkcs7.h" #include "openssl/rsa.h" #include "openssl/x509v3.h" @@ -46,6 +47,7 @@ using ScopedOpenSSLStackOnly = using ScopedBIGNUM = ScopedOpenSSLType; using ScopedBIO = ScopedOpenSSLType; +using ScopedPKCS7 = ScopedOpenSSLType; using ScopedPKEY = ScopedOpenSSLType; using ScopedRSA = ScopedOpenSSLType; using ScopedX509 = ScopedOpenSSLType; @@ -59,6 +61,7 @@ using ScopedX509StoreCtx = ScopedOpenSSLType; using ScopedX509Req = ScopedOpenSSLType; using ScopedAsn1UtcTime = ScopedOpenSSLType; +using ScopedAsn1Time = ScopedOpenSSLType; using ScopedAsn1Utc8String = ScopedOpenSSLType; using ScopedAsn1Integer = ScopedOpenSSLType; diff --git a/common/remote_attestation_verifier.cc b/common/remote_attestation_verifier.cc index b8cb252..185369a 100644 --- a/common/remote_attestation_verifier.cc +++ b/common/remote_attestation_verifier.cc @@ -107,7 +107,7 @@ RemoteAttestationVerifier& RemoteAttestationVerifier::get() { return instance; } -void RemoteAttestationVerifier::EnableTestCertificates(bool enable) { +void RemoteAttestationVerifier::EnableTestDrmCertificates(bool enable) { absl::WriterMutexLock lock(&ca_mutex_); enable_test_certificates_ = enable; ca_.reset(); diff --git a/common/remote_attestation_verifier.h b/common/remote_attestation_verifier.h index 4ea7d2c..34be518 100644 --- a/common/remote_attestation_verifier.h +++ b/common/remote_attestation_verifier.h @@ -40,7 +40,7 @@ class RemoteAttestationVerifier { // Call to use the test (non-production) remote attestation root certificate. // This method is thread-safe. - void EnableTestCertificates(bool enable); + void EnableTestDrmCertificates(bool enable); // Call to verify a RemoteAttestation challenge response, used in licensing // protocol. diff --git a/common/rsa_key.cc b/common/rsa_key.cc index 3f17744..6a14527 100644 --- a/common/rsa_key.cc +++ b/common/rsa_key.cc @@ -286,12 +286,12 @@ RsaKeyFactory::RsaKeyFactory() {} RsaKeyFactory::~RsaKeyFactory() {} std::unique_ptr RsaKeyFactory::CreateFromPkcs1PrivateKey( - const std::string& private_key) { + const std::string& private_key) const { return std::unique_ptr(RsaPrivateKey::Create(private_key)); } std::unique_ptr RsaKeyFactory::CreateFromPkcs8PrivateKey( - const std::string& private_key, const std::string& private_key_passphrase) { + const std::string& private_key, const std::string& private_key_passphrase) const { std::string pkcs1_key; const bool result = private_key_passphrase.empty() @@ -306,7 +306,7 @@ std::unique_ptr RsaKeyFactory::CreateFromPkcs8PrivateKey( } std::unique_ptr RsaKeyFactory::CreateFromPkcs1PublicKey( - const std::string& public_key) { + const std::string& public_key) const { return std::unique_ptr(RsaPublicKey::Create(public_key)); } diff --git a/common/rsa_key.h b/common/rsa_key.h index 30913a8..cb15ff5 100644 --- a/common/rsa_key.h +++ b/common/rsa_key.h @@ -60,9 +60,12 @@ class RsaPrivateKey { // Returns the RSA key size (modulus) in bytes. virtual uint32_t KeySize() const; + private: + friend class RsaPublicKey; + friend class X509CertificateBuilder; // TODO(user): Get rid of this. + const RSA* key() const { return key_; } - private: RSA* key_; // SWIG appears to think this declaration is a syntax error. Excluding it for @@ -110,9 +113,12 @@ class RsaPublicKey { // Returns the RSA key size (modulus) in bytes. virtual uint32_t KeySize() const; + private: + friend class RsaPrivateKey; + friend class X509CertificateBuilder; // TODO(user): Get rid of this. + const RSA* key() const { return key_; } - private: RSA* key_; // SWIG appears to think this declaration is a syntax error. Excluding it for @@ -130,16 +136,16 @@ class RsaKeyFactory { // Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey. virtual std::unique_ptr CreateFromPkcs1PrivateKey( - const std::string& private_key); + const std::string& private_key) const; // Create a PKCS#1 RsaPrivateKey object using an PKCS#8 PrivateKeyInfo or // EncryptedPrivateKeyInfo (if |private_key_passprhase| is not empty). virtual std::unique_ptr CreateFromPkcs8PrivateKey( - const std::string& private_key, const std::string& private_key_passphrase); + const std::string& private_key, const std::string& private_key_passphrase) const; // Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey. virtual std::unique_ptr CreateFromPkcs1PublicKey( - const std::string& public_key); + const std::string& public_key) const; private: DISALLOW_COPY_AND_ASSIGN(RsaKeyFactory); diff --git a/common/test_certificates.cc b/common/test_drm_certificates.cc similarity index 99% rename from common/test_certificates.cc rename to common/test_drm_certificates.cc index b9f53d8..69faa48 100644 --- a/common/test_certificates.cc +++ b/common/test_drm_certificates.cc @@ -8,7 +8,7 @@ // -#include "common/test_certificates.h" +#include "common/test_drm_certificates.h" namespace widevine { @@ -311,7 +311,7 @@ const unsigned char kTestDrmServiceCertificate[] = { 0x34, 0xba, 0xf5, 0xec, 0xaf, 0x26, 0xfb, 0x64, 0xc4, 0x38, 0x7e, 0xdb, 0x51, 0x28, 0x49, 0xa7, 0x12, 0x88, 0xa5, 0x6d, 0xa2, 0xfa}; -TestCertificates::TestCertificates() +TestDrmCertificates::TestDrmCertificates() : test_root_certificate_( kTestRootCertificate, kTestRootCertificate + sizeof(kTestRootCertificate)), diff --git a/common/test_certificates.h b/common/test_drm_certificates.h similarity index 84% rename from common/test_certificates.h rename to common/test_drm_certificates.h index 598b492..666d693 100644 --- a/common/test_certificates.h +++ b/common/test_drm_certificates.h @@ -10,18 +10,18 @@ // Class contains certificates that can be used for testing. Provides methods // to retrieve a test root certificate, a test intermediate certificate and a // test user device certificate. -#ifndef COMMON_TEST_CERTIFICATES_H_ -#define COMMON_TEST_CERTIFICATES_H_ +#ifndef COMMON_TEST_DRM_CERTIFICATES_H_ +#define COMMON_TEST_DRM_CERTIFICATES_H_ #include #include "base/macros.h" namespace widevine { -class TestCertificates { +class TestDrmCertificates { public: - TestCertificates(); - virtual ~TestCertificates() {} + TestDrmCertificates(); + virtual ~TestDrmCertificates() {} // returns a test root certificate const std::string& test_root_certificate() const { return test_root_certificate_; } @@ -47,8 +47,8 @@ class TestCertificates { const std::string test_user_device_certificate_; const std::string test_service_certificate_; - DISALLOW_COPY_AND_ASSIGN(TestCertificates); + DISALLOW_COPY_AND_ASSIGN(TestDrmCertificates); }; } // namespace widevine -#endif // COMMON_TEST_CERTIFICATES_H_ +#endif // COMMON_TEST_DRM_CERTIFICATES_H_ diff --git a/common/vmp_checker.cc b/common/vmp_checker.cc index cbba839..e4354b8 100644 --- a/common/vmp_checker.cc +++ b/common/vmp_checker.cc @@ -248,7 +248,7 @@ VmpChecker::VmpChecker() : allow_development_vmp_(false) {} VmpChecker::~VmpChecker() {} -util::Status VmpChecker::SelectDrmCertificateType(CertificateType cert_type) { +util::Status VmpChecker::SelectCertificateType(CertificateType cert_type) { std::unique_ptr ca_cert(new X509Cert); util::Status status = ca_cert->LoadDer( cert_type == kCertificateTypeProduction diff --git a/common/vmp_checker.h b/common/vmp_checker.h index e5269ad..b16107c 100644 --- a/common/vmp_checker.h +++ b/common/vmp_checker.h @@ -35,7 +35,7 @@ class VmpChecker { static VmpChecker* Instance(); // Select the type of root to use. Not thread-safe. - virtual util::Status SelectDrmCertificateType(CertificateType root_type); + virtual util::Status SelectCertificateType(CertificateType cert_type); // Verify VMP data and return appropriate result. virtual util::Status VerifyVmpData(const std::string& vmp_data, Result* result); diff --git a/common/vmp_checker_test.cc b/common/vmp_checker_test.cc index 521b5a6..005dc86 100644 --- a/common/vmp_checker_test.cc +++ b/common/vmp_checker_test.cc @@ -154,8 +154,8 @@ const char kSameAsPrevious[] = ""; class VmpCheckerTest : public ::testing::Test { public: void SetUp() override { - ASSERT_OK(VmpChecker::Instance()->SelectDrmCertificateType( - kCertificateTypeTesting)); + ASSERT_OK( + VmpChecker::Instance()->SelectCertificateType(kCertificateTypeTesting)); vmp_data_.Clear(); VmpChecker::Instance()->set_allow_development_vmp(true); signing_key_.reset( diff --git a/common/x509_cert.cc b/common/x509_cert.cc index a8e9a45..12cf3ed 100644 --- a/common/x509_cert.cc +++ b/common/x509_cert.cc @@ -255,6 +255,41 @@ util::Status X509CertChain::LoadPkcs7(const std::string& pk7_cert_chain) { return util::OkStatus(); } +std::string X509CertChain::GetPkcs7() { + std::string pkcs7_cert; + ScopedX509Stack cert_stack(sk_X509_new_null()); + for (X509Cert* cert : cert_chain_) { + // X509 stack takes ownership of certificates. Copy certificates to retain + // |cert_chain_|. + X509Cert cert_copy; + if (!cert_copy.LoadPem(cert->GetPem()).ok()) { + LOG(WARNING) << "Certificate chain serialization failed"; + return ""; + } + X509* openssl_cert_copy = const_cast(cert_copy.openssl_cert()); + cert_copy.openssl_cert_ = nullptr; + sk_X509_push(cert_stack.get(), openssl_cert_copy); + } + ScopedPKCS7 pkcs7( + PKCS7_sign(nullptr, nullptr, cert_stack.get(), nullptr, PKCS7_DETACHED)); + if (!pkcs7) { + LOG(WARNING) << "Could not convert certificate chain to PKCS7"; + return ""; + } + ScopedBIO bio(BIO_new(BIO_s_mem())); + if (bio.get() == nullptr || !i2d_PKCS7_bio(bio.get(), pkcs7.get())) { + LOG(WARNING) << "Failed writing PKCS7 to bio"; + return ""; + } + int cert_size = BIO_pending(bio.get()); + pkcs7_cert.resize(cert_size); + if (BIO_read(bio.get(), &pkcs7_cert[0], cert_size) != cert_size) { + LOG(WARNING) << "BIO_read failure"; + return ""; + } + return pkcs7_cert; +} + X509Cert* X509CertChain::GetCert(size_t cert_index) const { if (cert_index >= cert_chain_.size()) { return NULL; @@ -326,6 +361,25 @@ util::Status X509CA::VerifyCertChain(const X509CertChain& cert_chain) { return OpenSslX509Verify(leaf_cert->openssl_cert(), intermediates.get()); } +util::Status X509CA::VerifyCertWithChain(const X509Cert& cert, + const X509CertChain& cert_chain) { + ScopedX509StackOnly intermediates(sk_X509_new_null()); + if (!intermediates) { + // MakeStatus is now preferred. But we don't support it in the exported + // version, yet. So, ignore lint here. + // NOLINTNEXTLINE + return util::Status( + util::Status::canonical_space(), util::error::INTERNAL, + "Failed to allocate X.509 intermediate certificate stack"); + } + for (size_t idx = 0; idx < cert_chain.GetNumCerts(); ++idx) { + sk_X509_push(intermediates.get(), + const_cast(cert_chain.GetCert(idx)->openssl_cert())); + } + + return OpenSslX509Verify(cert.openssl_cert(), intermediates.get()); +} + util::Status X509CA::OpenSslX509Verify(const X509* cert, STACK_OF(X509) * intermediates) { DCHECK(cert); diff --git a/common/x509_cert.h b/common/x509_cert.h index db08235..4ef5c13 100644 --- a/common/x509_cert.h +++ b/common/x509_cert.h @@ -107,6 +107,10 @@ class X509CertChain { // container. util::Status LoadPkcs7(const std::string& pk7_cert_chain); + // Writes the |cert_chain_| to a DER-encoded PKCS#7 X.509 cryptographic + // message. The final message does not include signed data. + std::string GetPkcs7(); + // Returns the number of certificates in the chain. size_t GetNumCerts() const { return cert_chain_.size(); } @@ -138,6 +142,12 @@ class X509CA { // used when constructing X509CA. This method is thread-safe. util::Status VerifyCertChain(const X509CertChain& cert_chain); + // Does X.509 PKI validation of |cert| using the |cert_chain| + // certificates. This method allows |cert| to be an ICA. This method is + // thread-safe. + util::Status VerifyCertWithChain(const X509Cert& cert, + const X509CertChain& cert_chain); + private: util::Status InitializeStore(); util::Status OpenSslX509Verify(const X509* cert, STACK_OF(X509) * stack); diff --git a/common/x509_cert_test.cc b/common/x509_cert_test.cc index 0cc8ccb..e60775f 100644 --- a/common/x509_cert_test.cc +++ b/common/x509_cert_test.cc @@ -52,6 +52,32 @@ const char kTestRootCaDerCert[] = "d22de9a13c5092c92c297021c51a2a0a5250cf26c271ff262f25a7738ae4" "c270d87191c13aefdd177b"; +const char kTestRootCaPemCert[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIEAzCCAuugAwIBAgIJAKJPlK965oMfMA0GCSqGSIb3DQEBBQUAMIGXMQswCQYD\n" + "VQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2lya2xhbmQx\n" + "EzARBgNVBAoMCkdvb2dsZSBJbmMxETAPBgNVBAsMCFdpZGV2aW5lMRUwEwYDVQQD\n" + "DAxUZXN0IFJvb3QgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNv\n" + "bTAeFw0xMzA4MTYwMDU3MTBaFw0zMzA4MTUwMDU3MTBaMIGXMQswCQYDVQQGEwJV\n" + "UzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2lya2xhbmQxEzARBgNV\n" + "BAoMCkdvb2dsZSBJbmMxETAPBgNVBAsMCFdpZGV2aW5lMRUwEwYDVQQDDAxUZXN0\n" + "IFJvb3QgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNvbTCCASIw\n" + "DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbu5inZn3c2LbVUXtHW37NhbHQs\n" + "YX1f1I8vv8s/LsQKCAvQTVUc5RlHGou07Fwsdb+KLSyvP4XZDp45OR372q5oBRMZ\n" + "DacbGyrkgpoVxEvBsZsXE0hEuUxvBtkhYzMjZXTz8RsNEMPGIUEOQmMMV86ekBBX\n" + "7aXDwiA+4q2AWg2TUvqR2kWm9IdbRSTBk8Qv2QSKECBOWyyCA0Arp2Dn4bQSbD4q\n" + "tCWPK/KM0xcN6Mc4pqH0z8wGSfqV8UFP2dCd1PURvAqb86WESjNNngpLlSXSeJvm\n" + "q6/i0MwgedzwMP+pvorj/iyrTr36SU1IqoxjJk0x4iCKnCj3PgEDzhZGg78CAwEA\n" + "AaNQME4wHQYDVR0OBBYEFE0w/xgaxPENqZ5qEsAeAqzK34QKMB8GA1UdIwQYMBaA\n" + "FE0w/xgaxPENqZ5qEsAeAqzK34QKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF\n" + "BQADggEBAHeem5jT7AZvKYYpA6AOnJglnZh8BLnmoubDOB7lnsHdDX3uedphLk36\n" + "o0ZciRaZPtet67JzQN4gyhAQZ/g0KyEk7A1dtTEne0ZTw7xysqja6uEg5TSOGjOP\n" + "bmjnEpQ2Am54Ak8E12axMiUuwVJALc7CgXQ0aqC6mX1/GvFA/wJb7IQfgDm6ENfM\n" + "CYzyRVT4y7KqMYdSBcZ98vBTDYeE+vY8T5ReYto3TK1hVeauRPWXvP9FZuoqrEJY\n" + "5K6BVpwO3dHfaSlTK0U4vSBLL/WEfLRqxzg8lv6C0i3poTxQksksKXAhxRoqClJQ\n" + "zybCcf8mLyWnc4rkwnDYcZHBOu/dF3s=\n" + "-----END CERTIFICATE-----\n"; + const char kTestPemCert[] = "-----BEGIN CERTIFICATE-----\n" "MIIDwzCCAqsCAQIwDQYJKoZIhvcNAQEFBQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYD\n" @@ -130,6 +156,32 @@ const char kTestPemCertChain[] = "6kIkGZCFP/ws7ctk+fQyjjttncIdL2k=\n" "-----END CERTIFICATE-----\n"; +const char kTestPemIca[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIEAzCCAuugAwIBAgIBATANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCVVMx\n" + "EzARBgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQK\n" + "DApHb29nbGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEVMBMGA1UEAwwMVGVzdCBS\n" + "b290IENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcNMTMw\n" + "ODE2MjE0MTQ2WhcNMzMwODE1MjE0MTQ2WjCBnzELMAkGA1UEBhMCVVMxEzARBgNV\n" + "BAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQKDApHb29n\n" + "bGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEdMBsGA1UEAwwUVGVzdCBJbnRlcm1l\n" + "ZGlhdGUgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNvbTCCASIw\n" + "DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANooBi6x3I9Incs6ytlPjBu7yEy5\n" + "f6BLf5NREE5nQm74Rt7PAA7YVDtxHP+pi1uyxsL3fUrx904s4tdXNRK85/2zn7+o\n" + "oZPYb8fH6dgl7ocmYeyC0jSmg7++ZiaS6OsjPSUTE2aEbAe6Q+ZhYsAbdkL7Z2dN\n" + "UJR9akhLEqlqfX4q5bWA0M3P/2/fqNYMS0w010Nwpd+KydbceT0rHQTmTGVsqCCL\n" + "gmaP9a8aQRMSP0dn5IOcc/K1Qnnfw1gxnjGF4aBP7KbCMxNBrbgBOwiTxgEMIcKZ\n" + "9IGszAcpftKX5ra3XePzFWCcnwilppaaE/2XWXkcAehc8d3xtkdAYZyVIBUCAwEA\n" + "AaNQME4wHQYDVR0OBBYEFDm35gzM6ll13HhZUbW5uDw7BieTMB8GA1UdIwQYMBaA\n" + "FE0w/xgaxPENqZ5qEsAeAqzK34QKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF\n" + "BQADggEBALj+/Z8ygfWVNncV0N9UsAcwlGUe5ME+VoXUF/0SOmdrc8LtPc2Dkc8b\n" + "xiQN1wHxE/OFsbsOdobPzwOBh67KyYyVWtxzzsLO0MHGxsbOmwa1AersoP4x8xoC\n" + "HaBU90cviYqz5k6rZyBIlFIrM5lqG1JB3U0kTceG/1sqwRAAu94BYqMW1iWyr9Mq\n" + "ASRCVBOrksWda4pZkCLp62vk7ItOcs2PrHf6UWbANTDH+8Q+pIw2wuJ5lf/imqKO\n" + "qrYCJmAi6VBa2jyHqXVPMk6lL1Rmdk4UgOsRvsbmKzb2vYeWIwhsXY5Spo3WVTLv\n" + "6kIkGZCFP/ws7ctk+fQyjjttncIdL2k=\n" + "-----END CERTIFICATE-----\n"; + const char kTestPk7CertChain[] = "308207fb06092a864886f70d010702a08207ec308207e80201013100300b" "06092a864886f70d010701a08207ce308203c3308202ab020102300d0609" @@ -293,6 +345,7 @@ const char kTestDevCodeSigningCert[] = "5MXS+h+FxQ6QUar2q1zHc/0Gr1hLzA6HYBmI0/AF8LsHs799XjrMKHkSBN6UQkC1\n" "hRk=\n" "-----END CERTIFICATE-----\n"; + const char kDevCertFlagOid[] = "1.3.6.1.4.1.11129.4.1.2"; const bool kTestDevCodeSigningCertFlagValue = true; @@ -388,6 +441,62 @@ TEST(X509CertTest, ChainVerificationPkcs7) { EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain)); } +TEST(X509CertTest, VerifyCertWithChainIca) { + std::unique_ptr ca_cert(new X509Cert); + ASSERT_EQ(util::OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert)); + X509CA ca(ca_cert.release()); + + // Verify the ICA with the root succeeds. + X509CertChain test_chain; + ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestRootCaPemCert)); + ASSERT_EQ(1, test_chain.GetNumCerts()); + X509Cert ica_cert; + ASSERT_EQ(util::OkStatus(), ica_cert.LoadPem(kTestPemIca)); + EXPECT_EQ(util::OkStatus(), ca.VerifyCertWithChain(ica_cert, test_chain)); +} + +TEST(X509CertTest, VerifyCertWithChainLeaf) { + std::unique_ptr ca_cert(new X509Cert); + ASSERT_EQ(util::OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert)); + X509CA ca(ca_cert.release()); + + // Verify the leaf with the root and ICA succeeds. + X509CertChain test_chain; + ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemIca)); + ASSERT_EQ(1, test_chain.GetNumCerts()); + X509Cert leaf_cert; + ASSERT_EQ(util::OkStatus(), leaf_cert.LoadPem(kTestPemCert)); + EXPECT_EQ(util::OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain)); +} + +TEST(X509CertTest, VerifyCertWithChainLeafMissincIca) { + std::unique_ptr ca_cert(new X509Cert); + ASSERT_EQ(util::OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert)); + X509CA ca(ca_cert.release()); + + // Verify the leaf with only the root fails (ICA missing). + X509CertChain test_chain; + ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestRootCaPemCert)); + ASSERT_EQ(1, test_chain.GetNumCerts()); + X509Cert leaf_cert; + ASSERT_EQ(util::OkStatus(), leaf_cert.LoadPem(kTestPemCert)); + EXPECT_NE(util::OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain)); +} + +TEST(X509CertTest, GetPkcs7) { + X509CertChain test_chain; + ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain)); + std::string pkcs7_certificate = test_chain.GetPkcs7(); + ASSERT_NE(pkcs7_certificate.size(), 0); + X509CertChain new_test_chain; + ASSERT_EQ(util::OkStatus(), new_test_chain.LoadPkcs7(pkcs7_certificate)); + ASSERT_EQ(test_chain.GetNumCerts(), new_test_chain.GetNumCerts()); + for (int i = 0; i < test_chain.GetNumCerts(); i++) { + ASSERT_EQ(test_chain.GetCert(i)->GetPem(), + new_test_chain.GetCert(i)->GetPem()); + } +} + TEST(X509CertTest, BooleanExtension) { std::unique_ptr cert1(new X509Cert); ASSERT_EQ(util::OkStatus(), cert1->LoadPem(kTestPemCert)); diff --git a/license_server_sdk/internal/BUILD b/license_server_sdk/internal/BUILD index bbcd9f9..e923a1a 100644 --- a/license_server_sdk/internal/BUILD +++ b/license_server_sdk/internal/BUILD @@ -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", diff --git a/license_server_sdk/internal/client_cert.cc b/license_server_sdk/internal/client_cert.cc deleted file mode 100644 index 5737c57..0000000 --- a/license_server_sdk/internal/client_cert.cc +++ /dev/null @@ -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 -#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 "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 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 rsa_root_public_key_ - GUARDED_BY(&rsa_root_public_key_mutex_); - absl::Mutex validated_signer_cache_mutex_; - std::map validated_signer_cache_ - GUARDED_BY(&validated_signer_cache_mutex_); -}; - -void ValidatedSignerCache::SetRootPublicKey( - std::unique_ptr 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> -// 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 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 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& 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); -} - -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 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 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 diff --git a/license_server_sdk/internal/session_impl.cc b/license_server_sdk/internal/session_impl.cc index abc222d..5027a89 100644 --- a/license_server_sdk/internal/session_impl.cc +++ b/license_server_sdk/internal/session_impl.cc @@ -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 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 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 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); } diff --git a/license_server_sdk/internal/session_impl.h b/license_server_sdk/internal/session_impl.h index d25e69b..55fc2c5 100644 --- a/license_server_sdk/internal/session_impl.h +++ b/license_server_sdk/internal/session_impl.h @@ -17,7 +17,6 @@ #include #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(); // 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& keys); static void SetPreProvisioningKeys(const std::multimap& 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 .. . 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, diff --git a/license_server_sdk/internal/session_impl_test.cc b/license_server_sdk/internal/session_impl_test.cc index 05f5c8d..d561e32 100644 --- a/license_server_sdk/internal/session_impl_test.cc +++ b/license_server_sdk/internal/session_impl_test.cc @@ -25,6 +25,7 @@ #include "absl/strings/escaping.h" #include "absl/strings/str_cat.h" #include "common/aes_cbc_util.h" +#include "common/client_cert.h" #include "common/crypto_util.h" #include "common/drm_root_certificate.h" #include "common/error_space.h" @@ -33,9 +34,8 @@ #include "common/rsa_test_keys.h" #include "common/rsa_util.h" #include "common/signing_key_util.h" -#include "common/test_certificates.h" +#include "common/test_drm_certificates.h" #include "common/test_utils.h" -#include "license_server_sdk/internal/client_cert.h" #include "license_server_sdk/internal/key_control_block.h" #include "license_server_sdk/internal/session_usage_report.h" #include "protos/public/client_identification.pb.h" @@ -53,6 +53,7 @@ using google::protobuf::TextFormat; using testing::Eq; using testing::Return; +// TODO(user): Add test case for generateSignedLicense. namespace widevine { namespace { const char* kToken = @@ -114,7 +115,7 @@ const bool kDontUseDeprecatedKcbNonce(false); const uint32_t kMinOEMVersionForEntitlementKey = 14; // TODO(user): Replace the RA test certs here with RA certs from -// //common/X509TestCertificates (added in CL/144397600). +// //video/widevine/common/X509TestCertificates (added in CL/144397600). // Remote attestation cert chain for a device in verified mode. const char kVerifiedRaCertChain[] = "-----BEGIN CERTIFICATE-----\n" @@ -418,979 +419,19 @@ REGISTER_MODULE_INITIALIZER(KeyboxProvisioningKey, { SystemIdsToKeys keys; keys[kSystemId] = kPreProvisioningKey; SessionImpl::SetPreProvisioningKeys(keys); - RsaTestKeys test_keys; - CHECK_OK(CertificateClientCert::SetDrmRootCertificatePublicKey( - test_keys.public_test_key_1_3072_bits())); }); -namespace { -RsaTestKeys test_keys_; - -void SetupCertificateStatusList( - bool allow_unknown_devices, - SignedDeviceCertificateStatusList* status_list) { - ASSERT_TRUE(status_list); - - DeviceCertificateStatusList cert_status_list; - DeviceCertificateStatus* cert_status; - RsaTestKeys test_keys; - - // Device cert with status RELEASED. - cert_status = cert_status_list.add_certificate_status(); - cert_status->mutable_device_info()->set_system_id(kValidSystemId); - cert_status->set_drm_serial_number(kValidSerialNumber); - 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(kRevokedSystemId); - cert_status->set_drm_serial_number(kRevokedSerialNumber); - cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED); - - // Device cert with status Allowed to be REVOKED. - cert_status = cert_status_list.add_certificate_status(); - cert_status->mutable_device_info()->set_system_id(kAllowedRevokedSystemId); - cert_status->set_drm_serial_number(kAllowedRevokedSerialNumber); - cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED); - - cert_status_list.set_creation_time_seconds(kStatusListCreationTime); - cert_status_list.SerializeToString( - status_list->mutable_certificate_status_list()); - std::unique_ptr root_key( - RsaPrivateKey::Create(test_keys.private_test_key_1_3072_bits())); - ASSERT_TRUE(root_key.get()); - ASSERT_TRUE( - root_key->GenerateSignature(status_list->certificate_status_list(), - status_list->mutable_signature())); -} - -void SetCertificateStatusList(bool allow_unknown_devices) { - TestCertificates test_certs; - SignedDeviceCertificateStatusList status_list; - SetupCertificateStatusList(allow_unknown_devices, &status_list); - std::string serialized_status_list; - ASSERT_TRUE(status_list.SerializeToString(&serialized_status_list)); - uint32_t expiration_period_seconds = 0; - EXPECT_EQ(util::OkStatus(), - SessionImpl::SetCertificateStatusList( - kCertificateTypeTesting, serialized_status_list, - expiration_period_seconds, allow_unknown_devices)); -} - -// TODO(user): Add test utilizing provisioner certificates. - -SignedDrmCertificate* GenerateProvisionerCertificate( - const std::string& serial_number, uint32_t system_id) { - DrmCertificate provisioner_certificate; - provisioner_certificate.set_type(DrmCertificate::PROVISIONER); - provisioner_certificate.set_serial_number(serial_number); - // TODO(user) Use different test keys from the intermediate certificate. - provisioner_certificate.set_public_key( - test_keys_.public_test_key_2_2048_bits()); - provisioner_certificate.set_system_id(system_id); - provisioner_certificate.set_creation_time_seconds(kStatusListCreationTime - - 1); - provisioner_certificate.set_provider_id(kProvisionerProviderId); - std::unique_ptr signed_device_certificate( - new SignedDrmCertificate); - signed_device_certificate->set_drm_certificate( - provisioner_certificate.SerializeAsString()); - std::unique_ptr rsa_private_key( - RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())); - rsa_private_key->GenerateSignature( - signed_device_certificate->drm_certificate(), - signed_device_certificate->mutable_signature()); - return signed_device_certificate.release(); -} - -SignedDrmCertificate* GenerateIntermediateCertificate( - const std::string& serial_number, uint32_t system_id, - bool use_provisioner_provider) { - DrmCertificate intermediate_certificate; - 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(kStatusListCreationTime - - 1); - std::unique_ptr signed_drm_certificate( - new SignedDrmCertificate); - if (use_provisioner_provider) { - signed_drm_certificate->mutable_signer()->Swap( - GenerateProvisionerCertificate(serial_number, system_id)); - } - signed_drm_certificate->set_drm_certificate( - intermediate_certificate.SerializeAsString()); - std::unique_ptr rsa_private_key( - RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())); - rsa_private_key->GenerateSignature( - signed_drm_certificate->drm_certificate(), - signed_drm_certificate->mutable_signature()); - return signed_drm_certificate.release(); -} - -std::string GenerateSignedDrmCertificate(uint32_t system_id, - const std::string& serial_number, - bool use_provisioner_provider) { - DrmCertificate drm_certificate; - 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()); - - SignedDrmCertificate signed_drm_certificate; - signed_drm_certificate.set_allocated_signer(GenerateIntermediateCertificate( - serial_number, system_id, use_provisioner_provider)); - signed_drm_certificate.set_drm_certificate( - drm_certificate.SerializeAsString()); - std::unique_ptr rsa_private_key( - RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits())); - rsa_private_key->GenerateSignature( - signed_drm_certificate.drm_certificate(), - signed_drm_certificate.mutable_signature()); - return signed_drm_certificate.SerializeAsString(); -} - -std::string GenerateBasicLicenseRequest( - ProtocolVersion protocol_version, bool deprecated_nonce, - LicenseType license_type, uint32_t request_time, - const ClientIdentification::ClientCapabilities& client_capabilities, - bool client_token_in_request, - const LicenseRequest::ContentIdentification::InitData::InitDataType - init_data_type) { - LicenseRequest request; - request.mutable_client_id()->set_type(ClientIdentification::KEYBOX); - request.mutable_client_id()->set_token(absl::HexStringToBytes(kToken)); - *request.mutable_client_id()->mutable_client_capabilities() = - client_capabilities; - if (init_data_type == LicenseRequest::ContentIdentification::InitData::WEBM) { - LicenseRequest::ContentIdentification::WebmDeprecated* webm_id = - request.mutable_content_id()->mutable_webm_id_deprecated(); - webm_id->set_header("0123456789ABCDEF"); - webm_id->set_license_type(license_type); - webm_id->set_request_id("myCoolRequest, Dude!"); - } else { - LicenseRequest::ContentIdentification::CencDeprecated* cenc_id = - request.mutable_content_id()->mutable_cenc_id_deprecated(); - widevine::WidevinePsshData wv_pssh; - wv_pssh.set_protection_scheme(kProtectionScheme); - std::string pssh_string; - wv_pssh.SerializeToString(&pssh_string); - cenc_id->add_pssh(pssh_string); - cenc_id->set_license_type(license_type); - cenc_id->set_request_id("myCool-CENC-Request, Dude!"); - } - if (client_token_in_request) { - request.mutable_client_id()->set_provider_client_token( - kExistingProviderClientToken); - } - request.set_type(LicenseRequest::NEW); - if (request_time == kUseCurrentTime) { - request.set_request_time(time(nullptr)); - } else { - request.set_request_time(request_time); - } - request.set_protocol_version(protocol_version); - if (deprecated_nonce) { - request.set_key_control_nonce_deprecated(absl::StrCat(kNonce)); - } else { - // Deprecated nonce should be ignored. - request.set_key_control_nonce_deprecated(kBadNonce); - request.set_key_control_nonce(kNonce); - } - - SignedMessage signed_request_message; - CHECK(request.SerializeToString(signed_request_message.mutable_msg())); - - std::string signing_key = SessionImpl::DeriveKey( - absl::HexStringToBytes(kKey), SessionImpl::kSigningKeyLabel, - signed_request_message.msg(), - SigningKeyMaterialSize(request.protocol_version())); - signed_request_message.set_signature(crypto_util::CreateSignatureHmacSha256( - GetClientSigningKey(signing_key, request.protocol_version()), - signed_request_message.msg())); - - return signed_request_message.SerializeAsString(); -} - -std::string GenerateStreamingRequestDrmCert(ProtocolVersion protocol_version, - uint32_t system_id, - const std::string& serial_number, - LicenseRequest::RequestType request_type, - bool set_key_control_nonce) { - LicenseRequest request; - request.mutable_client_id()->set_type( - ClientIdentification::DRM_DEVICE_CERTIFICATE); - request.mutable_client_id()->set_token( - GenerateSignedDrmCertificate(system_id, serial_number, false)); - - request.mutable_content_id()->mutable_webm_id_deprecated()->set_header( - "0123456789ABCDEF"); - request.mutable_content_id()->mutable_webm_id_deprecated()->set_license_type( - STREAMING); - request.mutable_content_id()->mutable_webm_id_deprecated()->set_request_id( - "myCoolRequest, Dude!"); - if (request_type != LicenseRequest::NEW) { - request.mutable_content_id() - ->mutable_existing_license() - ->mutable_license_id() - ->set_session_id("111"); - } - request.set_type(request_type); - request.set_request_time(time(nullptr)); - request.set_protocol_version(protocol_version); - if (set_key_control_nonce) { - request.set_key_control_nonce(kNonce); - } - - SignedMessage signed_request_message; - CHECK(request.SerializeToString(signed_request_message.mutable_msg())); - - std::unique_ptr rsa_private_key( - RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits())); - rsa_private_key->GenerateSignature( - signed_request_message.msg(), signed_request_message.mutable_signature()); - - return signed_request_message.SerializeAsString(); -} - -std::string GenerateLicenseRequestWithVmp(bool tampered) { - LicenseRequest request; - - std::string vmp_data(absl::HexStringToBytes(kGoodVmpData)); - if (tampered) { - vmp_data[32]++; - } - request.mutable_client_id()->set_vmp_data(vmp_data); - - request.mutable_client_id()->set_type( - ClientIdentification::DRM_DEVICE_CERTIFICATE); - request.mutable_client_id()->set_token( - GenerateSignedDrmCertificate(kValidSystemId, kValidSerialNumber, false)); - - request.mutable_content_id()->mutable_webm_id_deprecated()->set_header( - "0123456789ABCDEF"); - request.mutable_content_id()->mutable_webm_id_deprecated()->set_license_type( - STREAMING); - request.mutable_content_id()->mutable_webm_id_deprecated()->set_request_id( - "myCoolRequest, Dude!"); - request.set_type(LicenseRequest::NEW); - request.set_request_time(time(nullptr)); - request.set_protocol_version(VERSION_2_1); - - SignedMessage signed_request_message; - CHECK(request.SerializeToString(signed_request_message.mutable_msg())); - - std::unique_ptr rsa_private_key( - RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits())); - rsa_private_key->GenerateSignature( - signed_request_message.msg(), signed_request_message.mutable_signature()); - - return signed_request_message.SerializeAsString(); -} - -void ValidateBasicRequestResponse( - const std::string& request_msg, - const ClientIdentification::ClientCapabilities& client_capabilities, - const LicenseRequest::ContentIdentification::InitData::InitDataType - init_data_type, - MockCDPush* cdpush) { - // Parse LicenseRequest from request_msg for comparison - // the one returned from SessionImpl::Create(). - SignedMessage signed_request; - ASSERT_TRUE(signed_request.ParseFromString(request_msg)); - LicenseRequest expected_request_proto; - ASSERT_TRUE(expected_request_proto.ParseFromString(signed_request.msg())); - - LicenseRequest actual_request_proto; - SessionImpl* session_ptr = nullptr; - util::Status status = - SessionImpl::Create(request_msg, &session_ptr, &actual_request_proto); - std::unique_ptr session(session_ptr); - ASSERT_EQ(util::OkStatus(), status); - ASSERT_TRUE(session.get()); - ASSERT_EQ(expected_request_proto.DebugString(), - actual_request_proto.DebugString()); - - if (session->request().client_id().type() == - ClientIdentification::DRM_DEVICE_CERTIFICATE) { - EXPECT_EQ(kValidSerialNumber, session->GetDrmDeviceId()); - } - if (actual_request_proto.has_key_control_nonce()) { - EXPECT_TRUE(session->HasKeyControlNonce()); - } else { - EXPECT_FALSE(session->HasKeyControlNonce()); - } - - License::Policy policies; - policies.set_can_play(true); - policies.set_can_renew(true); - - std::list keys; - // Setup Content Keys. - License::KeyContainer key; - const char* kKeyId1 = "key id-1"; - key.set_id(kKeyId1); - key.set_key("0123456789abcdef"); - key.set_type(License::KeyContainer::CONTENT); - key.set_track_label("SD"); - keys.push_front(key); - const char* kKeyId2 = "key id-2"; - key.set_id(kKeyId2); - key.set_key("abcdef9876543210"); - key.set_type(License::KeyContainer::CONTENT); - key.set_track_label("AUDIO"); - keys.push_front(key); - const char* kKeyId3 = "key id-3"; - key.set_id(kKeyId3); - key.set_key("9876543210fedcba"); - key.set_type(License::KeyContainer::CONTENT); - key.set_track_label("HD"); - // Current SRM requested for kKeyId3 - key.clear_required_protection(); - keys.push_front(key); - - const char* kEntitlementKeyId1 = "entitlement key id-1"; - const char* kEntitlementKeyId2 = "entitlement key id-2"; - const char* kEntitlementKeyId3 = "entitlement key id-3"; - int expected_num_entitlement_keys = 0; - if (client_capabilities.oem_crypto_api_version() >= - kMinOEMVersionForEntitlementKey) { - // Setup Entitlement Keys (introduced in OEMCrypto 14). - key.clear_required_protection(); - key.set_id(kEntitlementKeyId1); - key.set_key("deadbeefdeadbeef0011223344abcdef"); - key.set_type(License::KeyContainer::ENTITLEMENT); - key.set_track_label("SD"); - keys.push_front(key); - key.set_id(kEntitlementKeyId2); - key.set_key("deadbeefdeadbeefabcdef9988776655"); - key.set_type(License::KeyContainer::ENTITLEMENT); - key.set_track_label("AUDIO"); - keys.push_front(key); - key.set_id(kEntitlementKeyId3); - key.set_key("deadbeefdeadbeefabcdef4433221155"); - key.set_type(License::KeyContainer::ENTITLEMENT); - key.set_track_label("HD"); - keys.push_front(key); - expected_num_entitlement_keys = 3; - } - - SessionInit init; - init.set_purchase_id("Purchases!"); - init.set_provider_session_token(kProviderSessionToken); - init.set_provider_client_token(kProviderClientToken); - SessionState cache; - std::string signed_license_bytes; - // Test signing key size is enforced. Signing key size is too big. - init.set_master_signing_key("01234567890123450123456789012345"); - status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, - &signed_license_bytes); - EXPECT_EQ(INVALID_MASTER_SIGNING_KEY_SIZE, status.error_code()); - // Test signing key size is enforced. Signing key size is too small. - init.set_master_signing_key("012345"); - status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, - &signed_license_bytes); - EXPECT_EQ(INVALID_MASTER_SIGNING_KEY_SIZE, status.error_code()); - // Test signing key size is enforced. Signing key size is valid. - init.set_master_signing_key("0123456789012345"); - status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, - &signed_license_bytes); - ASSERT_EQ(util::OkStatus(), status); - - SignedMessage signed_message; - CHECK(signed_message.ParseFromString(signed_license_bytes)); - ASSERT_EQ(signed_message.signature().size(), 32); // Yep, SHA-256 was used! - - License license; - ASSERT_TRUE(license.ParseFromString(signed_message.msg())); - - // Strip out the signing key, since it's random, but also assert we see it - // (and there's only one). - bool signing_key_seen = false; - int num_content_keys_found = 0; - int num_entitlement_keys_found = 0; - for (int i = 0; i < license.key_size(); ++i) { - if (license.key(i).type() == License::KeyContainer::CONTENT) { - if (license.key(i).id() == kKeyId1 || license.key(i).id() == kKeyId2 || - license.key(i).id() == kKeyId3) { - num_content_keys_found++; - } - ASSERT_EQ(kEncryptionKeySizeBytes, license.key(i).key().size()) - << " for content key: " << license.key(i).id(); - } else if (license.key(i).type() == License::KeyContainer::SIGNING) { - // Check signing key size. Should be 256 bits for version 2.0 and - // 512 bits for version 2.1. - if (session->request().protocol_version() >= VERSION_2_1) { - ASSERT_EQ(std::string(license.key(i).key()).size(), - kProtocolVersion2_1KeySizeWithPadding); - } else { - ASSERT_EQ(std::string(license.key(i).key()).size(), - kProtocolVersion2_0KeySizeWithPadding); - } - ASSERT_FALSE(signing_key_seen); - signing_key_seen = true; - license.mutable_key()->DeleteSubrange(i--, 1); - } else if (license.key(i).type() == License::KeyContainer::ENTITLEMENT) { - if (license.key(i).id() == kEntitlementKeyId1 || - license.key(i).id() == kEntitlementKeyId2 || - license.key(i).id() == kEntitlementKeyId3) { - num_entitlement_keys_found++; - ASSERT_EQ(kEntitlementKeySizeBytes, license.key(i).key().size()) - << " for entitlement key: " << license.key(i).id(); - } - } else { - license.mutable_key(i)->clear_iv(); - license.mutable_key(i)->clear_key(); - } - } - ASSERT_TRUE(signing_key_seen); - ASSERT_EQ(3, num_content_keys_found); - ASSERT_EQ(expected_num_entitlement_keys, num_entitlement_keys_found); - - ASSERT_TRUE(license.has_license_start_time()); - license.clear_license_start_time(); - - ASSERT_TRUE(license.has_id()); - ASSERT_TRUE(license.id().has_session_id()); - license.mutable_id()->clear_session_id(); - - if (init_data_type == LicenseRequest::ContentIdentification::InitData::WEBM) { - EXPECT_EQ("myCoolRequest, Dude!", license.id().request_id()); - EXPECT_FALSE(license.has_protection_scheme()); - } else { - EXPECT_EQ("myCool-CENC-Request, Dude!", license.id().request_id()); - EXPECT_EQ(kProtectionScheme, license.protection_scheme()); - } - EXPECT_EQ("Purchases!", license.id().purchase_id()); - EXPECT_EQ(STREAMING, license.id().type()); - EXPECT_EQ(0, license.id().version()); - EXPECT_EQ(true, license.policy().can_play()); - EXPECT_EQ(true, license.policy().can_renew()); - EXPECT_FALSE(license.remote_attestation_verified()); - EXPECT_EQ(PLATFORM_UNVERIFIED, license.platform_verification_status()); - - EXPECT_EQ(client_capabilities.client_token(), - license.has_provider_client_token()); - ASSERT_EQ(client_capabilities.session_token(), - license.id().has_provider_session_token()); - if (client_capabilities.session_token()) { - EXPECT_EQ(kProviderSessionToken, license.id().provider_session_token()); - } - if (session->request().client_id().has_provider_client_token()) { - EXPECT_EQ(session->request().client_id().provider_client_token(), - cache.provider_client_token()); - } else if (license.has_provider_client_token()) { - EXPECT_EQ(license.provider_client_token(), cache.provider_client_token()); - } - -} - -void ValidateBasicRequestResponse( - const std::string& request_msg, - const ClientIdentification::ClientCapabilities& client_capabilities, - const LicenseRequest::ContentIdentification::InitData::InitDataType - init_data_type) { - ValidateBasicRequestResponse(request_msg, client_capabilities, init_data_type, - /* cdpush= */ nullptr); -} - -enum SessionUsageTestMode { - kNoSessionUsage, - kInvalidSessionUsage, - kInvalidSessionUsageSignature, - kValidSessionUsage, -}; - -void LicenseRenewalTest(const std::string& request_msg_bytes, - bool use_valid_key_expect_success, bool release_license, - SessionUsageTestMode session_usage_mode) { - License::Policy policies; - policies.set_can_play(true); - policies.set_can_renew(true); - policies.set_playback_duration_seconds(86000); - policies.set_license_duration_seconds(240); - policies.set_renewal_delay_seconds(180); - policies.set_renewal_retry_interval_seconds(30); - policies.set_renewal_recovery_duration_seconds(600); - policies.set_renewal_server_url("http://myrenewalserver.com"); - - License::KeyContainer key; - key.set_id("key id"); - key.set_key("0123456789abcdef"); - key.set_type(License::KeyContainer::CONTENT); - std::list keys; - keys.push_front(key); - - SessionImpl* session_ptr = nullptr; - SessionImpl::AllowDevelopmentClients(true); - SessionImpl::AllowRevokedDevices(kAllowedRevokedSystemIdValue); - util::Status status = SessionImpl::Create(request_msg_bytes, &session_ptr); - ASSERT_EQ(util::OkStatus(), status); - std::unique_ptr session(session_ptr); - ASSERT_TRUE(session.get()); - - SessionInit init; - init.set_purchase_id("Buy ALL the things!"); - - if (session->request().protocol_version() == VERSION_2_0) { - init.set_signing_key("01234567890123450123456789012345"); - } else { - init.set_signing_key( - "01234567890123450123456789012345" - "01234567890123450123456789012345"); - } - init.set_provider_client_token("client token"); - - SessionState session_state; - std::string license_bytes; - status = session->GenerateSignedLicense(&policies, &keys, &init, - &session_state, &license_bytes); - ASSERT_EQ(util::OkStatus(), status); - ASSERT_FALSE(license_bytes.empty()); - - if (!use_valid_key_expect_success) { - session_state.set_keybox_system_id(kRevokedSystemId); - } - - // Use license from original license request to create a Session instance - // for license renewal request. - SignedMessage signed_message; - CHECK(signed_message.ParseFromString(license_bytes)); - CHECK_EQ(signed_message.signature().size(), 32); - - License license; - CHECK(license.ParseFromString(signed_message.msg())); - license.ParseFromString(signed_message.msg()); - - SignedMessage request_message; - CHECK(request_message.ParseFromString(request_msg_bytes)); - - std::string device_key; - if (session->request().client_id().type() == - ClientIdentification::DRM_DEVICE_CERTIFICATE) { - ASSERT_TRUE(signed_message.has_session_key()); - std::unique_ptr rsa_session_key( - RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits())); - rsa_session_key->Decrypt(signed_message.session_key(), &device_key); - } else { - device_key = absl::HexStringToBytes(kKey); - } - std::string encrypt_key = SessionImpl::DeriveKey( - device_key, SessionImpl::kEncryptionKeyLabel, request_message.msg(), - SessionImpl::kEncryptionKeySizeBits); - std::string signing_key; - for (int i = 0; i < license.key_size(); ++i) { - if (license.key(i).type() == License::KeyContainer::SIGNING) { - signing_key = crypto_util::DecryptAesCbc(encrypt_key, license.key(i).iv(), - license.key(i).key()); - uint32_t signing_key_size_bytes = SessionImpl::kSigningKeySizeBits / 8; - if (signing_key.size() > signing_key_size_bytes) { - // Protocol v2.1 signing key. Retain only server portion. - signing_key.resize(signing_key_size_bytes); - } - break; - } - } - ASSERT_FALSE(signing_key.empty()); - - LicenseRequest renewal; - renewal.mutable_content_id() - ->mutable_existing_license() - ->mutable_license_id() - ->Swap(license.mutable_id()); - if (release_license) { - renewal.set_type(LicenseRequest::RELEASE); - } else { - renewal.set_type(LicenseRequest::RENEWAL); - } - std::string session_usage_data; - switch (session_usage_mode) { - case kInvalidSessionUsage: - session_usage_data = "String is too short."; - break; - case kInvalidSessionUsageSignature: - session_usage_data.assign(21, 'x'); - break; - case kValidSessionUsage: { - std::string signed_data( - reinterpret_cast(&kSampleSessionUsageReport.status), - sizeof(kSampleSessionUsageReport)); - session_usage_data = - crypto_util::CreateSignatureHmacSha1(signing_key, signed_data); - session_usage_data += signed_data; - break; - } - default: - break; - } - if (!session_usage_data.empty()) { - renewal.mutable_content_id() - ->mutable_existing_license() - ->set_session_usage_table_entry(session_usage_data); - } - - SignedMessage signed_request_message; - CHECK(renewal.SerializeToString(signed_request_message.mutable_msg())); - signed_request_message.set_signature(crypto_util::CreateSignatureHmacSha256( - signing_key, signed_request_message.msg())); - session_ptr = nullptr; - status = SessionImpl::Create(signed_request_message.SerializeAsString(), - &session_ptr); - ASSERT_EQ(util::OkStatus(), status); - std::unique_ptr renewal_session(session_ptr); - - if (use_valid_key_expect_success && release_license) { - License::Policy policies; - policies.set_can_play(true); - status = renewal_session->GenerateSignedLicense( - &policies, nullptr, &init, &session_state, &license_bytes); - EXPECT_EQ(INVALID_RELEASE_CAN_PLAY_VALUE, status.error_code()); - } - std::list renewal_keys; - status = renewal_session->GenerateSignedLicense( - nullptr, &renewal_keys, &init, &session_state, &license_bytes); - SessionUsage expected_session_usage; - SessionUsage session_usage; - switch (session_usage_mode) { - case kInvalidSessionUsage: - EXPECT_EQ(INVALID_SESSION_USAGE_TABLE_ENTRY, status.error_code()); - return; - case kInvalidSessionUsageSignature: - EXPECT_EQ(INVALID_SESSION_USAGE_SIGNATURE, status.error_code()); - return; - case kValidSessionUsage: - EXPECT_TRUE(renewal_session->GetSessionUsage(&session_usage)); - ASSERT_TRUE(TextFormat::ParseFromString(kSessionUsageText, - &expected_session_usage)); - EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals( - expected_session_usage, session_usage)); - EXPECT_EQ(expected_session_usage.ShortDebugString(), - session_usage.ShortDebugString()); - break; - default: - break; - } - if (!use_valid_key_expect_success) { - EXPECT_EQ(UNSUPPORTED_SYSTEM_ID, status.error_code()); - // Renewal with invalid, non-existent system ID. - ASSERT_TRUE(license_bytes.empty()); - return; - } - - ASSERT_EQ(util::OkStatus(), status); - ASSERT_FALSE(license_bytes.empty()); - - SignedMessage signed_renew_message; - CHECK(signed_renew_message.ParseFromString(license_bytes)); - - License renew_license; - ASSERT_TRUE(renew_license.ParseFromString(signed_renew_message.msg())); - - ASSERT_TRUE(renew_license.has_license_start_time()); - renew_license.clear_license_start_time(); - - ASSERT_TRUE(renew_license.id().has_session_id()); - renew_license.mutable_id()->clear_session_id(); - - // Renewals should never contain a client token. - ASSERT_FALSE(renew_license.has_provider_client_token()); - - // Renewal response messages should never contain a session key. - ASSERT_FALSE(signed_renew_message.has_session_key()); - - std::string renew_license_text; - TextFormat::PrintToString(renew_license, &renew_license_text); - EXPECT_EQ( - "id {\n" - " request_id: \"myCoolRequest, Dude!\"\n" - " purchase_id: \"Buy ALL the things!\"\n" - " type: STREAMING\n" - " version: 1\n" - "}\n" - "platform_verification_status: PLATFORM_UNVERIFIED\n", - renew_license_text); - - // Verify renewal will fail if content keys are specified. - status = renewal_session->GenerateSignedLicense( - nullptr, &keys, &init, &session_state, &license_bytes); - EXPECT_EQ(RENEWAL_WITH_CONTENT_KEYS_NOT_ALLOWED, status.error_code()); -} - -void LicenseTestFailed(const std::string& request_msg_bytes, bool release_license, - SessionUsageTestMode session_usage_mode) { - License::Policy policies; - policies.set_can_play(true); - policies.set_can_renew(true); - policies.set_playback_duration_seconds(86000); - policies.set_license_duration_seconds(240); - policies.set_renewal_delay_seconds(180); - policies.set_renewal_retry_interval_seconds(30); - policies.set_renewal_recovery_duration_seconds(600); - policies.set_renewal_server_url("http://myrenewalserver.com"); - - License::KeyContainer key; - key.set_id("key id"); - key.set_key("0123456789abcdef"); - key.set_type(License::KeyContainer::CONTENT); - std::list keys; - keys.push_front(key); - - SessionImpl* session_ptr = nullptr; - SessionImpl::AllowDevelopmentClients(true); - util::Status status = SessionImpl::Create(request_msg_bytes, &session_ptr); - ASSERT_EQ(DRM_DEVICE_CERTIFICATE_REVOKED, status.error_code()); - std::unique_ptr session(session_ptr); - ASSERT_FALSE(session); -} -void SigningCheckTest(std::string* signed_license_request) { - SessionImpl* session_ptr = nullptr; - - util::Status status = - SessionImpl::Create(*signed_license_request, &session_ptr); - ASSERT_EQ(util::OkStatus(), status); - std::unique_ptr session(session_ptr); - ASSERT_TRUE(session.get()); - ASSERT_EQ(LicenseRequest::NEW, session->request().type()); - - // Test that invalid signed messages fail to validate. - for (size_t i = 0; i < signed_license_request->size(); ++i) { - SCOPED_TRACE("byte: " + absl::StrCat(i)); - // Test that perturbing any byte in the request invalidates it. - char tmp = (*signed_license_request)[i] ? '\0' : '\255'; - std::swap((*signed_license_request)[i], tmp); - session_ptr = nullptr; - status = SessionImpl::Create(*signed_license_request, &session_ptr); - ASSERT_NE(util::OkStatus(), status); - ASSERT_FALSE(session_ptr); - std::swap((*signed_license_request)[i], tmp); - // Test that perturbing any single bit in the request invalidates it. - for (int bit = 0; bit < 8; ++bit) { - SCOPED_TRACE("bit: " + absl::StrCat(bit)); - signed_license_request->at(i) ^= 1 << bit; - session_ptr = nullptr; - status = SessionImpl::Create(*signed_license_request, &session_ptr); - if (session_ptr && session_ptr->request().type() != LicenseRequest::NEW) { - // SessionImpl::Create() will not verify the signature for a license - // unless the type is NEW. Perturbing the bits may change the license - // type. - delete session_ptr; - } else { - ASSERT_NE(util::OkStatus(), status); - ASSERT_FALSE(session_ptr); - } - (*signed_license_request)[i] ^= 1 << bit; - } - } - // Post-facto check we remembered to reset everything. - status = SessionImpl::Create(*signed_license_request, &session_ptr); - session.reset(session_ptr); - ASSERT_EQ(util::OkStatus(), status); - ASSERT_TRUE(session.get()); -} - -void TestKeyControlBlocks(const std::string& request_msg) { - SessionImpl* session_ptr = nullptr; - util::Status status = SessionImpl::Create(request_msg, &session_ptr); - std::unique_ptr session(session_ptr); - ASSERT_EQ(util::OkStatus(), status); - ASSERT_TRUE(session.get()); - License::KeyContainer key; - key.set_id("content"); - const std::string content_key("0123456789abcdef"); - key.set_key(content_key); - key.set_type(License::KeyContainer::CONTENT); - // If client supports hardware_anti_rollback, make it required for this key. - if (session->request() - .client_id() - .client_capabilities() - .anti_rollback_usage_table()) { - key.set_anti_rollback_usage_table(true); - } - std::list keys; - keys.push_back(key); - key.set_id("operator"); - const std::string operator_key("fedcba9876543210"); - key.set_key(operator_key); - key.set_type(License::KeyContainer::OPERATOR_SESSION); - key.clear_track_label(); - keys.push_back(key); - - SessionInit init; - init.set_purchase_id("Purchases!"); - License::Policy policies; - policies.set_can_persist(true); - SessionState cache; - std::string signed_license_bytes; - status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, - &signed_license_bytes); - ASSERT_EQ(util::OkStatus(), status); - - SignedMessage signed_message; - CHECK(signed_message.ParseFromString(signed_license_bytes)); - License license; - ASSERT_TRUE(license.ParseFromString(signed_message.msg())); - ASSERT_EQ(2, license.key_size()); - const std::string key_list[2] = {content_key, operator_key}; - int num_content_keys = 1; - int num_operator_session_keys = 1; - for (int idx = 0; idx < num_content_keys + num_operator_session_keys; ++idx) { - const License::KeyContainer& key = license.key(idx); - ASSERT_TRUE(key.has_key_control()); - const License::KeyContainer::KeyControl& kc = key.key_control(); - ASSERT_TRUE(kc.has_key_control_block()); - ASSERT_TRUE(kc.has_iv()); - std::string kcb(crypto_util::DecryptAesCbc(key_list[idx], kc.iv(), - kc.key_control_block())); - ASSERT_EQ(16, kcb.size()); - EXPECT_EQ('k', kcb[0]); - EXPECT_EQ('c', kcb[1]); - EXPECT_EQ('t', kcb[2]); - EXPECT_EQ('l', kcb[3]); - if (key.type() == License::KeyContainer::CONTENT) { - EXPECT_EQ(kNonce, strings::KeyToUint32(kcb.substr(8, 4))); - } - // Verify control bits. - uint32_t kcb_bits = strings::KeyToUint32(kcb.substr(12, 4)); - if (cache.license_id().type() == STREAMING) { - EXPECT_EQ(key_control_block::kKeyControlFlagsNonceEnable, - kcb_bits & key_control_block::kKeyControlFlagsNonceEnable); - } else { - EXPECT_EQ(0, kcb_bits & key_control_block::kKeyControlFlagsNonceEnable); - } - if (key.type() == License::KeyContainer::CONTENT && - session->request() - .client_id() - .client_capabilities() - .anti_rollback_usage_table()) { - EXPECT_EQ( - key_control_block::kKeyControlFlagsAntiRollbackUsageTableRequired, - kcb_bits & key_control_block:: - kKeyControlFlagsAntiRollbackUsageTableRequired); - } else { - EXPECT_EQ(0, kcb_bits & - key_control_block:: - kKeyControlFlagsAntiRollbackUsageTableRequired); - } - } -} - -void TestOfflineLicense(const std::string& request_msg) { - SessionImpl* session_ptr = nullptr; - util::Status status = SessionImpl::Create(request_msg, &session_ptr); - std::unique_ptr session(session_ptr); - ASSERT_EQ(util::OkStatus(), status); - ASSERT_TRUE(session.get()); - License::KeyContainer key; - key.set_id("content"); - const std::string content_key("0123456789abcdef"); - key.set_key(content_key); - key.set_type(License::KeyContainer::CONTENT); - std::list keys; - keys.push_back(key); - key.set_id("operator"); - const std::string operator_key("fedcba9876543210"); - key.set_key(operator_key); - key.set_type(License::KeyContainer::OPERATOR_SESSION); - keys.push_back(key); - SessionInit init; - init.set_purchase_id("Purchases!"); - License::Policy policies; - SessionState cache; - std::string signed_license_bytes; - status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, - &signed_license_bytes); - EXPECT_EQ(INVALID_OFFLINE_CAN_PERSIST, status.error_code()); - policies.set_can_persist(true); - status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, - &signed_license_bytes); - EXPECT_TRUE(status.ok()); -} - -std::string GenerateDrmServiceCertificate(const std::string& serial_number, - const std::string& provider_id, - uint32_t creation_time_seconds) { - DrmCertificate cert; - cert.set_type(DrmCertificate::SERVICE); - cert.set_serial_number(serial_number); - cert.set_provider_id(provider_id); - cert.set_public_key(test_keys_.public_test_key_2_2048_bits()); - cert.set_creation_time_seconds(creation_time_seconds); - SignedDrmCertificate signed_cert; - cert.SerializeToString(signed_cert.mutable_drm_certificate()); - std::unique_ptr root_key( - RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())); - if (root_key == NULL) { - return std::string(); - } - root_key->GenerateSignature(signed_cert.drm_certificate(), - signed_cert.mutable_signature()); - std::string serialized_cert; - signed_cert.SerializeToString(&serialized_cert); - return serialized_cert; -} - -void AddDrmServiceCertificate(const std::string& serial_number, - const std::string& provider_id, - uint32_t creation_time_seconds) { - std::string passphrase("this is a passphrase"); - std::string signed_cert(GenerateDrmServiceCertificate(serial_number, provider_id, - creation_time_seconds)); - std::string encrypted_private_key; - ASSERT_TRUE(rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( - test_keys_.private_test_key_2_2048_bits(), passphrase, - &encrypted_private_key)); - ASSERT_EQ(util::OkStatus(), SessionImpl::AddDrmServiceCertificate( - kCertificateTypeTesting, signed_cert, - encrypted_private_key, passphrase)); -} - -void EncryptClientIdentification( - const ClientIdentification& client_id, const std::string& serial_number, - const std::string& provider_id, - EncryptedClientIdentification* encrypted_client_id) { - CHECK(encrypted_client_id); - - std::string privacy_key("aabbccddeeffgghh"); - std::string iv("0011223344556677"); - encrypted_client_id->set_provider_id(provider_id); - encrypted_client_id->set_service_certificate_serial_number(serial_number); - std::string serial_client_id; - client_id.SerializeToString(&serial_client_id); - encrypted_client_id->set_encrypted_client_id( - crypto_util::EncryptAesCbc(privacy_key, iv, serial_client_id)); - encrypted_client_id->set_encrypted_client_id_iv(iv); - std::unique_ptr rsa_key( - RsaPublicKey::Create(test_keys_.public_test_key_2_2048_bits())); - ASSERT_TRUE(rsa_key.get()); - rsa_key->Encrypt(privacy_key, - encrypted_client_id->mutable_encrypted_privacy_key()); -} - -void AddRemoteAttestation(SignedMessage* signed_message, const std::string& ra_cert, - const std::string& ra_cert_private_key, - const std::string& drm_service_cert_serial_number) { - ClientIdentification client_id; - client_id.set_type(ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE); - client_id.set_token(ra_cert); - RemoteAttestation* ra = signed_message->mutable_remote_attestation(); - EncryptClientIdentification(client_id, drm_service_cert_serial_number, - kRaServiceId, ra->mutable_certificate()); - std::string salt("Ye are the salt of the earth"); - std::string message_signed = signed_message->msg() + salt; - ASSERT_EQ(util::OkStatus(), - GenerateRsaSignatureSha256Pkcs1(ra_cert_private_key, message_signed, - ra->mutable_signature())); - ra->set_salt(salt); -} - -} // anonymous namespace - class SessionTest : public ::testing::Test { public: SessionTest() {} - void SetUp() override {} + void SetUp() override { + ASSERT_OK(DrmRootCertificate::CreateByType(kCertificateTypeDevelopment, + &dev_root_)); + ASSERT_OK(DrmRootCertificate::CreateByType(kCertificateTypeProduction, + &prod_root_)); + ASSERT_OK( + DrmRootCertificate::CreateByType(kCertificateTypeTesting, &test_root_)); + } std::string TestGetProviderClientToken(const SessionInit& session_init, const ClientIdentification& client_id) { @@ -1418,7 +459,7 @@ class SessionTest : public ::testing::Test { util::Status VerifyPlatform() { return session_impl_.VerifyPlatform(); } void RemoteAttestationSuccess() { - RemoteAttestationVerifier::get().EnableTestCertificates(true); + RemoteAttestationVerifier::get().EnableTestDrmCertificates(true); std::string service_cert_sn("service_cert_sn"); AddDrmServiceCertificate(service_cert_sn, kRaServiceId, 123456); ClientIdentification::ClientCapabilities client_capabilities; @@ -1436,7 +477,8 @@ class SessionTest : public ::testing::Test { std::string updated_request; ASSERT_TRUE(signed_message.SerializeToString(&updated_request)); SessionImpl* session_ptr(nullptr); - EXPECT_OK(SessionImpl::Create(updated_request, &session_ptr)); + ASSERT_OK( + SessionImpl::Create(test_root_.get(), updated_request, &session_ptr)); std::unique_ptr session(session_ptr); EXPECT_EQ(PLATFORM_HARDWARE_VERIFIED, session->GetPlatformVerificationStatus()); @@ -1466,7 +508,989 @@ class SessionTest : public ::testing::Test { license.platform_verification_status()); } + void SetupCertificateStatusList( + bool allow_unknown_devices, + SignedDeviceCertificateStatusList* status_list) { + ASSERT_TRUE(status_list); + + DeviceCertificateStatusList cert_status_list; + DeviceCertificateStatus* cert_status; + RsaTestKeys test_keys; + + // Device cert with status RELEASED. + cert_status = cert_status_list.add_certificate_status(); + cert_status->mutable_device_info()->set_system_id(kValidSystemId); + cert_status->set_drm_serial_number(kValidSerialNumber); + 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(kRevokedSystemId); + cert_status->set_drm_serial_number(kRevokedSerialNumber); + cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED); + + // Device cert with status Allowed to be REVOKED. + cert_status = cert_status_list.add_certificate_status(); + cert_status->mutable_device_info()->set_system_id(kAllowedRevokedSystemId); + cert_status->set_drm_serial_number(kAllowedRevokedSerialNumber); + cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED); + + cert_status_list.set_creation_time_seconds(kStatusListCreationTime); + cert_status_list.SerializeToString( + status_list->mutable_certificate_status_list()); + std::unique_ptr root_key( + RsaPrivateKey::Create(test_keys.private_test_key_1_3072_bits())); + ASSERT_TRUE(root_key.get()); + ASSERT_TRUE( + root_key->GenerateSignature(status_list->certificate_status_list(), + status_list->mutable_signature())); + } + + void SetCertificateStatusList(bool allow_unknown_devices) { + TestDrmCertificates test_certs; + SignedDeviceCertificateStatusList status_list; + SetupCertificateStatusList(allow_unknown_devices, &status_list); + std::string serialized_status_list; + ASSERT_TRUE(status_list.SerializeToString(&serialized_status_list)); + uint32_t expiration_period_seconds = 0; + EXPECT_EQ(util::OkStatus(), + SessionImpl::SetCertificateStatusList( + test_root_.get(), serialized_status_list, + expiration_period_seconds, allow_unknown_devices)); + } + + // TODO(user): Add test utilizing provisioner certificates. + + SignedDrmCertificate* GenerateProvisionerCertificate( + const std::string& serial_number, uint32_t system_id) { + DrmCertificate provisioner_certificate; + provisioner_certificate.set_type(DrmCertificate::PROVISIONER); + provisioner_certificate.set_serial_number(serial_number); + // TODO(user) Use different test keys from the intermediate certificate. + provisioner_certificate.set_public_key( + test_keys_.public_test_key_2_2048_bits()); + provisioner_certificate.set_system_id(system_id); + provisioner_certificate.set_creation_time_seconds(kStatusListCreationTime - + 1); + provisioner_certificate.set_provider_id(kProvisionerProviderId); + std::unique_ptr signed_device_certificate( + new SignedDrmCertificate); + signed_device_certificate->set_drm_certificate( + provisioner_certificate.SerializeAsString()); + std::unique_ptr rsa_private_key( + RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())); + rsa_private_key->GenerateSignature( + signed_device_certificate->drm_certificate(), + signed_device_certificate->mutable_signature()); + return signed_device_certificate.release(); + } + + SignedDrmCertificate* GenerateIntermediateCertificate( + const std::string& serial_number, uint32_t system_id, + bool use_provisioner_provider) { + DrmCertificate intermediate_certificate; + 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(kStatusListCreationTime - + 1); + std::unique_ptr signed_drm_certificate( + new SignedDrmCertificate); + if (use_provisioner_provider) { + signed_drm_certificate->mutable_signer()->Swap( + GenerateProvisionerCertificate(serial_number, system_id)); + } + signed_drm_certificate->set_drm_certificate( + intermediate_certificate.SerializeAsString()); + std::unique_ptr rsa_private_key( + RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())); + rsa_private_key->GenerateSignature( + signed_drm_certificate->drm_certificate(), + signed_drm_certificate->mutable_signature()); + return signed_drm_certificate.release(); + } + + std::string GenerateSignedDrmCertificate(uint32_t system_id, + const std::string& serial_number, + bool use_provisioner_provider) { + DrmCertificate drm_certificate; + 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(kStatusListCreationTime - 1); + + SignedDrmCertificate signed_drm_certificate; + signed_drm_certificate.set_allocated_signer(GenerateIntermediateCertificate( + serial_number, system_id, use_provisioner_provider)); + signed_drm_certificate.set_drm_certificate( + drm_certificate.SerializeAsString()); + std::unique_ptr rsa_private_key( + RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits())); + rsa_private_key->GenerateSignature( + signed_drm_certificate.drm_certificate(), + signed_drm_certificate.mutable_signature()); + return signed_drm_certificate.SerializeAsString(); + } + + std::string GenerateBasicLicenseRequest( + ProtocolVersion protocol_version, bool deprecated_nonce, + LicenseType license_type, uint32_t request_time, + const ClientIdentification::ClientCapabilities& client_capabilities, + bool client_token_in_request, + const LicenseRequest::ContentIdentification::InitData::InitDataType + init_data_type) { + LicenseRequest request; + request.mutable_client_id()->set_type(ClientIdentification::KEYBOX); + request.mutable_client_id()->set_token(absl::HexStringToBytes(kToken)); + *request.mutable_client_id()->mutable_client_capabilities() = + client_capabilities; + if (init_data_type == + LicenseRequest::ContentIdentification::InitData::WEBM) { + LicenseRequest::ContentIdentification::WebmDeprecated* webm_id = + request.mutable_content_id()->mutable_webm_id_deprecated(); + webm_id->set_header("0123456789ABCDEF"); + webm_id->set_license_type(license_type); + webm_id->set_request_id("myCoolRequest, Dude!"); + } else { + LicenseRequest::ContentIdentification::CencDeprecated* cenc_id = + request.mutable_content_id()->mutable_cenc_id_deprecated(); + widevine::WidevinePsshData wv_pssh; + wv_pssh.set_protection_scheme(kProtectionScheme); + std::string pssh_string; + wv_pssh.SerializeToString(&pssh_string); + cenc_id->add_pssh(pssh_string); + cenc_id->set_license_type(license_type); + cenc_id->set_request_id("myCool-CENC-Request, Dude!"); + } + if (client_token_in_request) { + request.mutable_client_id()->set_provider_client_token( + kExistingProviderClientToken); + } + request.set_type(LicenseRequest::NEW); + if (request_time == kUseCurrentTime) { + request.set_request_time(time(nullptr)); + } else { + request.set_request_time(request_time); + } + request.set_protocol_version(protocol_version); + if (deprecated_nonce) { + request.set_key_control_nonce_deprecated(absl::StrCat(kNonce)); + } else { + // Deprecated nonce should be ignored. + request.set_key_control_nonce_deprecated(kBadNonce); + request.set_key_control_nonce(kNonce); + } + + SignedMessage signed_request_message; + CHECK(request.SerializeToString(signed_request_message.mutable_msg())); + + std::string signing_key = SessionImpl::DeriveKey( + absl::HexStringToBytes(kKey), SessionImpl::kSigningKeyLabel, + signed_request_message.msg(), + SigningKeyMaterialSize(request.protocol_version())); + signed_request_message.set_signature(crypto_util::CreateSignatureHmacSha256( + GetClientSigningKey(signing_key, request.protocol_version()), + signed_request_message.msg())); + + return signed_request_message.SerializeAsString(); + } + + std::string GenerateStreamingRequestDrmCert( + ProtocolVersion protocol_version, uint32_t system_id, + const std::string& serial_number, LicenseRequest::RequestType request_type, + bool set_key_control_nonce) { + LicenseRequest request; + request.mutable_client_id()->set_type( + ClientIdentification::DRM_DEVICE_CERTIFICATE); + request.mutable_client_id()->set_token( + GenerateSignedDrmCertificate(system_id, serial_number, false)); + + request.mutable_content_id()->mutable_webm_id_deprecated()->set_header( + "0123456789ABCDEF"); + request.mutable_content_id() + ->mutable_webm_id_deprecated() + ->set_license_type(STREAMING); + request.mutable_content_id()->mutable_webm_id_deprecated()->set_request_id( + "myCoolRequest, Dude!"); + if (request_type != LicenseRequest::NEW) { + request.mutable_content_id() + ->mutable_existing_license() + ->mutable_license_id() + ->set_session_id("111"); + } + request.set_type(request_type); + request.set_request_time(time(nullptr)); + request.set_protocol_version(protocol_version); + if (set_key_control_nonce) { + request.set_key_control_nonce(kNonce); + } + + SignedMessage signed_request_message; + CHECK(request.SerializeToString(signed_request_message.mutable_msg())); + + std::unique_ptr rsa_private_key( + RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits())); + rsa_private_key->GenerateSignature( + signed_request_message.msg(), + signed_request_message.mutable_signature()); + + return signed_request_message.SerializeAsString(); + } + + std::string GenerateLicenseRequestWithVmp(bool tampered) { + LicenseRequest request; + + std::string vmp_data(absl::HexStringToBytes(kGoodVmpData)); + if (tampered) { + vmp_data[32]++; + } + request.mutable_client_id()->set_vmp_data(vmp_data); + + request.mutable_client_id()->set_type( + ClientIdentification::DRM_DEVICE_CERTIFICATE); + request.mutable_client_id()->set_token(GenerateSignedDrmCertificate( + kValidSystemId, kValidSerialNumber, false)); + + request.mutable_content_id()->mutable_webm_id_deprecated()->set_header( + "0123456789ABCDEF"); + request.mutable_content_id() + ->mutable_webm_id_deprecated() + ->set_license_type(STREAMING); + request.mutable_content_id()->mutable_webm_id_deprecated()->set_request_id( + "myCoolRequest, Dude!"); + request.set_type(LicenseRequest::NEW); + request.set_request_time(time(nullptr)); + request.set_protocol_version(VERSION_2_1); + + SignedMessage signed_request_message; + CHECK(request.SerializeToString(signed_request_message.mutable_msg())); + + std::unique_ptr rsa_private_key( + RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits())); + rsa_private_key->GenerateSignature( + signed_request_message.msg(), + signed_request_message.mutable_signature()); + + return signed_request_message.SerializeAsString(); + } + + void ValidateBasicRequestResponse( + const std::string& request_msg, + const ClientIdentification::ClientCapabilities& client_capabilities, + const LicenseRequest::ContentIdentification::InitData::InitDataType + init_data_type, + MockCDPush* cdpush) { + // Parse LicenseRequest from request_msg for comparison + // the one returned from SessionImpl::Create(). + SignedMessage signed_request; + ASSERT_TRUE(signed_request.ParseFromString(request_msg)); + LicenseRequest expected_request_proto; + ASSERT_TRUE(expected_request_proto.ParseFromString(signed_request.msg())); + + LicenseRequest actual_request_proto; + SessionImpl* session_ptr = nullptr; + util::Status status = SessionImpl::Create( + test_root_.get(), request_msg, &session_ptr, &actual_request_proto); + std::unique_ptr session(session_ptr); + ASSERT_EQ(util::OkStatus(), status); + ASSERT_TRUE(session.get()); + ASSERT_EQ(expected_request_proto.DebugString(), + actual_request_proto.DebugString()); + + if (session->request().client_id().type() == + ClientIdentification::DRM_DEVICE_CERTIFICATE) { + EXPECT_EQ(kValidSerialNumber, session->GetDrmDeviceId()); + } + if (actual_request_proto.has_key_control_nonce()) { + EXPECT_TRUE(session->HasKeyControlNonce()); + } else { + EXPECT_FALSE(session->HasKeyControlNonce()); + } + + License::Policy policies; + policies.set_can_play(true); + policies.set_can_renew(true); + + std::list keys; + // Setup Content Keys. + License::KeyContainer key; + const char* kKeyId1 = "key id-1"; + key.set_id(kKeyId1); + key.set_key("0123456789abcdef"); + key.set_type(License::KeyContainer::CONTENT); + key.set_track_label("SD"); + keys.push_front(key); + const char* kKeyId2 = "key id-2"; + key.set_id(kKeyId2); + key.set_key("abcdef9876543210"); + key.set_type(License::KeyContainer::CONTENT); + key.set_track_label("AUDIO"); + keys.push_front(key); + const char* kKeyId3 = "key id-3"; + key.set_id(kKeyId3); + key.set_key("9876543210fedcba"); + key.set_type(License::KeyContainer::CONTENT); + key.set_track_label("HD"); + // Current SRM requested for kKeyId3 + key.clear_required_protection(); + keys.push_front(key); + + const char* kEntitlementKeyId1 = "entitlement key id-1"; + const char* kEntitlementKeyId2 = "entitlement key id-2"; + const char* kEntitlementKeyId3 = "entitlement key id-3"; + int expected_num_entitlement_keys = 0; + if (client_capabilities.oem_crypto_api_version() >= + kMinOEMVersionForEntitlementKey) { + // Setup Entitlement Keys (introduced in OEMCrypto 14). + key.clear_required_protection(); + key.set_id(kEntitlementKeyId1); + key.set_key("deadbeefdeadbeef0011223344abcdef"); + key.set_type(License::KeyContainer::ENTITLEMENT); + key.set_track_label("SD"); + keys.push_front(key); + key.set_id(kEntitlementKeyId2); + key.set_key("deadbeefdeadbeefabcdef9988776655"); + key.set_type(License::KeyContainer::ENTITLEMENT); + key.set_track_label("AUDIO"); + keys.push_front(key); + key.set_id(kEntitlementKeyId3); + key.set_key("deadbeefdeadbeefabcdef4433221155"); + key.set_type(License::KeyContainer::ENTITLEMENT); + key.set_track_label("HD"); + keys.push_front(key); + expected_num_entitlement_keys = 3; + } + + SessionInit init; + init.set_purchase_id("Purchases!"); + init.set_provider_session_token(kProviderSessionToken); + init.set_provider_client_token(kProviderClientToken); + SessionState cache; + std::string signed_license_bytes; + // Test signing key size is enforced. Signing key size is too big. + init.set_master_signing_key("01234567890123450123456789012345"); + status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes); + EXPECT_EQ(INVALID_MASTER_SIGNING_KEY_SIZE, status.error_code()); + // Test signing key size is enforced. Signing key size is too small. + init.set_master_signing_key("012345"); + status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes); + EXPECT_EQ(INVALID_MASTER_SIGNING_KEY_SIZE, status.error_code()); + // Test signing key size is enforced. Signing key size is valid. + init.set_master_signing_key("0123456789012345"); + status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes); + ASSERT_EQ(util::OkStatus(), status); + + SignedMessage signed_message; + CHECK(signed_message.ParseFromString(signed_license_bytes)); + ASSERT_EQ(signed_message.signature().size(), 32); // Yep, SHA-256 was used! + + License license; + ASSERT_TRUE(license.ParseFromString(signed_message.msg())); + + // Strip out the signing key, since it's random, but also assert we see it + // (and there's only one). + bool signing_key_seen = false; + int num_content_keys_found = 0; + int num_entitlement_keys_found = 0; + for (int i = 0; i < license.key_size(); ++i) { + if (license.key(i).type() == License::KeyContainer::CONTENT) { + if (license.key(i).id() == kKeyId1 || license.key(i).id() == kKeyId2 || + license.key(i).id() == kKeyId3) { + num_content_keys_found++; + } + ASSERT_EQ(kEncryptionKeySizeBytes, license.key(i).key().size()) + << " for content key: " << license.key(i).id(); + } else if (license.key(i).type() == License::KeyContainer::SIGNING) { + // Check signing key size. Should be 256 bits for version 2.0 and + // 512 bits for version 2.1. + if (session->request().protocol_version() >= VERSION_2_1) { + ASSERT_EQ(std::string(license.key(i).key()).size(), + kProtocolVersion2_1KeySizeWithPadding); + } else { + ASSERT_EQ(std::string(license.key(i).key()).size(), + kProtocolVersion2_0KeySizeWithPadding); + } + ASSERT_FALSE(signing_key_seen); + signing_key_seen = true; + license.mutable_key()->DeleteSubrange(i--, 1); + } else if (license.key(i).type() == License::KeyContainer::ENTITLEMENT) { + if (license.key(i).id() == kEntitlementKeyId1 || + license.key(i).id() == kEntitlementKeyId2 || + license.key(i).id() == kEntitlementKeyId3) { + num_entitlement_keys_found++; + ASSERT_EQ(kEntitlementKeySizeBytes, license.key(i).key().size()) + << " for entitlement key: " << license.key(i).id(); + } + } else { + license.mutable_key(i)->clear_iv(); + license.mutable_key(i)->clear_key(); + } + } + ASSERT_TRUE(signing_key_seen); + ASSERT_EQ(3, num_content_keys_found); + ASSERT_EQ(expected_num_entitlement_keys, num_entitlement_keys_found); + + ASSERT_TRUE(license.has_license_start_time()); + license.clear_license_start_time(); + + ASSERT_TRUE(license.has_id()); + ASSERT_TRUE(license.id().has_session_id()); + license.mutable_id()->clear_session_id(); + + if (init_data_type == + LicenseRequest::ContentIdentification::InitData::WEBM) { + EXPECT_EQ("myCoolRequest, Dude!", license.id().request_id()); + EXPECT_FALSE(license.has_protection_scheme()); + } else { + EXPECT_EQ("myCool-CENC-Request, Dude!", license.id().request_id()); + EXPECT_EQ(kProtectionScheme, license.protection_scheme()); + } + EXPECT_EQ("Purchases!", license.id().purchase_id()); + EXPECT_EQ(STREAMING, license.id().type()); + EXPECT_EQ(0, license.id().version()); + EXPECT_EQ(true, license.policy().can_play()); + EXPECT_EQ(true, license.policy().can_renew()); + EXPECT_FALSE(license.remote_attestation_verified()); + EXPECT_EQ(PLATFORM_UNVERIFIED, license.platform_verification_status()); + + EXPECT_EQ(client_capabilities.client_token(), + license.has_provider_client_token()); + ASSERT_EQ(client_capabilities.session_token(), + license.id().has_provider_session_token()); + if (client_capabilities.session_token()) { + EXPECT_EQ(kProviderSessionToken, license.id().provider_session_token()); + } + if (session->request().client_id().has_provider_client_token()) { + EXPECT_EQ(session->request().client_id().provider_client_token(), + cache.provider_client_token()); + } else if (license.has_provider_client_token()) { + EXPECT_EQ(license.provider_client_token(), cache.provider_client_token()); + } + + } + + void ValidateBasicRequestResponse( + const std::string& request_msg, + const ClientIdentification::ClientCapabilities& client_capabilities, + const LicenseRequest::ContentIdentification::InitData::InitDataType + init_data_type) { + ValidateBasicRequestResponse(request_msg, client_capabilities, + init_data_type, + /* cdpush= */ nullptr); + } + + enum SessionUsageTestMode { + kNoSessionUsage, + kInvalidSessionUsage, + kInvalidSessionUsageSignature, + kValidSessionUsage, + }; + + void LicenseRenewalTest(const std::string& request_msg_bytes, + bool use_valid_key_expect_success, + bool release_license, + SessionUsageTestMode session_usage_mode) { + License::Policy policies; + policies.set_can_play(true); + policies.set_can_renew(true); + policies.set_playback_duration_seconds(86000); + policies.set_license_duration_seconds(240); + policies.set_renewal_delay_seconds(180); + policies.set_renewal_retry_interval_seconds(30); + policies.set_renewal_recovery_duration_seconds(600); + policies.set_renewal_server_url("http://myrenewalserver.com"); + + License::KeyContainer key; + key.set_id("key id"); + key.set_key("0123456789abcdef"); + key.set_type(License::KeyContainer::CONTENT); + std::list keys; + keys.push_front(key); + + SessionImpl* session_ptr = nullptr; + SessionImpl::AllowDevelopmentClients(true); + SessionImpl::AllowRevokedDevices(kAllowedRevokedSystemIdValue); + util::Status status = + SessionImpl::Create(test_root_.get(), request_msg_bytes, &session_ptr); + ASSERT_EQ(util::OkStatus(), status); + std::unique_ptr session(session_ptr); + ASSERT_TRUE(session.get()); + + SessionInit init; + init.set_purchase_id("Buy ALL the things!"); + + if (session->request().protocol_version() == VERSION_2_0) { + init.set_signing_key("01234567890123450123456789012345"); + } else { + init.set_signing_key( + "01234567890123450123456789012345" + "01234567890123450123456789012345"); + } + init.set_provider_client_token("client token"); + + SessionState session_state; + std::string license_bytes; + status = session->GenerateSignedLicense(&policies, &keys, &init, + &session_state, &license_bytes); + ASSERT_EQ(util::OkStatus(), status); + ASSERT_FALSE(license_bytes.empty()); + + if (!use_valid_key_expect_success) { + session_state.set_keybox_system_id(kRevokedSystemId); + } + + // Use license from original license request to create a Session instance + // for license renewal request. + SignedMessage signed_message; + CHECK(signed_message.ParseFromString(license_bytes)); + CHECK_EQ(signed_message.signature().size(), 32); + + License license; + CHECK(license.ParseFromString(signed_message.msg())); + license.ParseFromString(signed_message.msg()); + + SignedMessage request_message; + CHECK(request_message.ParseFromString(request_msg_bytes)); + + std::string device_key; + if (session->request().client_id().type() == + ClientIdentification::DRM_DEVICE_CERTIFICATE) { + ASSERT_TRUE(signed_message.has_session_key()); + std::unique_ptr rsa_session_key( + RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits())); + rsa_session_key->Decrypt(signed_message.session_key(), &device_key); + } else { + device_key = absl::HexStringToBytes(kKey); + } + std::string encrypt_key = SessionImpl::DeriveKey( + device_key, SessionImpl::kEncryptionKeyLabel, request_message.msg(), + SessionImpl::kEncryptionKeySizeBits); + std::string signing_key; + for (int i = 0; i < license.key_size(); ++i) { + if (license.key(i).type() == License::KeyContainer::SIGNING) { + signing_key = crypto_util::DecryptAesCbc( + encrypt_key, license.key(i).iv(), license.key(i).key()); + uint32_t signing_key_size_bytes = SessionImpl::kSigningKeySizeBits / 8; + if (signing_key.size() > signing_key_size_bytes) { + // Protocol v2.1 signing key. Retain only server portion. + signing_key.resize(signing_key_size_bytes); + } + break; + } + } + ASSERT_FALSE(signing_key.empty()); + + LicenseRequest renewal; + renewal.mutable_content_id() + ->mutable_existing_license() + ->mutable_license_id() + ->Swap(license.mutable_id()); + if (release_license) { + renewal.set_type(LicenseRequest::RELEASE); + } else { + renewal.set_type(LicenseRequest::RENEWAL); + } + std::string session_usage_data; + switch (session_usage_mode) { + case kInvalidSessionUsage: + session_usage_data = "String is too short."; + break; + case kInvalidSessionUsageSignature: + session_usage_data.assign(21, 'x'); + break; + case kValidSessionUsage: { + std::string signed_data( + reinterpret_cast(&kSampleSessionUsageReport.status), + sizeof(kSampleSessionUsageReport)); + session_usage_data = + crypto_util::CreateSignatureHmacSha1(signing_key, signed_data); + session_usage_data += signed_data; + break; + } + default: + break; + } + if (!session_usage_data.empty()) { + renewal.mutable_content_id() + ->mutable_existing_license() + ->set_session_usage_table_entry(session_usage_data); + } + + SignedMessage signed_request_message; + CHECK(renewal.SerializeToString(signed_request_message.mutable_msg())); + signed_request_message.set_signature(crypto_util::CreateSignatureHmacSha256( + signing_key, signed_request_message.msg())); + session_ptr = nullptr; + status = SessionImpl::Create(test_root_.get(), + signed_request_message.SerializeAsString(), + &session_ptr); + ASSERT_EQ(util::OkStatus(), status); + std::unique_ptr renewal_session(session_ptr); + + if (use_valid_key_expect_success && release_license) { + License::Policy policies; + policies.set_can_play(true); + status = renewal_session->GenerateSignedLicense( + &policies, nullptr, &init, &session_state, &license_bytes); + EXPECT_EQ(INVALID_RELEASE_CAN_PLAY_VALUE, status.error_code()); + } + std::list renewal_keys; + status = renewal_session->GenerateSignedLicense( + nullptr, &renewal_keys, &init, &session_state, &license_bytes); + SessionUsage expected_session_usage; + SessionUsage session_usage; + switch (session_usage_mode) { + case kInvalidSessionUsage: + EXPECT_EQ(INVALID_SESSION_USAGE_TABLE_ENTRY, status.error_code()); + return; + case kInvalidSessionUsageSignature: + EXPECT_EQ(INVALID_SESSION_USAGE_SIGNATURE, status.error_code()); + return; + case kValidSessionUsage: + EXPECT_TRUE(renewal_session->GetSessionUsage(&session_usage)); + ASSERT_TRUE(TextFormat::ParseFromString(kSessionUsageText, + &expected_session_usage)); + EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals( + expected_session_usage, session_usage)); + EXPECT_EQ(expected_session_usage.ShortDebugString(), + session_usage.ShortDebugString()); + break; + default: + break; + } + if (!use_valid_key_expect_success) { + EXPECT_EQ(UNSUPPORTED_SYSTEM_ID, status.error_code()); + // Renewal with invalid, non-existent system ID. + ASSERT_TRUE(license_bytes.empty()); + return; + } + + ASSERT_EQ(util::OkStatus(), status); + ASSERT_FALSE(license_bytes.empty()); + + SignedMessage signed_renew_message; + CHECK(signed_renew_message.ParseFromString(license_bytes)); + + License renew_license; + ASSERT_TRUE(renew_license.ParseFromString(signed_renew_message.msg())); + + ASSERT_TRUE(renew_license.has_license_start_time()); + renew_license.clear_license_start_time(); + + ASSERT_TRUE(renew_license.id().has_session_id()); + renew_license.mutable_id()->clear_session_id(); + + // Renewals should never contain a client token. + ASSERT_FALSE(renew_license.has_provider_client_token()); + + // Renewal response messages should never contain a session key. + ASSERT_FALSE(signed_renew_message.has_session_key()); + + std::string renew_license_text; + TextFormat::PrintToString(renew_license, &renew_license_text); + EXPECT_EQ( + "id {\n" + " request_id: \"myCoolRequest, Dude!\"\n" + " purchase_id: \"Buy ALL the things!\"\n" + " type: STREAMING\n" + " version: 1\n" + "}\n" + "platform_verification_status: PLATFORM_UNVERIFIED\n", + renew_license_text); + + // Verify renewal will fail if content keys are specified. + status = renewal_session->GenerateSignedLicense( + nullptr, &keys, &init, &session_state, &license_bytes); + EXPECT_EQ(RENEWAL_WITH_CONTENT_KEYS_NOT_ALLOWED, status.error_code()); + } + + void LicenseTestFailed(const std::string& request_msg_bytes, bool release_license, + SessionUsageTestMode session_usage_mode) { + License::Policy policies; + policies.set_can_play(true); + policies.set_can_renew(true); + policies.set_playback_duration_seconds(86000); + policies.set_license_duration_seconds(240); + policies.set_renewal_delay_seconds(180); + policies.set_renewal_retry_interval_seconds(30); + policies.set_renewal_recovery_duration_seconds(600); + policies.set_renewal_server_url("http://myrenewalserver.com"); + + License::KeyContainer key; + key.set_id("key id"); + key.set_key("0123456789abcdef"); + key.set_type(License::KeyContainer::CONTENT); + std::list keys; + keys.push_front(key); + + SessionImpl* session_ptr = nullptr; + SessionImpl::AllowDevelopmentClients(true); + util::Status status = + SessionImpl::Create(test_root_.get(), request_msg_bytes, &session_ptr); + ASSERT_EQ(DRM_DEVICE_CERTIFICATE_REVOKED, status.error_code()); + std::unique_ptr session(session_ptr); + ASSERT_FALSE(session); + } + void SigningCheckTest(std::string* signed_license_request) { + SessionImpl* session_ptr = nullptr; + + util::Status status = SessionImpl::Create( + test_root_.get(), *signed_license_request, &session_ptr); + ASSERT_EQ(util::OkStatus(), status); + std::unique_ptr session(session_ptr); + ASSERT_TRUE(session.get()); + ASSERT_EQ(LicenseRequest::NEW, session->request().type()); + + // Test that invalid signed messages fail to validate. + for (size_t i = 0; i < signed_license_request->size(); ++i) { + SCOPED_TRACE("byte: " + absl::StrCat(i)); + // Test that perturbing any byte in the request invalidates it. + char tmp = (*signed_license_request)[i] ? '\0' : '\255'; + std::swap((*signed_license_request)[i], tmp); + session_ptr = nullptr; + status = SessionImpl::Create(test_root_.get(), *signed_license_request, + &session_ptr); + ASSERT_NE(util::OkStatus(), status); + ASSERT_FALSE(session_ptr); + std::swap((*signed_license_request)[i], tmp); + // Test that perturbing any single bit in the request invalidates it. + for (int bit = 0; bit < 8; ++bit) { + SCOPED_TRACE("bit: " + absl::StrCat(bit)); + signed_license_request->at(i) ^= 1 << bit; + session_ptr = nullptr; + status = SessionImpl::Create(test_root_.get(), *signed_license_request, + &session_ptr); + if (session_ptr && + session_ptr->request().type() != LicenseRequest::NEW) { + // SessionImpl::Create() will not verify the signature for a license + // unless the type is NEW. Perturbing the bits may change the license + // type. + delete session_ptr; + } else { + ASSERT_NE(util::OkStatus(), status); + ASSERT_FALSE(session_ptr); + } + (*signed_license_request)[i] ^= 1 << bit; + } + } + // Post-facto check we remembered to reset everything. + status = SessionImpl::Create(test_root_.get(), *signed_license_request, + &session_ptr); + session.reset(session_ptr); + ASSERT_EQ(util::OkStatus(), status); + ASSERT_TRUE(session.get()); + } + + void TestKeyControlBlocks(const std::string& request_msg) { + SessionImpl* session_ptr = nullptr; + util::Status status = + SessionImpl::Create(test_root_.get(), request_msg, &session_ptr); + std::unique_ptr session(session_ptr); + ASSERT_EQ(util::OkStatus(), status); + ASSERT_TRUE(session.get()); + License::KeyContainer key; + key.set_id("content"); + const std::string content_key("0123456789abcdef"); + key.set_key(content_key); + key.set_type(License::KeyContainer::CONTENT); + // If client supports hardware_anti_rollback, make it required for this key. + if (session->request() + .client_id() + .client_capabilities() + .anti_rollback_usage_table()) { + key.set_anti_rollback_usage_table(true); + } + std::list keys; + keys.push_back(key); + key.set_id("operator"); + const std::string operator_key("fedcba9876543210"); + key.set_key(operator_key); + key.set_type(License::KeyContainer::OPERATOR_SESSION); + key.clear_track_label(); + keys.push_back(key); + + SessionInit init; + init.set_purchase_id("Purchases!"); + License::Policy policies; + policies.set_can_persist(true); + SessionState cache; + std::string signed_license_bytes; + status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes); + ASSERT_EQ(util::OkStatus(), status); + + SignedMessage signed_message; + CHECK(signed_message.ParseFromString(signed_license_bytes)); + License license; + ASSERT_TRUE(license.ParseFromString(signed_message.msg())); + ASSERT_EQ(2, license.key_size()); + const std::string key_list[2] = {content_key, operator_key}; + int num_content_keys = 1; + int num_operator_session_keys = 1; + for (int idx = 0; idx < num_content_keys + num_operator_session_keys; + ++idx) { + const License::KeyContainer& key = license.key(idx); + ASSERT_TRUE(key.has_key_control()); + const License::KeyContainer::KeyControl& kc = key.key_control(); + ASSERT_TRUE(kc.has_key_control_block()); + ASSERT_TRUE(kc.has_iv()); + std::string kcb(crypto_util::DecryptAesCbc(key_list[idx], kc.iv(), + kc.key_control_block())); + ASSERT_EQ(16, kcb.size()); + EXPECT_EQ('k', kcb[0]); + EXPECT_EQ('c', kcb[1]); + EXPECT_EQ('t', kcb[2]); + EXPECT_EQ('l', kcb[3]); + if (key.type() == License::KeyContainer::CONTENT) { + EXPECT_EQ(kNonce, strings::KeyToUint32(kcb.substr(8, 4))); + } + // Verify control bits. + uint32_t kcb_bits = strings::KeyToUint32(kcb.substr(12, 4)); + if (cache.license_id().type() == STREAMING) { + EXPECT_EQ(key_control_block::kKeyControlFlagsNonceEnable, + kcb_bits & key_control_block::kKeyControlFlagsNonceEnable); + } else { + EXPECT_EQ(0, kcb_bits & key_control_block::kKeyControlFlagsNonceEnable); + } + if (key.type() == License::KeyContainer::CONTENT && + session->request() + .client_id() + .client_capabilities() + .anti_rollback_usage_table()) { + EXPECT_EQ( + key_control_block::kKeyControlFlagsAntiRollbackUsageTableRequired, + kcb_bits & key_control_block:: + kKeyControlFlagsAntiRollbackUsageTableRequired); + } else { + EXPECT_EQ(0, kcb_bits & + key_control_block:: + kKeyControlFlagsAntiRollbackUsageTableRequired); + } + } + } + + void TestOfflineLicense(const std::string& request_msg) { + SessionImpl* session_ptr = nullptr; + util::Status status = + SessionImpl::Create(test_root_.get(), request_msg, &session_ptr); + std::unique_ptr session(session_ptr); + ASSERT_EQ(util::OkStatus(), status); + ASSERT_TRUE(session.get()); + License::KeyContainer key; + key.set_id("content"); + const std::string content_key("0123456789abcdef"); + key.set_key(content_key); + key.set_type(License::KeyContainer::CONTENT); + std::list keys; + keys.push_back(key); + key.set_id("operator"); + const std::string operator_key("fedcba9876543210"); + key.set_key(operator_key); + key.set_type(License::KeyContainer::OPERATOR_SESSION); + keys.push_back(key); + SessionInit init; + init.set_purchase_id("Purchases!"); + License::Policy policies; + SessionState cache; + std::string signed_license_bytes; + status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes); + EXPECT_EQ(INVALID_OFFLINE_CAN_PERSIST, status.error_code()); + policies.set_can_persist(true); + status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes); + EXPECT_TRUE(status.ok()); + } + + std::string GenerateDrmServiceCertificate(const std::string& serial_number, + const std::string& provider_id, + uint32_t creation_time_seconds) { + DrmCertificate cert; + cert.set_type(DrmCertificate::SERVICE); + cert.set_serial_number(serial_number); + cert.set_provider_id(provider_id); + cert.set_public_key(test_keys_.public_test_key_2_2048_bits()); + cert.set_creation_time_seconds(creation_time_seconds); + SignedDrmCertificate signed_cert; + cert.SerializeToString(signed_cert.mutable_drm_certificate()); + std::unique_ptr root_key( + RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())); + if (root_key == nullptr) { + return std::string(); + } + root_key->GenerateSignature(signed_cert.drm_certificate(), + signed_cert.mutable_signature()); + std::string serialized_cert; + signed_cert.SerializeToString(&serialized_cert); + return serialized_cert; + } + + void AddDrmServiceCertificate(const std::string& serial_number, + const std::string& provider_id, + uint32_t creation_time_seconds) { + std::string passphrase("this is a passphrase"); + std::string signed_cert(GenerateDrmServiceCertificate(serial_number, provider_id, + creation_time_seconds)); + std::string encrypted_private_key; + ASSERT_TRUE(rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( + test_keys_.private_test_key_2_2048_bits(), passphrase, + &encrypted_private_key)); + ASSERT_EQ(util::OkStatus(), SessionImpl::AddDrmServiceCertificate( + test_root_.get(), signed_cert, + encrypted_private_key, passphrase)); + } + + void EncryptClientIdentification( + const ClientIdentification& client_id, const std::string& serial_number, + const std::string& provider_id, + EncryptedClientIdentification* encrypted_client_id) { + CHECK(encrypted_client_id); + + std::string privacy_key("aabbccddeeffgghh"); + std::string iv("0011223344556677"); + encrypted_client_id->set_provider_id(provider_id); + encrypted_client_id->set_service_certificate_serial_number(serial_number); + std::string serial_client_id; + client_id.SerializeToString(&serial_client_id); + encrypted_client_id->set_encrypted_client_id( + crypto_util::EncryptAesCbc(privacy_key, iv, serial_client_id)); + encrypted_client_id->set_encrypted_client_id_iv(iv); + std::unique_ptr rsa_key( + RsaPublicKey::Create(test_keys_.public_test_key_2_2048_bits())); + ASSERT_TRUE(rsa_key.get()); + rsa_key->Encrypt(privacy_key, + encrypted_client_id->mutable_encrypted_privacy_key()); + } + + void AddRemoteAttestation(SignedMessage* signed_message, + const std::string& ra_cert, + const std::string& ra_cert_private_key, + const std::string& drm_service_cert_serial_number) { + ClientIdentification client_id; + client_id.set_type(ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE); + client_id.set_token(ra_cert); + RemoteAttestation* ra = signed_message->mutable_remote_attestation(); + EncryptClientIdentification(client_id, drm_service_cert_serial_number, + kRaServiceId, ra->mutable_certificate()); + std::string salt("Ye are the salt of the earth"); + std::string message_signed = signed_message->msg() + salt; + ASSERT_EQ(util::OkStatus(), GenerateRsaSignatureSha256Pkcs1( + ra_cert_private_key, message_signed, + ra->mutable_signature())); + ra->set_salt(salt); + } + protected: + RsaTestKeys test_keys_; + std::unique_ptr dev_root_; + std::unique_ptr prod_root_; + std::unique_ptr test_root_; MockSessionImpl session_impl_; }; @@ -1588,23 +1612,25 @@ TEST_F(SessionTest, BasicRequestDrmCertificateResponseEmptyToken) { request.mutable_content_id()->mutable_webm_id_deprecated()->set_request_id( "myInvalidRequest, Dude!"); request.set_type(LicenseRequest::NEW); - request.set_request_time(time(NULL)); + request.set_request_time(time(nullptr)); SignedMessage signed_request_message; CHECK(request.SerializeToString(signed_request_message.mutable_msg())); util::Status status = SessionImpl::Create( - signed_request_message.SerializeAsString(), &session_ptr); + test_root_.get(), signed_request_message.SerializeAsString(), + &session_ptr); ASSERT_EQ(SERVICE_CERTIFICATE_NOT_FOUND, status.error_code()); ASSERT_FALSE(SessionImpl::is_service_certificate_loaded()); std::string serial_number("service_cert_sn"); std::string provider_id("myservice.com"); AddDrmServiceCertificate(serial_number, provider_id, 123); session_ptr = nullptr; - status = SessionImpl::Create(signed_request_message.SerializeAsString(), + EXPECT_TRUE(SessionImpl::is_service_certificate_loaded()); + status = SessionImpl::Create(test_root_.get(), + signed_request_message.SerializeAsString(), &session_ptr); std::unique_ptr session(session_ptr); - ASSERT_TRUE(SessionImpl::is_service_certificate_loaded()); - ASSERT_EQ(INVALID_DRM_CERTIFICATE, status.error_code()); + EXPECT_EQ(INVALID_DRM_CERTIFICATE, status.error_code()); } TEST_F(SessionTest, BasicRequestCheckRequestTime) { @@ -1619,7 +1645,8 @@ TEST_F(SessionTest, BasicRequestCheckRequestTime) { client_capabilities, client_token_in_request, LicenseRequest::ContentIdentification::InitData::WEBM)); ASSERT_EQ(util::OkStatus(), - SessionImpl::Create(signed_request_message, &session_ptr)); + SessionImpl::Create(test_root_.get(), signed_request_message, + &session_ptr)); std::unique_ptr session(session_ptr); ASSERT_TRUE(session.get()); @@ -1664,7 +1691,8 @@ TEST_F(SessionTest, BasicRequestCheckTokens) { client_capabilities, client_token_in_request, LicenseRequest::ContentIdentification::InitData::WEBM)); ASSERT_EQ(util::OkStatus(), - SessionImpl::Create(signed_request_message, &session_ptr)); + SessionImpl::Create(test_root_.get(), signed_request_message, + &session_ptr)); std::unique_ptr session(session_ptr); ASSERT_TRUE(session.get()); @@ -1794,7 +1822,8 @@ TEST_F(SessionTest, SignatureCheckProtocolVersion2_1) { TEST_F(SessionTest, ResultStatus) { std::string empty_request; SessionImpl* session_ptr = nullptr; - util::Status status = SessionImpl::Create(empty_request, &session_ptr); + util::Status status = + SessionImpl::Create(test_root_.get(), empty_request, &session_ptr); EXPECT_GT(status.ToString().size(), 0); } @@ -1809,21 +1838,13 @@ TEST_F(SessionTest, NewLicenseFailedWithRevokedSystemId) { kLicenseRelease, kNoSessionUsage); } -TEST_F(SessionTest, LicenseRequestWithMissingNonce) { - // This is also the default nonce setting. - std::string request_msg(GenerateStreamingRequestDrmCert( - VERSION_2_0, kValidSystemId, kValidSerialNumber, LicenseRequest::NEW, - false)); - ClientIdentification::ClientCapabilities client_capabilities; - SetCertificateStatusList(kAllowUnknownDevices); - ValidateBasicRequestResponse( - request_msg, client_capabilities, - LicenseRequest::ContentIdentification::InitData::WEBM); -} - -class SessionTestRenewal : public ::testing::TestWithParam { +class SessionTestRenewal : public SessionTest, + public ::testing::WithParamInterface { public: - void SetUp() override { SetCertificateStatusList(kAllowUnknownDevices); } + void SetUp() override { + SessionTest::SetUp(); + SetCertificateStatusList(kAllowUnknownDevices); + } }; TEST_P(SessionTestRenewal, RenewalLicenseFailedWithRevokedSystemId) { @@ -1834,7 +1855,7 @@ TEST_P(SessionTestRenewal, RenewalLicenseFailedWithRevokedSystemId) { LicenseTestFailed( GenerateStreamingRequestDrmCert(VERSION_2_1, kAllowedRevokedSystemId, kAllowedRevokedSerialNumber, - LicenseRequest::RENEWAL, true), + LicenseRequest::RENEWAL, false), kLicenseRelease, kNoSessionUsage); } @@ -1846,7 +1867,7 @@ TEST_P(SessionTestRenewal, ReleaseLicenseFailedWithRevokedSystemId) { LicenseTestFailed( GenerateStreamingRequestDrmCert(VERSION_2_1, kAllowedRevokedSystemId, kAllowedRevokedSerialNumber, - LicenseRequest::RELEASE, true), + LicenseRequest::RELEASE, false), kLicenseRelease, kNoSessionUsage); } @@ -2005,6 +2026,7 @@ TEST_F(SessionTest, InvalidRenewalKeySigningKey_2_0) { client_capabilities.set_anti_rollback_usage_table(false); ASSERT_EQ(util::OkStatus(), SessionImpl::Create( + test_root_.get(), GenerateBasicLicenseRequest( VERSION_2_0, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, client_capabilities, kIncludeProviderToken, @@ -2045,6 +2067,7 @@ TEST_F(SessionTest, InvalidRenewalKeySigningKey_2_1) { client_capabilities.set_anti_rollback_usage_table(false); ASSERT_EQ(util::OkStatus(), SessionImpl::Create( + test_root_.get(), GenerateBasicLicenseRequest( VERSION_2_1, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, client_capabilities, kIncludeProviderToken, @@ -2085,6 +2108,7 @@ TEST_F(SessionTest, ValidRenewalLongSigningKey) { client_capabilities.set_anti_rollback_usage_table(false); ASSERT_EQ(util::OkStatus(), SessionImpl::Create( + test_root_.get(), GenerateBasicLicenseRequest( VERSION_2_1, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, client_capabilities, kIncludeProviderToken, @@ -2184,7 +2208,8 @@ TEST_F(SessionTest, DrmCertificateNotSupported) { // Cannot create a session if the certificate system id // does not exist in the device status list and allow_unknown_device // is false. - util::Status status = SessionImpl::Create(request_msg, &session_ptr); + util::Status status = + SessionImpl::Create(test_root_.get(), request_msg, &session_ptr); std::unique_ptr session(session_ptr); ASSERT_EQ(DRM_DEVICE_CERTIFICATE_UNKNOWN, status.error_code()); } @@ -2197,7 +2222,8 @@ TEST_F(SessionTest, DrmCertificateIntermediateStatusChangedSerialNumber) { true)); SessionImpl* session_ptr = nullptr; SetCertificateStatusList(kAllowUnknownDevices); - util::Status status = SessionImpl::Create(request_msg, &session_ptr); + util::Status status = + SessionImpl::Create(test_root_.get(), request_msg, &session_ptr); std::unique_ptr session(session_ptr); ASSERT_EQ(INVALID_DRM_CERTIFICATE, status.error_code()); } @@ -2208,7 +2234,8 @@ TEST_F(SessionTest, DrmCertificateRevoked) { true)); SessionImpl* session_ptr = nullptr; SetCertificateStatusList(kAllowUnknownDevices); - util::Status status = SessionImpl::Create(request_msg, &session_ptr); + util::Status status = + SessionImpl::Create(test_root_.get(), request_msg, &session_ptr); std::unique_ptr session(session_ptr); ASSERT_EQ(DRM_DEVICE_CERTIFICATE_REVOKED, status.error_code()); } @@ -2218,7 +2245,8 @@ TEST_F(SessionTest, DrmCertificateReportingSerialNumberWithRenewal) { VERSION_2_0, kValidSystemId, kValidSerialNumber, LicenseRequest::NEW, true)); SessionImpl* session_ptr = nullptr; - util::Status status = SessionImpl::Create(request_msg, &session_ptr); + util::Status status = + SessionImpl::Create(test_root_.get(), request_msg, &session_ptr); ASSERT_EQ(util::OkStatus(), status); std::unique_ptr session(session_ptr); ASSERT_TRUE(session.get()); @@ -2226,7 +2254,7 @@ TEST_F(SessionTest, DrmCertificateReportingSerialNumberWithRenewal) { TEST_F(SessionTest, SetCertificateStatusListFail) { SignedDeviceCertificateStatusList status_list; - TestCertificates test_certs; + TestDrmCertificates test_certs; RsaTestKeys test_keys; SetupCertificateStatusList(false, &status_list); @@ -2236,33 +2264,33 @@ TEST_F(SessionTest, SetCertificateStatusListFail) { const uint32_t expiration_seconds = 0; EXPECT_EQ(util::OkStatus(), SessionImpl::SetCertificateStatusList( - kCertificateTypeTesting, serialized_status_list, - expiration_seconds, kDontAllowUnknownDevices)); + test_root_.get(), serialized_status_list, expiration_seconds, + kDontAllowUnknownDevices)); // Use different certificate for validation (other than kCertificateTypeTest). EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST, SessionImpl::SetCertificateStatusList( - kCertificateTypeDevelopment, serialized_status_list, - expiration_seconds, kDontAllowUnknownDevices) + dev_root_.get(), serialized_status_list, expiration_seconds, + kDontAllowUnknownDevices) .error_code()); EXPECT_EQ( "Errors::INVALID_CERTIFICATE_STATUS_LIST: invalid-status-list-signature", SessionImpl::SetCertificateStatusList( - kCertificateTypeDevelopment, serialized_status_list, - expiration_seconds, kDontAllowUnknownDevices) + dev_root_.get(), serialized_status_list, expiration_seconds, + kDontAllowUnknownDevices) .ToString()); // Invalid signature. ++(*status_list.mutable_signature())[20]; ASSERT_TRUE(status_list.SerializeToString(&serialized_status_list)); EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST, SessionImpl::SetCertificateStatusList( - kCertificateTypeDevelopment, serialized_status_list, - expiration_seconds, kDontAllowUnknownDevices) + dev_root_.get(), serialized_status_list, expiration_seconds, + kDontAllowUnknownDevices) .error_code()); } TEST_F(SessionTest, SetCertificateStatusList) { DeviceCertificateStatusList cert_status_list; - TestCertificates test_certs; + TestDrmCertificates test_certs; RsaTestKeys test_keys; SignedDeviceCertificateStatusList status_list; @@ -2273,34 +2301,33 @@ TEST_F(SessionTest, SetCertificateStatusList) { SetCertificateStatusList(kAllowUnknownDevices); EXPECT_EQ(util::OkStatus(), SessionImpl::SetCertificateStatusList( - kCertificateTypeTesting, serialized_status_list, - expiration_seconds, kDontAllowUnknownDevices)); + test_root_.get(), serialized_status_list, expiration_seconds, + kDontAllowUnknownDevices)); // Use different certificate for validation (other than kCertificateTypeTest). EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST, SessionImpl::SetCertificateStatusList( - kCertificateTypeDevelopment, serialized_status_list, - expiration_seconds, kAllowUnknownDevices) + dev_root_.get(), serialized_status_list, expiration_seconds, + kAllowUnknownDevices) .error_code()); EXPECT_EQ( "Errors::INVALID_CERTIFICATE_STATUS_LIST: invalid-status-list-signature", SessionImpl::SetCertificateStatusList( - kCertificateTypeDevelopment, serialized_status_list, - expiration_seconds, kAllowUnknownDevices) + dev_root_.get(), serialized_status_list, expiration_seconds, + kAllowUnknownDevices) .ToString()); // Invalid signature. ++(*status_list.mutable_signature())[20]; ASSERT_TRUE(status_list.SerializeToString(&serialized_status_list)); EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST, SessionImpl::SetCertificateStatusList( - kCertificateTypeTesting, serialized_status_list, - expiration_seconds, kAllowUnknownDevices) + test_root_.get(), serialized_status_list, expiration_seconds, + kAllowUnknownDevices) .error_code()); // Empty status list. - EXPECT_EQ( - INVALID_CERTIFICATE_STATUS_LIST, - SessionImpl::SetCertificateStatusList( - kCertificateTypeTesting, "", expiration_seconds, kAllowUnknownDevices) - .error_code()); + EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST, + SessionImpl::SetCertificateStatusList( + test_root_.get(), "", expiration_seconds, kAllowUnknownDevices) + .error_code()); // Known status list from using the API: // https://www.googleapis.com/certificateprovisioning/v1/ @@ -2324,7 +2351,7 @@ TEST_F(SessionTest, SetCertificateStatusList) { "617423d57b2d1f1f9cb6ddfe056bdc74ea9830142cae45f3b6ba7d59ffe28e3e"; std::string signed_list = absl::HexStringToBytes(hex_signed_list); EXPECT_EQ(util::OkStatus(), SessionImpl::SetCertificateStatusList( - kCertificateTypeProduction, signed_list, + prod_root_.get(), signed_list, expiration_seconds, kAllowUnknownDevices)); } @@ -2405,16 +2432,19 @@ TEST_F(SessionTest, EncryptedClientIdentification) { ASSERT_TRUE(signed_request_message.SerializeToString(&request_msg_bytes)); SessionImpl* session_ptr(nullptr); // License request cannot contain both client_id and encrypted_client_id; - EXPECT_EQ(MULTIPLE_CLIENT_ID, - SessionImpl::Create(request_msg_bytes, &session_ptr).error_code()); + EXPECT_EQ( + MULTIPLE_CLIENT_ID, + SessionImpl::Create(test_root_.get(), request_msg_bytes, &session_ptr) + .error_code()); request.clear_client_id(); ASSERT_TRUE(request.SerializeToString(signed_request_message.mutable_msg())); rsa_private_key->GenerateSignature( signed_request_message.msg(), signed_request_message.mutable_signature()); ASSERT_TRUE(signed_request_message.SerializeToString(&request_msg_bytes)); AddDrmServiceCertificate(serial_number, provider_id, 123); - EXPECT_EQ(util::OkStatus(), - SessionImpl::Create(request_msg_bytes, &session_ptr)); + ASSERT_EQ( + util::OkStatus(), + SessionImpl::Create(test_root_.get(), request_msg_bytes, &session_ptr)); std::unique_ptr session(session_ptr); EXPECT_EQ(license_counter, session->request().client_id().license_counter()); @@ -2425,13 +2455,14 @@ TEST_F(SessionTest, EncryptedClientIdentification) { PlatformVerificationStatus platform_verification_status = PLATFORM_NO_VERIFICATION; EXPECT_EQ(util::OkStatus(), - SessionImpl::CreateForProxy( - request_msg_bytes, platform_verification_status, &client_id, - &session_ptr_with_client_id, &parsed_request)); + SessionImpl::CreateForProxy(test_root_.get(), request_msg_bytes, + platform_verification_status, + &client_id, &session_ptr_with_client_id, + &parsed_request)); std::unique_ptr session_with_client_id( session_ptr_with_client_id); // Verify the license is using the client_id specified when creating a session - // using CreateForProxy() instead of what is in the request. + // using CreateForProxy(test_root_.get(), ) instead of what is in the request. EXPECT_EQ(updated_license_counter, session_with_client_id->request().client_id().license_counter()); // SessionImpl::VerifyPlatform() was called because platform verification @@ -2451,7 +2482,8 @@ TEST_F(SessionTest, DrmServiceCertificateRequest) { std::string request_msg_bytes; ASSERT_TRUE(signed_request_message.SerializeToString(&request_msg_bytes)); SessionImpl* session_ptr(nullptr); - util::Status status = SessionImpl::Create(request_msg_bytes, &session_ptr); + util::Status status = + SessionImpl::Create(test_root_.get(), request_msg_bytes, &session_ptr); ASSERT_EQ(SERVICE_CERTIFICATE_REQUEST_MESSAGE, status.error_code()); std::string error_license; ASSERT_TRUE(SessionImpl::GenerateErrorResponse(status, &error_license)); @@ -2473,14 +2505,16 @@ TEST_F(SessionTest, InvalidMessageType) { std::string request_msg_bytes; ASSERT_TRUE(signed_request_message.SerializeToString(&request_msg_bytes)); SessionImpl* session_ptr(nullptr); - EXPECT_EQ(INVALID_MESSAGE_TYPE, - SessionImpl::Create(request_msg_bytes, &session_ptr).error_code()); + EXPECT_EQ( + INVALID_MESSAGE_TYPE, + SessionImpl::Create(test_root_.get(), request_msg_bytes, &session_ptr) + .error_code()); } TEST_F(SessionTest, RemoteAttestationSuccess) { RemoteAttestationSuccess(); } TEST_F(SessionTest, RemoteAttestationFailureDeveloperMode) { - RemoteAttestationVerifier::get().EnableTestCertificates(true); + RemoteAttestationVerifier::get().EnableTestDrmCertificates(true); std::string service_cert_sn("service_cert_sn"); AddDrmServiceCertificate(service_cert_sn, kRaServiceId, 123456); ClientIdentification::ClientCapabilities client_capabilities; @@ -2498,13 +2532,14 @@ TEST_F(SessionTest, RemoteAttestationFailureDeveloperMode) { std::string updated_request; ASSERT_TRUE(signed_message.SerializeToString(&updated_request)); SessionImpl* session_ptr(nullptr); - ASSERT_OK(SessionImpl::Create(updated_request, &session_ptr)); + ASSERT_OK( + SessionImpl::Create(test_root_.get(), updated_request, &session_ptr)); std::unique_ptr session(session_ptr); EXPECT_EQ(PLATFORM_TAMPERED, session_ptr->GetPlatformVerificationStatus()); } TEST_F(SessionTest, RemoteAttestationFailureCertChainValidation) { - RemoteAttestationVerifier::get().EnableTestCertificates(true); + RemoteAttestationVerifier::get().EnableTestDrmCertificates(true); std::string service_cert_sn("service_cert_sn"); AddDrmServiceCertificate(service_cert_sn, kRaServiceId, 123456); ClientIdentification::ClientCapabilities client_capabilities; @@ -2527,13 +2562,14 @@ TEST_F(SessionTest, RemoteAttestationFailureCertChainValidation) { std::string updated_request; ASSERT_TRUE(signed_message.SerializeToString(&updated_request)); SessionImpl* session_ptr(nullptr); - ASSERT_OK(SessionImpl::Create(updated_request, &session_ptr)); + ASSERT_OK( + SessionImpl::Create(test_root_.get(), updated_request, &session_ptr)); std::unique_ptr session(session_ptr); EXPECT_EQ(PLATFORM_TAMPERED, session_ptr->GetPlatformVerificationStatus()); } TEST_F(SessionTest, RemoteAttestationFailureSignature) { - RemoteAttestationVerifier::get().EnableTestCertificates(true); + RemoteAttestationVerifier::get().EnableTestDrmCertificates(true); std::string service_cert_sn("service_cert_sn"); AddDrmServiceCertificate(service_cert_sn, kRaServiceId, 123456); ClientIdentification::ClientCapabilities client_capabilities; @@ -2553,7 +2589,8 @@ TEST_F(SessionTest, RemoteAttestationFailureSignature) { std::string updated_request; ASSERT_TRUE(good_signed_message.SerializeToString(&updated_request)); SessionImpl* session_ptr(nullptr); - EXPECT_OK(SessionImpl::Create(updated_request, &session_ptr)); + EXPECT_OK( + SessionImpl::Create(test_root_.get(), updated_request, &session_ptr)); std::unique_ptr session(session_ptr); // Invalid signature. @@ -2563,7 +2600,8 @@ TEST_F(SessionTest, RemoteAttestationFailureSignature) { updated_request.clear(); ASSERT_TRUE(bad_signed_message.SerializeToString(&updated_request)); session_ptr = nullptr; - ASSERT_OK(SessionImpl::Create(updated_request, &session_ptr)); + ASSERT_OK( + SessionImpl::Create(test_root_.get(), updated_request, &session_ptr)); session.reset(session_ptr); EXPECT_EQ(PLATFORM_TAMPERED, session_ptr->GetPlatformVerificationStatus()); @@ -2573,7 +2611,8 @@ TEST_F(SessionTest, RemoteAttestationFailureSignature) { updated_request.clear(); ASSERT_TRUE(bad_signed_message.SerializeToString(&updated_request)); session_ptr = nullptr; - ASSERT_OK(SessionImpl::Create(updated_request, &session_ptr)); + ASSERT_OK( + SessionImpl::Create(test_root_.get(), updated_request, &session_ptr)); session.reset(session_ptr); EXPECT_EQ(PLATFORM_TAMPERED, session_ptr->GetPlatformVerificationStatus()); } @@ -2752,7 +2791,7 @@ TEST_P(SessionVmpTest, VmpVerifiedAndTampered) { SessionImpl* session_ptr(nullptr); if (kPlatformVerificationStatus != PLATFORM_TAMPERED) { ASSERT_EQ(util::OkStatus(), - SessionImpl::Create(request_msg, &session_ptr)); + SessionImpl::Create(test_root_.get(), request_msg, &session_ptr)); std::unique_ptr session(session_ptr); if (kPlatformVerificationStatus != PLATFORM_NO_VERIFICATION) { ASSERT_EQ(kPlatformVerificationStatus, @@ -2785,13 +2824,14 @@ TEST_P(SessionVmpTest, VmpVerifiedAndTampered) { } } else { // Platform verification status is tampered. - util::Status status = SessionImpl::Create(request_msg, &session_ptr); + util::Status status = + SessionImpl::Create(test_root_.get(), request_msg, &session_ptr); ASSERT_EQ(util::error::INTERNAL, status.error_code()); } } TEST_F(SessionTest, SessionFailureWithInvalidRemoteAttestation) { - RemoteAttestationVerifier::get().EnableTestCertificates(true); + RemoteAttestationVerifier::get().EnableTestDrmCertificates(true); std::string service_cert_sn("service_cert_sn"); AddDrmServiceCertificate(service_cert_sn, kRaServiceId, 123456); ClientIdentification::ClientCapabilities client_capabilities; @@ -2811,7 +2851,8 @@ TEST_F(SessionTest, SessionFailureWithInvalidRemoteAttestation) { ASSERT_TRUE(signed_message.SerializeToString(&updated_request)); SessionImpl* session_ptr(nullptr); EXPECT_EQ(INVALID_MESSAGE, - SessionImpl::Create(updated_request, &session_ptr).error_code()); + SessionImpl::Create(test_root_.get(), updated_request, &session_ptr) + .error_code()); } TEST_F(SessionTest, AnalogOutputAllowed_DeviceWithoutAnalogOutput) { diff --git a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.cc b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.cc index dc45ed8..776decc 100644 --- a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.cc +++ b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.cc @@ -26,14 +26,7 @@ WvPLCASProxyEnvironment::WvPLCASProxyEnvironment( std::map::const_iterator it = config_values.find(kDrmCertificateType); if (it != config_values.end()) { - if (it->second == "dev" || it->second == "prod" || it->second == "test") { - drm_certificate_type_ = it->second; - } - if (drm_certificate_type_ == "dev") { - certificate_type_ = kCertificateTypeDevelopment; - } else if (drm_certificate_type_ == "test") { - certificate_type_ = kCertificateTypeTesting; - } + drm_certificate_type_ = it->second; } it = config_values.find(kProvider); if (it != config_values.end()) { @@ -48,6 +41,11 @@ WvPLCASProxyEnvironment::WvPLCASProxyEnvironment( } } +WvPLStatus WvPLCASProxyEnvironment::Initialize() { + return widevine::DrmRootCertificate::CreateByTypeString( + drm_certificate_type_, &drm_root_certificate_); +} + WvPLStatus WvPLCASProxyEnvironment::CreateSession( const std::string& cas_license_request, WvPLCASProxySession** cas_proxy_session) { @@ -61,7 +59,8 @@ WvPLStatus WvPLCASProxyEnvironment::CreateSession( "*proxy_session is not NULL"); } std::unique_ptr wvpl_cas_proxy_session( - new WvPLCASProxySession(cas_license_request)); + new WvPLCASProxySession(drm_root_certificate_.get(), + cas_license_request)); // TODO(user): Complete the license request parsing in WvPLCASProxySession. // status = wvpl_cas_proxy_session->ParseLicenseRequest(); if (status.ok()) { @@ -70,6 +69,11 @@ WvPLStatus WvPLCASProxyEnvironment::CreateSession( return status; } +WvPLStatus WvPLCASProxyEnvironment::SetDeviceCertificateStatusList( + const std::string& cert_list) const { + return WvPLSDKEnvironment::SetDeviceCertificateStatusList(cert_list); +} + WvPLCASProxyEnvironment::~WvPLCASProxyEnvironment() {} } // namespace wv_pl_sdk diff --git a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.h b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.h index 6bafa56..45edf1e 100644 --- a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.h +++ b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.h @@ -32,6 +32,11 @@ class WvPLCASProxyEnvironment : public WvPLSDKEnvironment { */ ~WvPLCASProxyEnvironment() override; + /** + * One-time initialization. Must be called after creating the environment. + */ + virtual WvPLStatus Initialize(); + /** * Creates a session for the license request from the Widevine CDM. * @@ -42,6 +47,17 @@ class WvPLCASProxyEnvironment : public WvPLSDKEnvironment { */ virtual WvPLStatus CreateSession(const std::string& cas_license_request, WvPLCASProxySession** cas_proxy_session); + + /** + * Set the certificate status list system-wide. + * |cert_list| specifies the device certificate status + * list as std::string or certificate status list response from keysmith. + * + * @param cert_list + * + * @return WvPLStatus enumeration + */ + WvPLStatus SetDeviceCertificateStatusList(const std::string& cert_list) const; }; } // namespace wv_pl_sdk diff --git a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.cc b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.cc index d18a000..d0c6613 100644 --- a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.cc +++ b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.cc @@ -25,6 +25,11 @@ PlatformVerificationStatus WvPLCASProxySession::VerifyPlatform() { return PLATFORM_NO_VERIFICATION; } +WvPLCASProxySession::WvPLCASProxySession( + const widevine::DrmRootCertificate* drm_root_certificate, + const std::string& cas_license_request_from_cdm) + : WvPLSDKSession(drm_root_certificate) {} + WvPLStatus WvPLCASProxySession::ParsePsshData( WvPLWidevinePsshData* wvpl_widevine_pssh_data) const { // TODO(user): Implement this functionality in WvPLSdk and remove the diff --git a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.h b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.h index 7a61e20..0991b6f 100644 --- a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.h +++ b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.h @@ -73,7 +73,9 @@ class WvPLCASProxySession : public WvPLSDKSession { friend class WvPLCASProxyEnvironmentTest; friend class WvPLCASProxySessionTest; - explicit WvPLCASProxySession(const std::string& cas_license_request_from_cdm) {} + WvPLCASProxySession( + const widevine::DrmRootCertificate* drm_root_certificate, + const std::string& cas_license_request_from_cdm); WvPLStatus ParsePsshData( WvPLWidevinePsshData* wvpl_widevine_pssh_data) const override; diff --git a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session_test.cc b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session_test.cc index 5f5ed2b..2b29a6e 100644 --- a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session_test.cc +++ b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session_test.cc @@ -23,7 +23,7 @@ class WvPLCASProxySessionTest : public testing::Test { void SetUp() override { // TODO(user): Fill out with valid CAS License request. const std::string request = ""; - wvpl_cas_proxy_session_ = new WvPLCASProxySession(request); + wvpl_cas_proxy_session_ = new WvPLCASProxySession(nullptr, request); } bool IsChromeCDM() { return wvpl_cas_proxy_session_->IsChromeCDM(); } diff --git a/protos/public/client_identification.proto b/protos/public/client_identification.proto index 6cb0bda..79c83aa 100644 --- a/protos/public/client_identification.proto +++ b/protos/public/client_identification.proto @@ -74,6 +74,14 @@ message ClientIdentification { optional AnalogOutputCapabilities analog_output_capabilities = 10 [default = ANALOG_OUTPUT_UNKNOWN]; optional bool can_disable_analog_output = 11 [default = false]; + // Clients can indicate a performance level supported by OEMCrypto. + // This will allow applications and providers to choose an appropriate + // quality of content to serve. Currently defined tiers are + // 1 (low), 2 (medium) and 3 (high). Any other value indicate that + // the resource rating is unavailable or reporting erroneous values + // for that device. For details see, + // https://docs.google.com/document/d/1wodSYK-Unj3AgTSXqujWuBCAFC00qF85G1AhfLtqdko + optional uint32 resource_rating_tier = 12 [default = 0]; } // Type of factory-provisioned device root of trust. Optional. diff --git a/protos/public/errors.proto b/protos/public/errors.proto index ec14f74..cb3c10b 100644 --- a/protos/public/errors.proto +++ b/protos/public/errors.proto @@ -236,4 +236,7 @@ enum Errors { // Invalid key size. INVALID_KEY_SIZE = 169; + // Invalid method parameter. + INVALID_PARAMETER = 170; + } diff --git a/protos/public/license_services.proto b/protos/public/license_services.proto index 8bb4a37..923df54 100644 --- a/protos/public/license_services.proto +++ b/protos/public/license_services.proto @@ -284,6 +284,8 @@ message ModularDrmLicenseResponse { // ClientIdentification.ClientCapabilities.can_update_srm in the license // request. optional bool can_update_srm = 6 [default = false]; + // SessionInit that was used when generating the license. + optional SessionInit session_init = 7; } optional LicenseMetadata license_metadata = 4; message Track { diff --git a/sdk/external/common/wvpl/BUILD b/sdk/external/common/wvpl/BUILD index 0142ece..bd82701 100644 --- a/sdk/external/common/wvpl/BUILD +++ b/sdk/external/common/wvpl/BUILD @@ -38,13 +38,14 @@ cc_library( ], deps = [ # TODO(user): Refactor these deps as classes that derive from WvPLSDKSession may not rely on license SDK(s). + ":wvpl_types", "//base", "//util:status", - ":wvpl_types", - "//common:certificate_type", - "//common:drm_service_certificate", + "//common:client_cert", "//common:error_space", "//common:remote_attestation_verifier", + "//common:drm_root_certificate", + "//common:drm_service_certificate", "//common:verified_media_pipeline", "//license_server_sdk/internal:sdk", "//protos/public:client_identification_proto", @@ -66,16 +67,16 @@ cc_library( ], deps = [ # TODO(user): Refactor these deps as classes that derive from WvPLSDKEnvironment may not rely on license SDK(s). + ":wvpl_types", "//base", "@abseil_repo//absl/strings", "@abseil_repo//absl/synchronization", "//util:status", - ":wvpl_types", "//common:aes_cbc_util", - "//common:certificate_type", - "//common:certificate_util", - "//common:drm_service_certificate", + "//common:device_status_list", + "//common:drm_root_certificate", "//common:error_space", + "//common:drm_service_certificate", "//common:sha_util", "//license_server_sdk/internal:sdk", "//protos/public:device_certificate_status_proto", @@ -99,8 +100,9 @@ cc_library( "@abseil_repo//absl/synchronization", "//util:status", "//common:aes_cbc_util", - "//common:certificate_type", - "//common:certificate_util", + "//common:client_cert", + "//common:device_status_list", + "//common:drm_root_certificate", "//common:drm_service_certificate", "//common:error_space", "//common:remote_attestation_verifier", diff --git a/sdk/external/common/wvpl/wvpl_sdk_environment.cc b/sdk/external/common/wvpl/wvpl_sdk_environment.cc index 09e2630..fbf7738 100644 --- a/sdk/external/common/wvpl/wvpl_sdk_environment.cc +++ b/sdk/external/common/wvpl/wvpl_sdk_environment.cc @@ -12,8 +12,7 @@ #include "absl/synchronization/mutex.h" #include "util/status.h" #include "common/aes_cbc_util.h" -#include "common/certificate_type.h" -#include "common/certificate_util.h" +#include "common/device_status_list.h" #include "common/drm_service_certificate.h" #include "common/error_space.h" #include "common/sha_util.h" @@ -21,16 +20,16 @@ #include "protos/public/errors.pb.h" namespace util = widevine::util; -using widevine::AddDrmServiceCertificate; using widevine::DeviceCertificateStatus; using widevine::DeviceCertificateStatusList; +using widevine::DeviceStatusList; using widevine::DrmServiceCertificate; using widevine::error_space; using widevine::kCertificateTypeDevelopment; using widevine::kCertificateTypeProduction; using widevine::kCertificateTypeTesting; using widevine::ProvisionedDeviceInfo; -using widevine::SetCertificateStatusList; +using widevine::SignedDeviceCertificateStatusList; using widevine::crypto_util::EncryptAesCbc; namespace widevine_server { @@ -74,8 +73,9 @@ ProvisionedDeviceInfoMap& GetProvisionedDeviceInfoMap() { WvPLStatus WvPLSDKEnvironment::SetDrmServiceCertificate( const std::string& service_certificate, const std::string& service_private_key, const std::string& service_private_key_passphrase) { - WvPLStatus wvpl_status = AddDrmServiceCertificate( - certificate_type_, service_certificate, service_private_key, + CHECK(drm_root_certificate()) << "DRM root certificate not set!"; + WvPLStatus wvpl_status = DrmServiceCertificate::AddDrmServiceCertificate( + drm_root_certificate(), service_certificate, service_private_key, service_private_key_passphrase); if (!wvpl_status.ok()) return wvpl_status; wvpl_status = DrmServiceCertificate::ValidateDrmServiceCertificate(); @@ -85,7 +85,6 @@ WvPLStatus WvPLSDKEnvironment::SetDrmServiceCertificate( return wvpl_status; } - bool WvPLSDKEnvironment::GenerateErrorResponse( const WvPLStatus& create_session_status, std::string* license_response) { return widevine::GenerateErrorResponse(create_session_status, @@ -198,5 +197,31 @@ void WvPLSDKEnvironment::SetConfigValue( const std::map& config_values) { config_values_->insert(config_values.begin(), config_values.end()); } + +WvPLStatus WvPLSDKEnvironment::SetDeviceCertificateStatusList( + const std::string& cert_list) const { + WvPLStatus status = util::OkStatus(); + SignedDeviceCertificateStatusList device_certificate_status_list; + std::string decoded_certificate_status_list; + std::string device_certicate_status_list; + status = DeviceStatusList::ExtractFromProvisioningServiceResponse( + cert_list, &decoded_certificate_status_list, + &device_certicate_status_list); + if (!status.ok()) return status; + DeviceCertificateStatusList certificate_status_list; + if (!certificate_status_list.ParseFromString(device_certicate_status_list)) { + return util::Status(error_space, + widevine::INVALID_CERTIFICATE_STATUS_LIST, + "certificate status list parse error"); + } + status = DeviceStatusList::Instance()->UpdateStatusList( + drm_root_certificate_->public_key(), decoded_certificate_status_list, + device_certificate_expiration_seconds_); + if (!status.ok()) return status; + status = WvPLSDKEnvironment::UpdateProvisionedDeviceInfoMap( + certificate_status_list); + return status; +} + } // namespace wv_pl_sdk } // namespace widevine_server diff --git a/sdk/external/common/wvpl/wvpl_sdk_environment.h b/sdk/external/common/wvpl/wvpl_sdk_environment.h index 6a6d3ca..6b74b86 100644 --- a/sdk/external/common/wvpl/wvpl_sdk_environment.h +++ b/sdk/external/common/wvpl/wvpl_sdk_environment.h @@ -9,9 +9,10 @@ #ifndef SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_ENVIRONMENT_H_ #define SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_ENVIRONMENT_H_ +#include #include -#include "common/certificate_type.h" +#include "common/drm_root_certificate.h" #include "sdk/external/common/wvpl/wvpl_types.h" #include "protos/public/device_certificate_status.pb.h" #include "protos/public/provisioned_device_info.pb.h" @@ -66,9 +67,10 @@ class WvPLSDKEnvironment { const std::string& service_certificate, const std::string& service_private_key, const std::string& service_private_key_passphrase); - // Returns the DRM Root Certificate type. This would be a setting passed into - // the environment, by a derived class constructor. - virtual std::string GetDrmCertificateType() { return drm_certificate_type_; } + // Returns the DRM root certificate configured for this environment. + const widevine::DrmRootCertificate* drm_root_certificate() const { + return drm_root_certificate_.get(); + } protected: // Return the signature for the provider specified in the |config_values| @@ -83,6 +85,8 @@ class WvPLSDKEnvironment { const widevine::DeviceCertificateStatusList& certificate_status_list); + WvPLStatus SetDeviceCertificateStatusList(const std::string& cert_list) const; + // Number of seconds until the certificate status list expires after its // creation time. Default value is 604800 seconds. uint32_t device_certificate_expiration_seconds_ = 604800; @@ -100,9 +104,8 @@ class WvPLSDKEnvironment { bool is_service_certificate_loaded_ = false; // If true, allow devices not in the certificate status list. bool allow_unknown_device_ = false; - // DRM Certificate type. - widevine::CertificateType certificate_type_ = - widevine::kCertificateTypeProduction; + // DRM root certificate used for verifying all other DRM certificates. + std::unique_ptr drm_root_certificate_; private: /** diff --git a/sdk/external/common/wvpl/wvpl_sdk_session.cc b/sdk/external/common/wvpl/wvpl_sdk_session.cc index 152faa0..4a8a994 100644 --- a/sdk/external/common/wvpl/wvpl_sdk_session.cc +++ b/sdk/external/common/wvpl/wvpl_sdk_session.cc @@ -10,14 +10,15 @@ #include "glog/logging.h" #include "util/status.h" #include "absl/memory/memory.h" +#include "common/client_cert.h" #include "common/drm_service_certificate.h" #include "common/error_space.h" #include "common/remote_attestation_verifier.h" #include "common/verified_media_pipeline.h" -#include "license_server_sdk/internal/client_cert.h" #include "sdk/external/common/wvpl/wvpl_sdk_environment.h" #include "sdk/external/common/wvpl/wvpl_types.h" #include "protos/public/errors.pb.h" +#include "protos/public/provisioned_device_info.pb.h" // TODO(user): Mark getProvisionedDeviceInfo as deprecated, move the // implementation of isChromeCDM, getcontentid, parsePsshdata in wvpl_session @@ -28,8 +29,16 @@ // wvpl_sdk_session_test.cc. // TODO(user): Remove sdk_license_request_ and both proxy and wvpl LSDK set // signed_message_request_from_cdm_ when create session. +// TODO(user): Move all the protected memeber variables to private and use +// getter and setter to access it. +// TODO(user): Try to avoid virtual private function like parsepsshdata. +// TODO(user): (b/119566765) Refactor ParseLicenseRequest and break it into +// different classes. + namespace util = widevine::util; +using widevine::ClientCert; using widevine::ClientIdentification; +using widevine::DrmRootCertificate; using widevine::DrmServiceCertificate; using widevine::error_space; using widevine::KeyboxClientCert; @@ -38,12 +47,14 @@ using widevine::LicenseRequest; using widevine::ProvisionedDeviceInfo; using widevine::RemoteAttestationVerifier; using widevine::SessionInit; -using widevine::SessionState; using widevine::SignedMessage; namespace widevine_server { namespace wv_pl_sdk { +WvPLSDKSession::WvPLSDKSession(const DrmRootCertificate* drm_root_certificate) + : drm_root_certificate_(drm_root_certificate) {} + WvPLSDKSession::~WvPLSDKSession() {} WvPLStatus WvPLSDKSession::AddKey(const WvPLKey& key) { @@ -451,13 +462,22 @@ WvPLStatus WvPLSDKSession::ParseLicenseRequest() { } has_client_id_ = true; } - if (client_id_.has_token()) { + if (client_id_.has_token() && + client_id_.type() == ClientIdentification::KEYBOX) { // Get system_id from token field in ClientIdentification. - system_id_ = KeyboxClientCert::GetSystemId(client_id_.token()); - has_system_id_ = true; + SetSystemId(KeyboxClientCert::GetSystemId(client_id_.token())); + } + if (!HasSystemId()) { + ClientCert* client_cert_ptr = nullptr; + status = ClientCert::Create( + drm_root_certificate_, sdk_license_request_->client_id().type(), + sdk_license_request_->client_id().token(), &client_cert_ptr); + std::unique_ptr client_cert(client_cert_ptr); + if (client_cert != nullptr) { + SetSystemId(client_cert->system_id()); + } } // TODO(user): Consider enforcing missing client id here. - // Verifies platform for license requests and sets Platform Verification // status. platform_verification_status_ = VerifyPlatform(); @@ -651,15 +671,14 @@ WvPLStatus WvPLSDKSession::GetDeviceInfo(WvPLDeviceInfo* device_info) const { return WvPLStatus(error_space, util::error::INVALID_ARGUMENT, "device_info is NULL"); } - if (!has_system_id_) { + if (!HasSystemId()) { return WvPLStatus( error_space, widevine::UNSUPPORTED_SYSTEM_ID, "Widevine SystemID does not exist because it is not found " "in the license request"); } ProvisionedDeviceInfo provisioned_device_info; - status = WvPLSDKEnvironment::LookupDeviceInfo(system_id_, - &provisioned_device_info); + status = LookupDeviceInfo(GetSystemId(), &provisioned_device_info); if (!status.ok()) { return status; } @@ -697,5 +716,22 @@ WvPLStatus WvPLSDKSession::GetDeviceInfo(WvPLDeviceInfo* device_info) const { } return status; } + +WvPLStatus WvPLSDKSession::LookupDeviceInfo( + uint32_t system_id, ProvisionedDeviceInfo* provisioned_device_info) const { + return WvPLSDKEnvironment::LookupDeviceInfo(system_id, + provisioned_device_info); +} + +void WvPLSDKSession::SetSystemId(uint32_t system_id) { + system_id_ = absl::make_unique(system_id); +} + +bool WvPLSDKSession::HasSystemId() const { return system_id_ != nullptr; } + +uint32_t WvPLSDKSession::GetSystemId() const { + CHECK(system_id_); + return *system_id_; +} } // namespace wv_pl_sdk } // namespace widevine_server diff --git a/sdk/external/common/wvpl/wvpl_sdk_session.h b/sdk/external/common/wvpl/wvpl_sdk_session.h index 971a64e..21e8335 100644 --- a/sdk/external/common/wvpl/wvpl_sdk_session.h +++ b/sdk/external/common/wvpl/wvpl_sdk_session.h @@ -9,6 +9,7 @@ #ifndef SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_SESSION_H_ #define SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_SESSION_H_ +#include #include "sdk/external/common/wvpl/wvpl_types.h" #include "protos/public/client_identification.pb.h" #include "protos/public/device_certificate_status.pb.h" @@ -17,13 +18,16 @@ #include "protos/public/provisioned_device_info.pb.h" namespace widevine { +class DrmRootCertificate; class SessionInit; -} +} // namespace widevine namespace widevine_server { namespace wv_pl_sdk { class WvPLSDKSession { public: + explicit WvPLSDKSession( + const widevine::DrmRootCertificate* drm_root_certificate); virtual ~WvPLSDKSession() = 0; public: @@ -102,7 +106,7 @@ class WvPLSDKSession { } protected: - uint32_t system_id_ = 0xFFFFFFFF; + const widevine::DrmRootCertificate* drm_root_certificate_; std::string user_agent_; std::vector keys_; WvPLPlaybackPolicy policy_; @@ -110,7 +114,6 @@ class WvPLSDKSession { WvPLWidevinePsshData pssh_data_; widevine::ClientIdentification client_id_; bool has_pssh_data_ = false; - bool has_system_id_ = false; bool has_client_id_ = false; MessageType message_type_ = UNKNOWN; PlatformVerificationStatus platform_verification_status_ = @@ -182,7 +185,28 @@ class WvPLSDKSession { void CopySessionState(const WvPLSessionState& wvpl_session_state, widevine::SessionState* session_state); + // Set system_id value. + virtual void SetSystemId(uint32_t system_id); + + // Return has_system_id_ value. True if session has system id. + virtual bool HasSystemId() const; + + // Return system_id value in uint32_t. The function will crash if it does not + // have system_id. + virtual uint32_t GetSystemId() const; + + /** + * Use system_id to loop up device info. + * + * @return WvPLStatus - Status::OK if success, else error. + */ + virtual WvPLStatus LookupDeviceInfo( + uint32_t system_id, + widevine::ProvisionedDeviceInfo* provisioned_device_info) const; + private: + std::unique_ptr system_id_; + /** * Parses WvPLWidevinePsshData in the new license request. * diff --git a/util/status.h b/util/status.h index 5e3b15b..c690692 100644 --- a/util/status.h +++ b/util/status.h @@ -110,7 +110,7 @@ inline bool operator!=(const Status& s1, const Status& s2) { // Prints a human-readable representation of 'x' to 'os'. std::ostream& operator<<(std::ostream& os, const Status& x); -#define CHECK_OK(expression) CHECK_EQ(util::error::OK, expression.error_code()) +#define CHECK_OK(expression) CHECK(expression.ok()) << expression.ToString() } // namespace util